Backed out changeset b462e7b742d8 (bug 1908261) for causing multiple reftest failures...
[gecko.git] / dom / base / FragmentOrElement.cpp
blobbe0f5f63a59ff3680855b1cff63530ddb23df835
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * Base class for all element classes and DocumentFragment.
9 */
11 #include "mozilla/ArrayUtils.h"
12 #include "mozilla/Likely.h"
13 #include "mozilla/MemoryReporting.h"
14 #include "mozilla/StaticPtr.h"
16 #include "mozilla/dom/FragmentOrElement.h"
17 #include "DOMIntersectionObserver.h"
18 #include "mozilla/AsyncEventDispatcher.h"
19 #include "mozilla/EffectSet.h"
20 #include "mozilla/EventDispatcher.h"
21 #include "mozilla/EventListenerManager.h"
22 #include "mozilla/ElementAnimationData.h"
23 #include "mozilla/DeclarationBlock.h"
24 #include "mozilla/HTMLEditor.h"
25 #include "mozilla/mozInlineSpellChecker.h"
26 #include "mozilla/PresShell.h"
27 #include "mozilla/RestyleManager.h"
28 #include "mozilla/TextEditor.h"
29 #include "mozilla/TouchEvents.h"
30 #include "mozilla/URLExtraData.h"
31 #include "mozilla/dom/Attr.h"
32 #include "mozilla/dom/RadioGroupContainer.h"
33 #include "mozilla/dom/UnbindContext.h"
34 #include "nsDOMAttributeMap.h"
35 #include "nsAtom.h"
36 #include "mozilla/dom/NodeInfo.h"
37 #include "mozilla/dom/CloseWatcher.h"
38 #include "mozilla/dom/Event.h"
39 #include "mozilla/dom/ScriptLoader.h"
40 #include "mozilla/dom/CustomElementRegistry.h"
41 #include "mozilla/dom/Document.h"
42 #include "mozilla/dom/DocumentInlines.h"
43 #include "nsIControllers.h"
44 #include "nsIDocumentEncoder.h"
45 #include "nsFocusManager.h"
46 #include "nsNetUtil.h"
47 #include "nsIFrame.h"
48 #include "nsIAnonymousContentCreator.h"
49 #include "nsPresContext.h"
50 #include "nsString.h"
51 #include "nsDOMCSSAttrDeclaration.h"
52 #include "nsNameSpaceManager.h"
53 #include "nsContentList.h"
54 #include "nsDOMTokenList.h"
55 #include "nsError.h"
56 #include "nsXULElement.h"
57 #include "mozilla/InternalMutationEvent.h"
58 #include "mozilla/MouseEvents.h"
59 #ifdef DEBUG
60 # include "nsRange.h"
61 #endif
63 #include "nsFrameLoader.h"
64 #include "nsPIDOMWindow.h"
65 #include "nsLayoutUtils.h"
66 #include "nsGkAtoms.h"
67 #include "nsContentUtils.h"
68 #include "nsTextFragment.h"
69 #include "nsWindowSizes.h"
71 #include "nsIWidget.h"
73 #include "nsNodeInfoManager.h"
74 #include "nsGenericHTMLElement.h"
75 #include "nsContentCreatorFunctions.h"
76 #include "nsView.h"
77 #include "ChildIterator.h"
78 #include "mozilla/dom/NodeListBinding.h"
79 #include "mozilla/dom/MutationObservers.h"
80 #include "nsCCUncollectableMarker.h"
82 #include "mozAutoDocUpdate.h"
84 #include "mozilla/Sprintf.h"
85 #include "nsDOMMutationObserver.h"
86 #include "nsWrapperCacheInlines.h"
87 #include "nsCycleCollector.h"
88 #include "xpcpublic.h"
90 #include "mozilla/dom/ShadowRoot.h"
91 #include "mozilla/dom/HTMLSlotElement.h"
92 #include "mozilla/dom/HTMLTemplateElement.h"
93 #include "mozilla/dom/SVGUseElement.h"
95 #include "nsIContentInlines.h"
96 #include "nsChildContentList.h"
97 #include "mozilla/BloomFilter.h"
99 #include "NodeUbiReporting.h"
101 #ifdef ACCESSIBILITY
102 # include "nsAccessibilityService.h"
103 #endif
105 using namespace mozilla;
106 using namespace mozilla::dom;
108 uint64_t nsMutationGuard::sGeneration = 0;
110 NS_IMPL_CYCLE_COLLECTION_CLASS(nsIContent)
112 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsIContent)
113 MOZ_ASSERT_UNREACHABLE("Our subclasses don't call us");
114 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
116 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsIContent)
117 MOZ_ASSERT_UNREACHABLE("Our subclasses don't call us");
118 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
120 NS_INTERFACE_MAP_BEGIN(nsIContent)
121 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
122 // Don't bother to QI to cycle collection, because our CC impl is
123 // not doing anything anyway.
124 NS_INTERFACE_MAP_ENTRY(nsIContent)
125 NS_INTERFACE_MAP_ENTRY(nsINode)
126 NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
127 NS_INTERFACE_MAP_ENTRY_TEAROFF(nsISupportsWeakReference,
128 new nsNodeSupportsWeakRefTearoff(this))
129 // DOM bindings depend on the identity pointer being the
130 // same as nsINode (which nsIContent inherits).
131 NS_INTERFACE_MAP_ENTRY(nsISupports)
132 NS_INTERFACE_MAP_END
134 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsIContent)
136 NS_IMPL_DOMARENA_DESTROY(nsIContent)
138 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE_AND_DESTROY(nsIContent,
139 LastRelease(),
140 Destroy())
142 nsIContent* nsIContent::FindFirstNonChromeOnlyAccessContent() const {
143 // This handles also nested native anonymous content.
144 for (const nsIContent* content = this; content;
145 content = content->GetChromeOnlyAccessSubtreeRootParent()) {
146 if (!content->ChromeOnlyAccess()) {
147 // Oops, this function signature allows casting const to
148 // non-const. (Then again, so does GetFirstChild()->GetParent().)
149 return const_cast<nsIContent*>(content);
152 return nullptr;
155 void nsIContent::UnbindFromTree() {
156 UnbindContext context(*this);
157 UnbindFromTree(context);
160 // https://dom.spec.whatwg.org/#dom-slotable-assignedslot
161 HTMLSlotElement* nsIContent::GetAssignedSlotByMode() const {
163 * Get slotable's assigned slot for the result of
164 * find a slot with open flag UNSET [1].
166 * [1] https://dom.spec.whatwg.org/#assign-a-slot
168 HTMLSlotElement* slot = GetAssignedSlot();
169 if (!slot) {
170 return nullptr;
173 MOZ_ASSERT(GetParent());
174 MOZ_ASSERT(GetParent()->GetShadowRoot());
177 * Additional check for open flag SET:
178 * If slotable’s parent’s shadow root's mode is not "open",
179 * then return null.
181 if (GetParent()->GetShadowRoot()->IsClosed()) {
182 return nullptr;
185 return slot;
188 nsIContent::IMEState nsIContent::GetDesiredIMEState() {
189 if (!IsEditable() || !IsInComposedDoc()) {
190 // Check for the special case where we're dealing with elements which don't
191 // have the editable flag set, but are readwrite (such as text controls).
192 if (!IsElement() ||
193 !AsElement()->State().HasState(ElementState::READWRITE)) {
194 return IMEState(IMEEnabled::Disabled);
197 // NOTE: The content for independent editors (e.g., input[type=text],
198 // textarea) must override this method, so, we don't need to worry about
199 // that here.
200 nsIContent* editableAncestor = GetEditingHost();
202 // This is in another editable content, use the result of it.
203 if (editableAncestor && editableAncestor != this) {
204 return editableAncestor->GetDesiredIMEState();
206 Document* doc = GetComposedDoc();
207 if (!doc) {
208 return IMEState(IMEEnabled::Disabled);
210 nsPresContext* pc = doc->GetPresContext();
211 if (!pc) {
212 return IMEState(IMEEnabled::Disabled);
214 HTMLEditor* htmlEditor = nsContentUtils::GetHTMLEditor(pc);
215 if (!htmlEditor) {
216 return IMEState(IMEEnabled::Disabled);
218 IMEState state;
219 htmlEditor->GetPreferredIMEState(&state);
220 return state;
223 bool nsIContent::HasIndependentSelection() const {
224 nsIFrame* frame = GetPrimaryFrame();
225 return (frame && frame->HasAnyStateBits(NS_FRAME_INDEPENDENT_SELECTION));
228 dom::Element* nsIContent::GetEditingHost() {
229 // If this isn't editable, return nullptr.
230 if (!IsEditable()) {
231 return nullptr;
234 Document* doc = GetComposedDoc();
235 if (!doc) {
236 return nullptr;
239 // If this is in designMode, we should return <body>
240 if (IsInDesignMode() && !IsInShadowTree()) {
241 // FIXME: There may be no <body>. In such case and aLimitInBodyElement is
242 // "No", we should use root element instead.
243 return doc->GetBodyElement();
246 dom::Element* editableParentElement = nullptr;
247 for (dom::Element* parent = GetParentElement();
248 parent && parent->HasFlag(NODE_IS_EDITABLE);
249 parent = editableParentElement->GetParentElement()) {
250 editableParentElement = parent;
252 return editableParentElement ? editableParentElement
253 : dom::Element::FromNode(this);
256 nsresult nsIContent::LookupNamespaceURIInternal(
257 const nsAString& aNamespacePrefix, nsAString& aNamespaceURI) const {
258 if (aNamespacePrefix.EqualsLiteral("xml")) {
259 // Special-case for xml prefix
260 aNamespaceURI.AssignLiteral("http://www.w3.org/XML/1998/namespace");
261 return NS_OK;
264 if (aNamespacePrefix.EqualsLiteral("xmlns")) {
265 // Special-case for xmlns prefix
266 aNamespaceURI.AssignLiteral("http://www.w3.org/2000/xmlns/");
267 return NS_OK;
270 RefPtr<nsAtom> name;
271 if (!aNamespacePrefix.IsEmpty()) {
272 name = NS_Atomize(aNamespacePrefix);
273 NS_ENSURE_TRUE(name, NS_ERROR_OUT_OF_MEMORY);
274 } else {
275 name = nsGkAtoms::xmlns;
277 // Trace up the content parent chain looking for the namespace
278 // declaration that declares aNamespacePrefix.
279 for (Element* element = GetAsElementOrParentElement(); element;
280 element = element->GetParentElement()) {
281 if (element->GetAttr(kNameSpaceID_XMLNS, name, aNamespaceURI)) {
282 return NS_OK;
285 return NS_ERROR_FAILURE;
288 nsAtom* nsIContent::GetLang() const {
289 for (const Element* element = GetAsElementOrParentElement(); element;
290 element = element->GetParentElement()) {
291 if (!element->GetAttrCount()) {
292 continue;
295 // xml:lang has precedence over lang on HTML elements (see
296 // XHTML1 section C.7).
297 const nsAttrValue* attr =
298 element->GetParsedAttr(nsGkAtoms::lang, kNameSpaceID_XML);
299 if (!attr && element->SupportsLangAttr()) {
300 attr = element->GetParsedAttr(nsGkAtoms::lang);
302 if (attr) {
303 MOZ_ASSERT(attr->Type() == nsAttrValue::eAtom);
304 MOZ_ASSERT(attr->GetAtomValue());
305 return attr->GetAtomValue();
309 return nullptr;
312 nsIURI* nsIContent::GetBaseURI(bool aTryUseXHRDocBaseURI) const {
313 if (SVGUseElement* use = GetContainingSVGUseShadowHost()) {
314 if (URLExtraData* data = use->GetContentURLData()) {
315 return data->BaseURI();
319 return OwnerDoc()->GetBaseURI(aTryUseXHRDocBaseURI);
322 nsIURI* nsIContent::GetBaseURIForStyleAttr() const {
323 if (SVGUseElement* use = GetContainingSVGUseShadowHost()) {
324 if (URLExtraData* data = use->GetContentURLData()) {
325 return data->BaseURI();
328 // This also ignores the case that SVG inside XBL binding.
329 // But it is probably fine.
330 return OwnerDoc()->GetDocBaseURI();
333 already_AddRefed<URLExtraData> nsIContent::GetURLDataForStyleAttr(
334 nsIPrincipal* aSubjectPrincipal) const {
335 if (SVGUseElement* use = GetContainingSVGUseShadowHost()) {
336 if (URLExtraData* data = use->GetContentURLData()) {
337 return do_AddRef(data);
340 auto* doc = OwnerDoc();
341 if (aSubjectPrincipal && aSubjectPrincipal != NodePrincipal()) {
342 nsCOMPtr<nsIReferrerInfo> referrerInfo =
343 doc->ReferrerInfoForInternalCSSAndSVGResources();
344 // TODO: Cache this?
345 return MakeAndAddRef<URLExtraData>(doc->GetDocBaseURI(), referrerInfo,
346 aSubjectPrincipal);
348 return do_AddRef(doc->DefaultStyleAttrURLData());
351 void nsIContent::ConstructUbiNode(void* storage) {
352 JS::ubi::Concrete<nsIContent>::construct(storage, this);
355 bool nsIContent::InclusiveDescendantMayNeedSpellchecking(HTMLEditor* aEditor) {
356 // Return true if the node may have elements as children, since those or their
357 // descendants may have spellcheck attributes.
358 return HasFlag(NODE_MAY_HAVE_ELEMENT_CHILDREN) ||
359 mozInlineSpellChecker::ShouldSpellCheckNode(aEditor, this);
362 //----------------------------------------------------------------------
364 static inline JSObject* GetJSObjectChild(nsWrapperCache* aCache) {
365 return aCache->PreservingWrapper() ? aCache->GetWrapperPreserveColor()
366 : nullptr;
369 static bool NeedsScriptTraverse(nsINode* aNode) {
370 return aNode->PreservingWrapper() && aNode->GetWrapperPreserveColor() &&
371 !aNode->HasKnownLiveWrapperAndDoesNotNeedTracing(aNode);
374 //----------------------------------------------------------------------
376 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAttrChildContentList)
377 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAttrChildContentList)
379 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsAttrChildContentList, mNode)
381 // If the wrapper is known-live, the list can't be part of a garbage cycle.
382 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsAttrChildContentList)
383 return tmp->HasKnownLiveWrapper();
384 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
386 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsAttrChildContentList)
387 return tmp->HasKnownLiveWrapperAndDoesNotNeedTracing(tmp);
388 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
390 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsAttrChildContentList)
391 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
393 NS_INTERFACE_TABLE_HEAD(nsAttrChildContentList)
394 NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
395 NS_INTERFACE_TABLE(nsAttrChildContentList, nsINodeList)
396 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsAttrChildContentList)
397 NS_INTERFACE_MAP_END
399 JSObject* nsAttrChildContentList::WrapObject(
400 JSContext* cx, JS::Handle<JSObject*> aGivenProto) {
401 return NodeList_Binding::Wrap(cx, this, aGivenProto);
404 uint32_t nsAttrChildContentList::Length() {
405 return mNode ? mNode->GetChildCount() : 0;
408 nsIContent* nsAttrChildContentList::Item(uint32_t aIndex) {
409 if (mNode) {
410 return mNode->GetChildAt_Deprecated(aIndex);
413 return nullptr;
416 int32_t nsAttrChildContentList::IndexOf(nsIContent* aContent) {
417 if (mNode) {
418 return mNode->ComputeIndexOf_Deprecated(aContent);
421 return -1;
424 //----------------------------------------------------------------------
425 uint32_t nsParentNodeChildContentList::Length() {
426 return mNode ? mNode->GetChildCount() : 0;
429 nsIContent* nsParentNodeChildContentList::Item(uint32_t aIndex) {
430 if (!mIsCacheValid) {
431 if (MOZ_UNLIKELY(!mNode)) {
432 return nullptr;
434 // Try to avoid the cache for some common cases, see bug 1917511.
435 if (aIndex == 0) {
436 return mNode->GetFirstChild();
438 uint32_t childCount = mNode->GetChildCount();
439 if (aIndex >= childCount) {
440 return nullptr;
442 if (aIndex + 1 == childCount) {
443 return mNode->GetLastChild();
445 ValidateCache();
446 MOZ_ASSERT(mIsCacheValid);
448 return mCachedChildArray.SafeElementAt(aIndex, nullptr);
451 int32_t nsParentNodeChildContentList::IndexOf(nsIContent* aContent) {
452 EnsureCacheValid();
453 return mCachedChildArray.IndexOf(aContent);
456 void nsParentNodeChildContentList::ValidateCache() {
457 MOZ_ASSERT(!mIsCacheValid);
458 MOZ_ASSERT(mCachedChildArray.IsEmpty());
460 if (MOZ_UNLIKELY(!mNode)) {
461 return;
464 for (nsIContent* node = mNode->GetFirstChild(); node;
465 node = node->GetNextSibling()) {
466 mCachedChildArray.AppendElement(node);
468 mIsCacheValid = true;
471 //----------------------------------------------------------------------
473 nsIHTMLCollection* FragmentOrElement::Children() {
474 nsDOMSlots* slots = DOMSlots();
476 if (!slots->mChildrenList) {
477 slots->mChildrenList =
478 new nsContentList(this, kNameSpaceID_Wildcard, nsGkAtoms::_asterisk,
479 nsGkAtoms::_asterisk, false);
482 return slots->mChildrenList;
485 //----------------------------------------------------------------------
487 NS_IMPL_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff, mNode)
489 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff)
490 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
491 NS_INTERFACE_MAP_END_AGGREGATED(mNode)
493 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNodeSupportsWeakRefTearoff)
494 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNodeSupportsWeakRefTearoff)
496 NS_IMETHODIMP
497 nsNodeSupportsWeakRefTearoff::GetWeakReference(
498 nsIWeakReference** aInstancePtr) {
499 nsINode::nsSlots* slots = mNode->Slots();
500 if (!slots->mWeakReference) {
501 slots->mWeakReference = new nsNodeWeakReference(mNode);
504 NS_ADDREF(*aInstancePtr = slots->mWeakReference);
506 return NS_OK;
509 //----------------------------------------------------------------------
511 static const size_t MaxDOMSlotSizeAllowed =
512 #ifdef HAVE_64BIT_BUILD
513 128;
514 #else
516 #endif
518 static_assert(sizeof(nsINode::nsSlots) <= MaxDOMSlotSizeAllowed,
519 "DOM slots cannot be grown without consideration");
520 static_assert(sizeof(FragmentOrElement::nsDOMSlots) <= MaxDOMSlotSizeAllowed,
521 "DOM slots cannot be grown without consideration");
523 void nsIContent::nsExtendedContentSlots::UnlinkExtendedSlots(nsIContent&) {
524 mContainingShadow = nullptr;
525 mAssignedSlot = nullptr;
528 void nsIContent::nsExtendedContentSlots::TraverseExtendedSlots(
529 nsCycleCollectionTraversalCallback& aCb) {
530 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mContainingShadow");
531 aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mContainingShadow));
533 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mAssignedSlot");
534 aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mAssignedSlot.get()));
537 nsIContent::nsExtendedContentSlots::nsExtendedContentSlots() = default;
539 nsIContent::nsExtendedContentSlots::~nsExtendedContentSlots() {
540 MOZ_ASSERT(!mManualSlotAssignment);
543 size_t nsIContent::nsExtendedContentSlots::SizeOfExcludingThis(
544 MallocSizeOf aMallocSizeOf) const {
545 // For now, nothing to measure here. We don't actually own any of our
546 // members.
547 return 0;
550 FragmentOrElement::nsDOMSlots::nsDOMSlots() { MOZ_COUNT_CTOR(nsDOMSlots); }
552 FragmentOrElement::nsDOMSlots::~nsDOMSlots() {
553 MOZ_COUNT_DTOR(nsDOMSlots);
555 if (mAttributeMap) {
556 mAttributeMap->DropReference();
560 void FragmentOrElement::nsDOMSlots::Traverse(
561 nsCycleCollectionTraversalCallback& aCb) {
562 nsIContent::nsContentSlots::Traverse(aCb);
564 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mStyle");
565 aCb.NoteXPCOMChild(mStyle.get());
567 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mAttributeMap");
568 aCb.NoteXPCOMChild(mAttributeMap.get());
570 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mChildrenList");
571 aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsINodeList*, mChildrenList));
573 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mClassList");
574 aCb.NoteXPCOMChild(mClassList.get());
577 void FragmentOrElement::nsDOMSlots::Unlink(nsINode& aNode) {
578 nsIContent::nsContentSlots::Unlink(aNode);
579 mStyle = nullptr;
580 if (mAttributeMap) {
581 mAttributeMap->DropReference();
582 mAttributeMap = nullptr;
584 mChildrenList = nullptr;
585 mClassList = nullptr;
588 size_t FragmentOrElement::nsDOMSlots::SizeOfIncludingThis(
589 MallocSizeOf aMallocSizeOf) const {
590 size_t n = aMallocSizeOf(this);
592 nsExtendedContentSlots* extendedSlots = GetExtendedContentSlots();
593 if (extendedSlots) {
594 if (OwnsExtendedSlots()) {
595 n += aMallocSizeOf(extendedSlots);
598 n += extendedSlots->SizeOfExcludingThis(aMallocSizeOf);
601 if (mAttributeMap) {
602 n += mAttributeMap->SizeOfIncludingThis(aMallocSizeOf);
605 if (mChildrenList) {
606 n += mChildrenList->SizeOfIncludingThis(aMallocSizeOf);
609 // Measurement of the following members may be added later if DMD finds it is
610 // worthwhile:
611 // - Superclass members (nsINode::nsSlots)
612 // - mStyle
613 // - mClassList
615 // The following member are not measured:
616 // - mControllers: because it is non-owning
617 return n;
620 FragmentOrElement::nsExtendedDOMSlots::nsExtendedDOMSlots() = default;
622 FragmentOrElement::nsExtendedDOMSlots::~nsExtendedDOMSlots() = default;
624 void FragmentOrElement::nsExtendedDOMSlots::UnlinkExtendedSlots(
625 nsIContent& aContent) {
626 nsIContent::nsExtendedContentSlots::UnlinkExtendedSlots(aContent);
628 // mShadowRoot will similarly be cleared explicitly from
629 // FragmentOrElement::Unlink.
630 mSMILOverrideStyle = nullptr;
631 mControllers = nullptr;
632 mLabelsList = nullptr;
633 mPopoverData = nullptr;
634 if (mCustomElementData) {
635 mCustomElementData->Unlink();
636 mCustomElementData = nullptr;
638 if (mAnimations) {
639 mAnimations = nullptr;
640 aContent.ClearMayHaveAnimations();
642 mExplicitlySetAttrElementMap.Clear();
643 mAttrElementsMap.Clear();
644 mRadioGroupContainer = nullptr;
645 mPart = nullptr;
648 void FragmentOrElement::nsExtendedDOMSlots::TraverseExtendedSlots(
649 nsCycleCollectionTraversalCallback& aCb) {
650 nsIContent::nsExtendedContentSlots::TraverseExtendedSlots(aCb);
652 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mSMILOverrideStyle");
653 aCb.NoteXPCOMChild(mSMILOverrideStyle.get());
655 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mControllers");
656 aCb.NoteXPCOMChild(mControllers);
658 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mLabelsList");
659 aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsINodeList*, mLabelsList));
661 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mShadowRoot");
662 aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mShadowRoot));
664 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mPart");
665 aCb.NoteXPCOMChild(mPart.get());
667 for (auto& tableEntry : mAttrElementsMap) {
668 auto& [explicitlySetElements, cachedAttrElements] =
669 *tableEntry.GetModifiableData();
670 if (cachedAttrElements) {
671 ImplCycleCollectionTraverse(aCb, *cachedAttrElements,
672 "cached attribute elements entry", 0);
676 if (mCustomElementData) {
677 mCustomElementData->Traverse(aCb);
679 if (mAnimations) {
680 mAnimations->Traverse(aCb);
682 if (mRadioGroupContainer) {
683 RadioGroupContainer::Traverse(mRadioGroupContainer.get(), aCb);
687 size_t FragmentOrElement::nsExtendedDOMSlots::SizeOfExcludingThis(
688 MallocSizeOf aMallocSizeOf) const {
689 size_t n =
690 nsIContent::nsExtendedContentSlots::SizeOfExcludingThis(aMallocSizeOf);
692 // We own mSMILOverrideStyle but there seems to be no memory reporting on CSS
693 // declarations? At least report the memory the declaration takes up
694 // directly.
695 if (mSMILOverrideStyle) {
696 n += aMallocSizeOf(mSMILOverrideStyle);
699 // We don't really own mSMILOverrideStyleDeclaration. mSMILOverrideStyle owns
700 // it.
702 // We don't seem to have memory reporting for nsXULControllers. At least
703 // report the memory it's using directly.
704 if (mControllers) {
705 n += aMallocSizeOf(mControllers);
708 if (mLabelsList) {
709 n += mLabelsList->SizeOfIncludingThis(aMallocSizeOf);
712 // mShadowRoot should be handled during normal DOM tree memory reporting, just
713 // like kids, siblings, etc.
715 if (mCustomElementData) {
716 n += mCustomElementData->SizeOfIncludingThis(aMallocSizeOf);
719 if (mRadioGroupContainer) {
720 n += mRadioGroupContainer->SizeOfIncludingThis(aMallocSizeOf);
723 return n;
726 FragmentOrElement::FragmentOrElement(
727 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
728 : nsIContent(std::move(aNodeInfo)) {}
730 FragmentOrElement::~FragmentOrElement() {
731 MOZ_ASSERT(!IsInUncomposedDoc(),
732 "Please remove this from the document properly");
733 if (GetParent()) {
734 NS_RELEASE(mParent);
738 static nsINode* FindChromeAccessOnlySubtreeOwnerForEvents(nsINode* aNode) {
739 if (!aNode->ChromeOnlyAccessForEvents()) {
740 return aNode;
742 return const_cast<nsIContent*>(aNode->GetChromeOnlyAccessSubtreeRootParent());
745 nsINode* FindChromeAccessOnlySubtreeOwnerForEvents(EventTarget* aTarget) {
746 nsINode* node = nsINode::FromEventTargetOrNull(aTarget);
747 if (!node) {
748 return nullptr;
750 return FindChromeAccessOnlySubtreeOwnerForEvents(node);
753 void nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
754 // FIXME! Document how this event retargeting works, Bug 329124.
755 aVisitor.mCanHandle = true;
756 aVisitor.mMayHaveListenerManager = HasListenerManager();
758 if (IsInShadowTree()) {
759 aVisitor.mItemInShadowTree = true;
762 // Don't propagate mouseover and mouseout events when mouse is moving
763 // inside chrome access only content.
764 const bool isAnonForEvents = IsRootOfChromeAccessOnlySubtree();
765 aVisitor.mRootOfClosedTree = isAnonForEvents;
766 if ((aVisitor.mEvent->mMessage == eMouseOver ||
767 aVisitor.mEvent->mMessage == eMouseOut ||
768 aVisitor.mEvent->mMessage == ePointerOver ||
769 aVisitor.mEvent->mMessage == ePointerOut) &&
770 // Check if we should stop event propagation when event has just been
771 // dispatched or when we're about to propagate from
772 // chrome access only subtree or if we are about to propagate out of
773 // a shadow root to a shadow root host.
774 ((this == aVisitor.mEvent->mOriginalTarget && !ChromeOnlyAccess()) ||
775 isAnonForEvents)) {
776 nsIContent* relatedTarget = nsIContent::FromEventTargetOrNull(
777 aVisitor.mEvent->AsMouseEvent()->mRelatedTarget);
778 if (relatedTarget && relatedTarget->OwnerDoc() == OwnerDoc()) {
779 // If current target is anonymous for events or we know that related
780 // target is descendant of an element which is anonymous for events,
781 // we may want to stop event propagation.
782 // If this is the original target, aVisitor.mRelatedTargetIsInAnon
783 // must be updated.
784 if (isAnonForEvents || aVisitor.mRelatedTargetIsInAnon ||
785 (aVisitor.mEvent->mOriginalTarget == this &&
786 (aVisitor.mRelatedTargetIsInAnon =
787 relatedTarget->ChromeOnlyAccessForEvents()))) {
788 nsINode* anonOwner = FindChromeAccessOnlySubtreeOwnerForEvents(this);
789 if (anonOwner) {
790 nsINode* anonOwnerRelated =
791 FindChromeAccessOnlySubtreeOwnerForEvents(relatedTarget);
792 if (anonOwnerRelated) {
793 // Note, anonOwnerRelated may still be inside some other
794 // native anonymous subtree. The case where anonOwner is still
795 // inside native anonymous subtree will be handled when event
796 // propagates up in the DOM tree.
797 while (anonOwner != anonOwnerRelated &&
798 anonOwnerRelated->ChromeOnlyAccessForEvents()) {
799 anonOwnerRelated =
800 FindChromeAccessOnlySubtreeOwnerForEvents(anonOwnerRelated);
802 if (anonOwner == anonOwnerRelated) {
803 #ifdef DEBUG_smaug
804 nsIContent* originalTarget = nsIContent::FromEventTargetOrNull(
805 aVisitor.mEvent->mOriginalTarget);
806 nsAutoString ot, ct, rt;
807 if (originalTarget) {
808 originalTarget->NodeInfo()->NameAtom()->ToString(ot);
810 NodeInfo()->NameAtom()->ToString(ct);
811 relatedTarget->NodeInfo()->NameAtom()->ToString(rt);
812 printf(
813 "Stopping %s propagation:"
814 "\n\toriginalTarget=%s \n\tcurrentTarget=%s %s"
815 "\n\trelatedTarget=%s %s \n%s",
816 (aVisitor.mEvent->mMessage == eMouseOver) ? "mouseover"
817 : "mouseout",
818 NS_ConvertUTF16toUTF8(ot).get(),
819 NS_ConvertUTF16toUTF8(ct).get(),
820 isAnonForEvents
821 ? "(is native anonymous)"
822 : (ChromeOnlyAccess() ? "(is in native anonymous subtree)"
823 : ""),
824 NS_ConvertUTF16toUTF8(rt).get(),
825 relatedTarget->ChromeOnlyAccess()
826 ? "(is in native anonymous subtree)"
827 : "",
828 (originalTarget &&
829 relatedTarget->FindFirstNonChromeOnlyAccessContent() ==
830 originalTarget->FindFirstNonChromeOnlyAccessContent())
831 ? ""
832 : "Wrong event propagation!?!\n");
833 #endif
834 aVisitor.SetParentTarget(nullptr, false);
835 // Event should not propagate to non-anon content.
836 aVisitor.mCanHandle = isAnonForEvents;
837 return;
845 // Event parent is the assigned slot, if node is assigned, or node's parent
846 // otherwise.
847 HTMLSlotElement* slot = GetAssignedSlot();
848 nsIContent* parent = slot ? slot : GetParent();
850 // Event may need to be retargeted if this is the root of a native anonymous
851 // content subtree.
852 if (isAnonForEvents) {
853 #ifdef DEBUG
854 // If a DOM event is explicitly dispatched using node.dispatchEvent(), then
855 // all the events are allowed even in the native anonymous content..
856 nsIContent* t =
857 nsIContent::FromEventTargetOrNull(aVisitor.mEvent->mOriginalTarget);
858 NS_ASSERTION(!t || !t->ChromeOnlyAccessForEvents() ||
859 aVisitor.mEvent->mClass != eMutationEventClass ||
860 aVisitor.mDOMEvent,
861 "Mutation event dispatched in native anonymous content!?!");
862 #endif
863 if (aVisitor.mEvent->mClass == eTransitionEventClass ||
864 aVisitor.mEvent->mClass == eAnimationEventClass) {
865 // Event should not propagate to non-anon content.
866 aVisitor.SetParentTarget(nullptr, false);
867 return;
869 aVisitor.mEventTargetAtParent = parent;
870 } else if (parent && aVisitor.mOriginalTargetIsInAnon) {
871 nsIContent* content =
872 nsIContent::FromEventTargetOrNull(aVisitor.mEvent->mTarget);
873 if (content &&
874 content->GetClosestNativeAnonymousSubtreeRootParentOrHost() == parent) {
875 aVisitor.mEventTargetAtParent = parent;
879 if (!aVisitor.mEvent->mFlags.mComposedInNativeAnonymousContent &&
880 isAnonForEvents && OwnerDoc()->GetWindow()) {
881 aVisitor.SetParentTarget(OwnerDoc()->GetWindow()->GetParentTarget(), true);
882 } else if (parent) {
883 aVisitor.SetParentTarget(parent, false);
884 if (slot) {
885 ShadowRoot* root = slot->GetContainingShadow();
886 if (root && root->IsClosed()) {
887 aVisitor.mParentIsSlotInClosedTree = true;
890 } else {
891 aVisitor.SetParentTarget(GetComposedDoc(), false);
894 if (!ChromeOnlyAccessForEvents() &&
895 !aVisitor.mRelatedTargetRetargetedInCurrentScope) {
896 // We don't support Shadow DOM in native anonymous content yet.
897 aVisitor.mRelatedTargetRetargetedInCurrentScope = true;
898 if (aVisitor.mEvent->mOriginalRelatedTarget) {
899 // https://dom.spec.whatwg.org/#concept-event-dispatch
900 // Step 3.
901 // "Let relatedTarget be the result of retargeting event's relatedTarget
902 // against target if event's relatedTarget is non-null, and null
903 // otherwise."
905 // This is a bit complicated because the event might be from native
906 // anonymous content, but we need to deal with non-native anonymous
907 // content there.
908 bool initialTarget = this == aVisitor.mEvent->mOriginalTarget;
909 nsINode* originalTargetAsNode = nullptr;
910 // Use of mOriginalTargetIsInAnon is an optimization here.
911 if (!initialTarget && aVisitor.mOriginalTargetIsInAnon) {
912 originalTargetAsNode = FindChromeAccessOnlySubtreeOwnerForEvents(
913 aVisitor.mEvent->mOriginalTarget);
914 initialTarget = originalTargetAsNode == this;
916 if (initialTarget) {
917 nsINode* relatedTargetAsNode =
918 FindChromeAccessOnlySubtreeOwnerForEvents(
919 aVisitor.mEvent->mOriginalRelatedTarget);
920 if (!originalTargetAsNode) {
921 originalTargetAsNode =
922 nsINode::FromEventTargetOrNull(aVisitor.mEvent->mOriginalTarget);
925 if (relatedTargetAsNode && originalTargetAsNode) {
926 nsINode* retargetedRelatedTarget = nsContentUtils::Retarget(
927 relatedTargetAsNode, originalTargetAsNode);
928 if (originalTargetAsNode == retargetedRelatedTarget &&
929 retargetedRelatedTarget != relatedTargetAsNode) {
930 // Step 4.
931 // "If target is relatedTarget and target is not event's
932 // relatedTarget, then return true."
933 aVisitor.IgnoreCurrentTargetBecauseOfShadowDOMRetargeting();
934 // Old code relies on mTarget to point to the first element which
935 // was not added to the event target chain because of mCanHandle
936 // being false, but in Shadow DOM case mTarget really should
937 // point to a node in Shadow DOM.
938 aVisitor.mEvent->mTarget = aVisitor.mTargetInKnownToBeHandledScope;
939 return;
942 // Part of step 5. Retargeting target has happened already higher
943 // up in this method.
944 // "Append to an event path with event, target, targetOverride,
945 // relatedTarget, and false."
946 aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
948 } else if (nsINode* relatedTargetAsNode =
949 FindChromeAccessOnlySubtreeOwnerForEvents(
950 aVisitor.mEvent->mOriginalRelatedTarget)) {
951 // Step 11.3.
952 // "Let relatedTarget be the result of retargeting event's
953 // relatedTarget against parent if event's relatedTarget is non-null,
954 // and null otherwise.".
955 nsINode* retargetedRelatedTarget =
956 nsContentUtils::Retarget(relatedTargetAsNode, this);
957 nsINode* targetInKnownToBeHandledScope =
958 FindChromeAccessOnlySubtreeOwnerForEvents(
959 aVisitor.mTargetInKnownToBeHandledScope);
960 // If aVisitor.mTargetInKnownToBeHandledScope wasn't nsINode,
961 // targetInKnownToBeHandledScope will be null. This may happen when
962 // dispatching event to Window object in a content page and
963 // propagating the event to a chrome Element.
964 if (targetInKnownToBeHandledScope &&
965 IsShadowIncludingInclusiveDescendantOf(
966 targetInKnownToBeHandledScope->SubtreeRoot())) {
967 // Part of step 11.4.
968 // "If target's root is a shadow-including inclusive ancestor of
969 // parent, then"
970 // "...Append to an event path with event, parent, null,
971 // relatedTarget, " and slot-in-closed-tree."
972 aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
973 } else if (this == retargetedRelatedTarget) {
974 // Step 11.5
975 // "Otherwise, if parent and relatedTarget are identical, then set
976 // parent to null."
977 aVisitor.IgnoreCurrentTargetBecauseOfShadowDOMRetargeting();
978 // Old code relies on mTarget to point to the first element which
979 // was not added to the event target chain because of mCanHandle
980 // being false, but in Shadow DOM case mTarget really should
981 // point to a node in Shadow DOM.
982 aVisitor.mEvent->mTarget = aVisitor.mTargetInKnownToBeHandledScope;
983 return;
984 } else if (targetInKnownToBeHandledScope) {
985 // Note, if targetInKnownToBeHandledScope is null,
986 // mTargetInKnownToBeHandledScope could be Window object in content
987 // page and we're in chrome document in the same process.
989 // Step 11.6
990 aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
995 if (aVisitor.mEvent->mClass == eTouchEventClass) {
996 // Retarget touch objects.
997 MOZ_ASSERT(!aVisitor.mRetargetedTouchTargets.isSome());
998 aVisitor.mRetargetedTouchTargets.emplace();
999 WidgetTouchEvent* touchEvent = aVisitor.mEvent->AsTouchEvent();
1000 WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
1001 for (uint32_t i = 0; i < touches.Length(); ++i) {
1002 Touch* touch = touches[i];
1003 EventTarget* originalTarget = touch->mOriginalTarget;
1004 EventTarget* touchTarget = originalTarget;
1005 nsCOMPtr<nsINode> targetAsNode =
1006 nsINode::FromEventTargetOrNull(originalTarget);
1007 if (targetAsNode) {
1008 EventTarget* retargeted =
1009 nsContentUtils::Retarget(targetAsNode, this);
1010 if (retargeted) {
1011 touchTarget = retargeted;
1014 aVisitor.mRetargetedTouchTargets->AppendElement(touchTarget);
1015 touch->mTarget = touchTarget;
1017 MOZ_ASSERT(aVisitor.mRetargetedTouchTargets->Length() ==
1018 touches.Length());
1022 if (slot) {
1023 // Inform that we're about to exit the current scope.
1024 aVisitor.mRelatedTargetRetargetedInCurrentScope = false;
1028 Element* nsIContent::GetAutofocusDelegate(IsFocusableFlags aFlags) const {
1029 for (nsINode* node = GetFirstChild(); node; node = node->GetNextNode(this)) {
1030 auto* descendant = Element::FromNode(*node);
1031 if (!descendant || !descendant->GetBoolAttr(nsGkAtoms::autofocus)) {
1032 continue;
1035 nsIFrame* frame = descendant->GetPrimaryFrame();
1036 if (frame && frame->IsFocusable(aFlags)) {
1037 return descendant;
1040 return nullptr;
1043 Element* nsIContent::GetFocusDelegate(IsFocusableFlags aFlags) const {
1044 const nsIContent* whereToLook = this;
1045 if (ShadowRoot* root = GetShadowRoot()) {
1046 if (!root->DelegatesFocus()) {
1047 // 1. If focusTarget is a shadow host and its shadow root 's delegates
1048 // focus is false, then return null.
1049 return nullptr;
1051 whereToLook = root;
1054 auto IsFocusable = [&](Element* aElement) -> Focusable {
1055 nsIFrame* frame = aElement->GetPrimaryFrame();
1057 if (!frame) {
1058 return {};
1061 return frame->IsFocusable(aFlags);
1064 Element* potentialFocus = nullptr;
1065 for (nsINode* node = whereToLook->GetFirstChild(); node;
1066 node = node->GetNextNode(whereToLook)) {
1067 auto* el = Element::FromNode(*node);
1068 if (!el) {
1069 continue;
1072 const bool autofocus = el->GetBoolAttr(nsGkAtoms::autofocus);
1074 if (autofocus) {
1075 if (IsFocusable(el)) {
1076 // Found an autofocus candidate.
1077 return el;
1079 } else if (!potentialFocus) {
1080 if (Focusable focusable = IsFocusable(el)) {
1081 if (IsHTMLElement(nsGkAtoms::dialog)) {
1082 if (focusable.mTabIndex >= 0) {
1083 // If focusTarget is a dialog element and descendant is sequentially
1084 // focusable, then set focusableArea to descendant.
1085 potentialFocus = el;
1087 } else {
1088 // This element could be the one if we can't find an
1089 // autofocus candidate which has the precedence.
1090 potentialFocus = el;
1095 if (!autofocus && potentialFocus) {
1096 // Nothing else to do, we are not looking for more focusable elements
1097 // here.
1098 continue;
1101 if (auto* shadow = el->GetShadowRoot()) {
1102 if (shadow->DelegatesFocus()) {
1103 if (Element* delegatedFocus = shadow->GetFocusDelegate(aFlags)) {
1104 if (autofocus) {
1105 // This element has autofocus and we found an focus delegates
1106 // in its descendants, so use the focus delegates
1107 return delegatedFocus;
1109 if (!potentialFocus) {
1110 potentialFocus = delegatedFocus;
1117 return potentialFocus;
1120 Focusable nsIContent::IsFocusableWithoutStyle(IsFocusableFlags) {
1121 // Default, not tabbable
1122 return {};
1125 void nsIContent::SetAssignedSlot(HTMLSlotElement* aSlot) {
1126 MOZ_ASSERT(aSlot || GetExistingExtendedContentSlots());
1127 ExtendedContentSlots()->mAssignedSlot = aSlot;
1130 #ifdef MOZ_DOM_LIST
1131 void nsIContent::Dump() { List(); }
1132 #endif
1134 void FragmentOrElement::GetTextContentInternal(nsAString& aTextContent,
1135 OOMReporter& aError) {
1136 if (!nsContentUtils::GetNodeTextContent(this, true, aTextContent, fallible)) {
1137 aError.ReportOOM();
1141 void FragmentOrElement::SetTextContentInternal(const nsAString& aTextContent,
1142 nsIPrincipal* aSubjectPrincipal,
1143 ErrorResult& aError) {
1144 bool tryReuse = false;
1145 if (!aTextContent.IsEmpty()) {
1146 if (nsIContent* firstChild = GetFirstChild()) {
1147 tryReuse = firstChild->NodeType() == TEXT_NODE &&
1148 !firstChild->GetNextSibling() &&
1149 firstChild->OwnedOnlyByTheDOMAndFrameTrees() &&
1150 #ifdef ACCESSIBILITY
1151 !GetAccService() &&
1152 #endif
1153 !OwnerDoc()->MayHaveDOMMutationObservers() &&
1154 !nsContentUtils::HasMutationListeners(
1155 OwnerDoc(), NS_EVENT_BITS_MUTATION_ALL);
1159 aError = nsContentUtils::SetNodeTextContent(this, aTextContent, tryReuse);
1162 void FragmentOrElement::DestroyContent() {
1163 // Drop any servo data. We do this before the RemovedFromDocument call below
1164 // so that it doesn't need to try to keep the style state sane when shuffling
1165 // around the flattened tree.
1167 // TODO(emilio): I suspect this can be asserted against instead, with a bit of
1168 // effort to avoid calling Document::Destroy with a shell...
1169 if (IsElement()) {
1170 AsElement()->ClearServoData();
1173 #ifdef DEBUG
1174 uint32_t oldChildCount = GetChildCount();
1175 #endif
1177 for (nsIContent* child = GetFirstChild(); child;
1178 child = child->GetNextSibling()) {
1179 child->DestroyContent();
1180 MOZ_ASSERT(child->GetParent() == this,
1181 "Mutating the tree during XBL destructors is evil");
1184 MOZ_ASSERT(oldChildCount == GetChildCount(),
1185 "Mutating the tree during XBL destructors is evil");
1187 if (ShadowRoot* shadowRoot = GetShadowRoot()) {
1188 shadowRoot->DestroyContent();
1192 void FragmentOrElement::SaveSubtreeState() {
1193 for (nsIContent* child = GetFirstChild(); child;
1194 child = child->GetNextSibling()) {
1195 child->SaveSubtreeState();
1198 // FIXME(bug 1469277): Pretty sure this wants to dig into shadow trees as
1199 // well.
1202 //----------------------------------------------------------------------
1204 // Generic DOMNode implementations
1206 void FragmentOrElement::FireNodeInserted(
1207 Document* aDoc, nsINode* aParent,
1208 const nsTArray<nsCOMPtr<nsIContent>>& aNodes) {
1209 for (const nsCOMPtr<nsIContent>& childContent : aNodes) {
1210 if (nsContentUtils::WantMutationEvents(
1211 childContent, NS_EVENT_BITS_MUTATION_NODEINSERTED, aParent)) {
1212 InternalMutationEvent mutation(true, eLegacyNodeInserted);
1213 mutation.mRelatedNode = aParent;
1215 mozAutoSubtreeModified subtree(aDoc, aParent);
1216 AsyncEventDispatcher::RunDOMEventWhenSafe(*childContent, mutation);
1221 //----------------------------------------------------------------------
1223 // nsISupports implementation
1225 #define SUBTREE_UNBINDINGS_PER_RUNNABLE 500
1227 class ContentUnbinder : public Runnable {
1228 public:
1229 ContentUnbinder() : Runnable("ContentUnbinder") { mLast = this; }
1231 ~ContentUnbinder() { Run(); }
1233 void UnbindSubtree(nsIContent* aNode) {
1234 if (aNode->NodeType() != nsINode::ELEMENT_NODE &&
1235 aNode->NodeType() != nsINode::DOCUMENT_FRAGMENT_NODE) {
1236 return;
1238 FragmentOrElement* container = static_cast<FragmentOrElement*>(aNode);
1239 if (container->HasChildren()) {
1240 // Invalidate cached array of child nodes
1241 container->InvalidateChildNodes();
1243 while (container->HasChildren()) {
1244 // Hold a strong ref to the node when we remove it, because we may be
1245 // the last reference to it. We need to call DisconnectChild()
1246 // before calling UnbindFromTree, since this last can notify various
1247 // observers and they should really see consistent
1248 // tree state.
1249 // If this code changes, change the corresponding code in
1250 // FragmentOrElement's and Document's unlink impls.
1251 nsCOMPtr<nsIContent> child = container->GetLastChild();
1252 container->DisconnectChild(child);
1253 UnbindSubtree(child);
1254 child->UnbindFromTree();
1259 NS_IMETHOD Run() override {
1260 nsAutoScriptBlocker scriptBlocker;
1261 uint32_t len = mSubtreeRoots.Length();
1262 if (len) {
1263 for (uint32_t i = 0; i < len; ++i) {
1264 UnbindSubtree(mSubtreeRoots[i]);
1266 mSubtreeRoots.Clear();
1268 nsCycleCollector_dispatchDeferredDeletion();
1269 if (this == sContentUnbinder) {
1270 sContentUnbinder = nullptr;
1271 if (mNext) {
1272 RefPtr<ContentUnbinder> next;
1273 next.swap(mNext);
1274 sContentUnbinder = next;
1275 next->mLast = mLast;
1276 mLast = nullptr;
1277 NS_DispatchToCurrentThreadQueue(next.forget(),
1278 EventQueuePriority::Idle);
1281 return NS_OK;
1284 static void UnbindAll() {
1285 RefPtr<ContentUnbinder> ub = sContentUnbinder;
1286 sContentUnbinder = nullptr;
1287 while (ub) {
1288 ub->Run();
1289 ub = ub->mNext;
1293 static void Append(nsIContent* aSubtreeRoot) {
1294 if (!sContentUnbinder) {
1295 sContentUnbinder = new ContentUnbinder();
1296 nsCOMPtr<nsIRunnable> e = sContentUnbinder;
1297 NS_DispatchToCurrentThreadQueue(e.forget(), EventQueuePriority::Idle);
1300 if (sContentUnbinder->mLast->mSubtreeRoots.Length() >=
1301 SUBTREE_UNBINDINGS_PER_RUNNABLE) {
1302 sContentUnbinder->mLast->mNext = new ContentUnbinder();
1303 sContentUnbinder->mLast = sContentUnbinder->mLast->mNext;
1305 sContentUnbinder->mLast->mSubtreeRoots.AppendElement(aSubtreeRoot);
1308 private:
1309 AutoTArray<nsCOMPtr<nsIContent>, SUBTREE_UNBINDINGS_PER_RUNNABLE>
1310 mSubtreeRoots;
1311 RefPtr<ContentUnbinder> mNext;
1312 ContentUnbinder* mLast;
1313 static ContentUnbinder* sContentUnbinder;
1316 ContentUnbinder* ContentUnbinder::sContentUnbinder = nullptr;
1318 void FragmentOrElement::ClearContentUnbinder() { ContentUnbinder::UnbindAll(); }
1320 // Note, _INHERITED macro isn't used here since nsINode implementations are
1321 // rather special.
1322 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(FragmentOrElement)
1324 // We purposefully don't UNLINK_BEGIN_INHERITED here.
1325 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement)
1326 nsIContent::Unlink(tmp);
1328 // Unlink child content (and unbind our subtree).
1329 if (tmp->UnoptimizableCCNode() || !nsCCUncollectableMarker::sGeneration) {
1330 // Don't allow script to run while we're unbinding everything.
1331 nsAutoScriptBlocker scriptBlocker;
1332 while (tmp->HasChildren()) {
1333 // Hold a strong ref to the node when we remove it, because we may be
1334 // the last reference to it.
1335 // If this code changes, change the corresponding code in Document's
1336 // unlink impl and ContentUnbinder::UnbindSubtree.
1337 nsCOMPtr<nsIContent> child = tmp->GetLastChild();
1338 tmp->DisconnectChild(child);
1339 child->UnbindFromTree();
1341 } else if (!tmp->GetParent() && tmp->HasChildren()) {
1342 ContentUnbinder::Append(tmp);
1343 } /* else {
1344 The subtree root will end up to a ContentUnbinder, and that will
1345 unbind the child nodes.
1346 } */
1348 if (ShadowRoot* shadowRoot = tmp->GetShadowRoot()) {
1349 shadowRoot->Unbind();
1350 tmp->ExtendedDOMSlots()->mShadowRoot = nullptr;
1353 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1355 void FragmentOrElement::MarkNodeChildren(nsINode* aNode) {
1356 JSObject* o = GetJSObjectChild(aNode);
1357 if (o) {
1358 JS::ExposeObjectToActiveJS(o);
1361 EventListenerManager* elm = aNode->GetExistingListenerManager();
1362 if (elm) {
1363 elm->MarkForCC();
1367 nsINode* FindOptimizableSubtreeRoot(nsINode* aNode) {
1368 nsINode* p;
1369 while ((p = aNode->GetParentNode())) {
1370 if (aNode->UnoptimizableCCNode()) {
1371 return nullptr;
1373 aNode = p;
1376 if (aNode->UnoptimizableCCNode()) {
1377 return nullptr;
1379 return aNode;
1382 StaticAutoPtr<nsTHashSet<nsINode*>> gCCBlackMarkedNodes;
1384 static void ClearBlackMarkedNodes() {
1385 if (!gCCBlackMarkedNodes) {
1386 return;
1388 for (nsINode* n : *gCCBlackMarkedNodes) {
1389 n->SetCCMarkedRoot(false);
1390 n->SetInCCBlackTree(false);
1392 gCCBlackMarkedNodes = nullptr;
1395 // static
1396 void FragmentOrElement::RemoveBlackMarkedNode(nsINode* aNode) {
1397 if (!gCCBlackMarkedNodes) {
1398 return;
1400 gCCBlackMarkedNodes->Remove(aNode);
1403 static bool IsCertainlyAliveNode(nsINode* aNode, Document* aDoc) {
1404 MOZ_ASSERT(aNode->GetComposedDoc() == aDoc);
1406 // Marked to be in-CC-generation or if the document is an svg image that's
1407 // being kept alive by the image cache. (Note that an svg image's internal
1408 // SVG document will receive an OnPageHide() call when it gets purged from
1409 // the image cache; hence, we use IsVisible() as a hint that the document is
1410 // actively being kept alive by the cache.)
1411 return nsCCUncollectableMarker::InGeneration(aDoc->GetMarkedCCGeneration()) ||
1412 (nsCCUncollectableMarker::sGeneration && aDoc->IsBeingUsedAsImage() &&
1413 aDoc->IsVisible());
1416 // static
1417 bool FragmentOrElement::CanSkipInCC(nsINode* aNode) {
1418 // Don't try to optimize anything during shutdown.
1419 if (nsCCUncollectableMarker::sGeneration == 0) {
1420 return false;
1423 Document* currentDoc = aNode->GetComposedDoc();
1424 if (currentDoc && IsCertainlyAliveNode(aNode, currentDoc)) {
1425 return !NeedsScriptTraverse(aNode);
1428 // Bail out early if aNode is somewhere in anonymous content,
1429 // or otherwise unusual.
1430 if (aNode->UnoptimizableCCNode()) {
1431 return false;
1434 nsINode* root = currentDoc ? static_cast<nsINode*>(currentDoc)
1435 : FindOptimizableSubtreeRoot(aNode);
1436 if (!root) {
1437 return false;
1440 // Subtree has been traversed already.
1441 if (root->CCMarkedRoot()) {
1442 return root->InCCBlackTree() && !NeedsScriptTraverse(aNode);
1445 if (!gCCBlackMarkedNodes) {
1446 gCCBlackMarkedNodes = new nsTHashSet<nsINode*>(1020);
1449 // nodesToUnpurple contains nodes which will be removed
1450 // from the purple buffer if the DOM tree is known-live.
1451 AutoTArray<nsIContent*, 1020> nodesToUnpurple;
1452 // grayNodes need script traverse, so they aren't removed from
1453 // the purple buffer, but are marked to be in known-live subtree so that
1454 // traverse is faster.
1455 AutoTArray<nsINode*, 1020> grayNodes;
1457 bool foundLiveWrapper = root->HasKnownLiveWrapper();
1458 if (root != currentDoc) {
1459 currentDoc = nullptr;
1460 if (NeedsScriptTraverse(root)) {
1461 grayNodes.AppendElement(root);
1462 } else if (static_cast<nsIContent*>(root)->IsPurple()) {
1463 nodesToUnpurple.AppendElement(static_cast<nsIContent*>(root));
1467 // Traverse the subtree and check if we could know without CC
1468 // that it is known-live.
1469 // Note, this traverse is non-virtual and inline, so it should be a lot faster
1470 // than CC's generic traverse.
1471 for (nsIContent* node = root->GetFirstChild(); node;
1472 node = node->GetNextNode(root)) {
1473 foundLiveWrapper = foundLiveWrapper || node->HasKnownLiveWrapper();
1474 if (foundLiveWrapper && currentDoc) {
1475 // If we can mark the whole document known-live, no need to optimize
1476 // so much, since when the next purple node in the document will be
1477 // handled, it is fast to check that currentDoc is in CCGeneration.
1478 break;
1480 if (NeedsScriptTraverse(node)) {
1481 // Gray nodes need real CC traverse.
1482 grayNodes.AppendElement(node);
1483 } else if (node->IsPurple()) {
1484 nodesToUnpurple.AppendElement(node);
1488 root->SetCCMarkedRoot(true);
1489 root->SetInCCBlackTree(foundLiveWrapper);
1490 gCCBlackMarkedNodes->Insert(root);
1492 if (!foundLiveWrapper) {
1493 return false;
1496 if (currentDoc) {
1497 // Special case documents. If we know the document is known-live,
1498 // we can mark the document to be in CCGeneration.
1499 currentDoc->MarkUncollectableForCCGeneration(
1500 nsCCUncollectableMarker::sGeneration);
1501 } else {
1502 for (uint32_t i = 0; i < grayNodes.Length(); ++i) {
1503 nsINode* node = grayNodes[i];
1504 node->SetInCCBlackTree(true);
1505 gCCBlackMarkedNodes->Insert(node);
1509 // Subtree is known-live, we can remove non-gray purple nodes from
1510 // purple buffer.
1511 for (uint32_t i = 0; i < nodesToUnpurple.Length(); ++i) {
1512 nsIContent* purple = nodesToUnpurple[i];
1513 // Can't remove currently handled purple node.
1514 if (purple != aNode) {
1515 purple->RemovePurple();
1518 return !NeedsScriptTraverse(aNode);
1521 AutoTArray<nsINode*, 1020>* gPurpleRoots = nullptr;
1522 AutoTArray<nsIContent*, 1020>* gNodesToUnbind = nullptr;
1524 void ClearCycleCollectorCleanupData() {
1525 if (gPurpleRoots) {
1526 uint32_t len = gPurpleRoots->Length();
1527 for (uint32_t i = 0; i < len; ++i) {
1528 nsINode* n = gPurpleRoots->ElementAt(i);
1529 n->SetIsPurpleRoot(false);
1531 delete gPurpleRoots;
1532 gPurpleRoots = nullptr;
1534 if (gNodesToUnbind) {
1535 uint32_t len = gNodesToUnbind->Length();
1536 for (uint32_t i = 0; i < len; ++i) {
1537 nsIContent* c = gNodesToUnbind->ElementAt(i);
1538 c->SetIsPurpleRoot(false);
1539 ContentUnbinder::Append(c);
1541 delete gNodesToUnbind;
1542 gNodesToUnbind = nullptr;
1546 static bool ShouldClearPurple(nsIContent* aContent) {
1547 MOZ_ASSERT(aContent);
1548 if (aContent->IsPurple()) {
1549 return true;
1552 JSObject* o = GetJSObjectChild(aContent);
1553 if (o && JS::ObjectIsMarkedGray(o)) {
1554 return true;
1557 if (aContent->HasListenerManager()) {
1558 return true;
1561 return aContent->HasProperties();
1564 // If aNode is not optimizable, but is an element
1565 // with a frame in a document which has currently active presshell,
1566 // we can act as if it was optimizable. When the primary frame dies, aNode
1567 // will end up to the purple buffer because of the refcount change.
1568 bool NodeHasActiveFrame(Document* aCurrentDoc, nsINode* aNode) {
1569 return aCurrentDoc->GetPresShell() && aNode->IsElement() &&
1570 aNode->AsElement()->GetPrimaryFrame();
1573 // CanSkip checks if aNode is known-live, and if it is, returns true. If aNode
1574 // is in a known-live DOM tree, CanSkip may also remove other objects from
1575 // purple buffer and unmark event listeners and user data. If the root of the
1576 // DOM tree is a document, less optimizations are done since checking the
1577 // liveness of the current document is usually fast and we don't want slow down
1578 // such common cases.
1579 bool FragmentOrElement::CanSkip(nsINode* aNode, bool aRemovingAllowed) {
1580 // Don't try to optimize anything during shutdown.
1581 if (nsCCUncollectableMarker::sGeneration == 0) {
1582 return false;
1585 bool unoptimizable = aNode->UnoptimizableCCNode();
1586 Document* currentDoc = aNode->GetComposedDoc();
1587 if (currentDoc && IsCertainlyAliveNode(aNode, currentDoc) &&
1588 (!unoptimizable || NodeHasActiveFrame(currentDoc, aNode))) {
1589 MarkNodeChildren(aNode);
1590 return true;
1593 if (unoptimizable) {
1594 return false;
1597 nsINode* root = currentDoc ? static_cast<nsINode*>(currentDoc)
1598 : FindOptimizableSubtreeRoot(aNode);
1599 if (!root) {
1600 return false;
1603 // Subtree has been traversed already, and aNode has
1604 // been handled in a way that doesn't require revisiting it.
1605 if (root->IsPurpleRoot()) {
1606 return false;
1609 // nodesToClear contains nodes which are either purple or
1610 // gray.
1611 AutoTArray<nsIContent*, 1020> nodesToClear;
1613 bool foundLiveWrapper = root->HasKnownLiveWrapper();
1614 bool domOnlyCycle = false;
1615 if (root != currentDoc) {
1616 currentDoc = nullptr;
1617 if (!foundLiveWrapper) {
1618 domOnlyCycle = static_cast<nsIContent*>(root)->OwnedOnlyByTheDOMTree();
1620 if (ShouldClearPurple(static_cast<nsIContent*>(root))) {
1621 nodesToClear.AppendElement(static_cast<nsIContent*>(root));
1625 // Traverse the subtree and check if we could know without CC
1626 // that it is known-live.
1627 // Note, this traverse is non-virtual and inline, so it should be a lot faster
1628 // than CC's generic traverse.
1629 for (nsIContent* node = root->GetFirstChild(); node;
1630 node = node->GetNextNode(root)) {
1631 foundLiveWrapper = foundLiveWrapper || node->HasKnownLiveWrapper();
1632 if (foundLiveWrapper) {
1633 domOnlyCycle = false;
1634 if (currentDoc) {
1635 // If we can mark the whole document live, no need to optimize
1636 // so much, since when the next purple node in the document will be
1637 // handled, it is fast to check that the currentDoc is in CCGeneration.
1638 break;
1640 // No need to put stuff to the nodesToClear array, if we can clear it
1641 // already here.
1642 if (node->IsPurple() && (node != aNode || aRemovingAllowed)) {
1643 node->RemovePurple();
1645 MarkNodeChildren(node);
1646 } else {
1647 domOnlyCycle = domOnlyCycle && node->OwnedOnlyByTheDOMTree();
1648 if (ShouldClearPurple(node)) {
1649 // Collect interesting nodes which we can clear if we find that
1650 // they are kept alive in a known-live tree or are in a DOM-only cycle.
1651 nodesToClear.AppendElement(node);
1656 if (!currentDoc || !foundLiveWrapper) {
1657 root->SetIsPurpleRoot(true);
1658 if (domOnlyCycle) {
1659 if (!gNodesToUnbind) {
1660 gNodesToUnbind = new AutoTArray<nsIContent*, 1020>();
1662 gNodesToUnbind->AppendElement(static_cast<nsIContent*>(root));
1663 for (uint32_t i = 0; i < nodesToClear.Length(); ++i) {
1664 nsIContent* n = nodesToClear[i];
1665 if ((n != aNode || aRemovingAllowed) && n->IsPurple()) {
1666 n->RemovePurple();
1669 return true;
1670 } else {
1671 if (!gPurpleRoots) {
1672 gPurpleRoots = new AutoTArray<nsINode*, 1020>();
1674 gPurpleRoots->AppendElement(root);
1678 if (!foundLiveWrapper) {
1679 return false;
1682 if (currentDoc) {
1683 // Special case documents. If we know the document is known-live,
1684 // we can mark the document to be in CCGeneration.
1685 currentDoc->MarkUncollectableForCCGeneration(
1686 nsCCUncollectableMarker::sGeneration);
1687 MarkNodeChildren(currentDoc);
1690 // Subtree is known-live, so we can remove purple nodes from
1691 // purple buffer and mark stuff that to be certainly alive.
1692 for (uint32_t i = 0; i < nodesToClear.Length(); ++i) {
1693 nsIContent* n = nodesToClear[i];
1694 MarkNodeChildren(n);
1695 // Can't remove currently handled purple node,
1696 // unless aRemovingAllowed is true.
1697 if ((n != aNode || aRemovingAllowed) && n->IsPurple()) {
1698 n->RemovePurple();
1701 return true;
1704 bool FragmentOrElement::CanSkipThis(nsINode* aNode) {
1705 if (nsCCUncollectableMarker::sGeneration == 0) {
1706 return false;
1708 if (aNode->HasKnownLiveWrapper()) {
1709 return true;
1711 Document* c = aNode->GetComposedDoc();
1712 return ((c && IsCertainlyAliveNode(aNode, c)) || aNode->InCCBlackTree()) &&
1713 !NeedsScriptTraverse(aNode);
1716 void FragmentOrElement::InitCCCallbacks() {
1717 nsCycleCollector_setForgetSkippableCallback(ClearCycleCollectorCleanupData);
1718 nsCycleCollector_setBeforeUnlinkCallback(ClearBlackMarkedNodes);
1721 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(FragmentOrElement)
1722 return FragmentOrElement::CanSkip(tmp, aRemovingAllowed);
1723 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1725 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(FragmentOrElement)
1726 return FragmentOrElement::CanSkipInCC(tmp);
1727 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1729 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(FragmentOrElement)
1730 return FragmentOrElement::CanSkipThis(tmp);
1731 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1733 // We purposefully don't TRAVERSE_BEGIN_INHERITED here. All the bits
1734 // we should traverse should be added here or in nsINode::Traverse.
1735 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement)
1736 if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
1737 char name[512];
1738 uint32_t nsid = tmp->GetNameSpaceID();
1739 nsAtomCString localName(tmp->NodeInfo()->NameAtom());
1740 nsAutoCString uri;
1741 if (tmp->OwnerDoc()->GetDocumentURI()) {
1742 uri = tmp->OwnerDoc()->GetDocumentURI()->GetSpecOrDefault();
1745 nsAutoString id;
1746 nsAtom* idAtom = tmp->GetID();
1747 if (idAtom) {
1748 id.AppendLiteral(" id='");
1749 id.Append(nsDependentAtomString(idAtom));
1750 id.Append('\'');
1753 nsAutoString classes;
1754 const nsAttrValue* classAttrValue =
1755 tmp->IsElement() ? tmp->AsElement()->GetClasses() : nullptr;
1756 if (classAttrValue) {
1757 classes.AppendLiteral(" class='");
1758 nsAutoString classString;
1759 classAttrValue->ToString(classString);
1760 classString.ReplaceChar(char16_t('\n'), char16_t(' '));
1761 classes.Append(classString);
1762 classes.Append('\'');
1765 nsAutoCString orphan;
1766 if (!tmp->IsInComposedDoc()) {
1767 orphan.AppendLiteral(" (orphan)");
1770 const char* nsuri = nsNameSpaceManager::GetNameSpaceDisplayName(nsid);
1771 SprintfLiteral(name, "FragmentOrElement %s %s%s%s%s %s", nsuri,
1772 localName.get(), NS_ConvertUTF16toUTF8(id).get(),
1773 NS_ConvertUTF16toUTF8(classes).get(), orphan.get(),
1774 uri.get());
1775 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
1776 } else {
1777 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(FragmentOrElement, tmp->mRefCnt.get())
1780 if (!nsIContent::Traverse(tmp, cb)) {
1781 return NS_SUCCESS_INTERRUPTED_TRAVERSE;
1783 if (tmp->IsElement()) {
1784 Element* element = tmp->AsElement();
1785 // Traverse attribute names.
1786 uint32_t i;
1787 uint32_t attrs = element->GetAttrCount();
1788 for (i = 0; i < attrs; i++) {
1789 const nsAttrName* name = element->GetUnsafeAttrNameAt(i);
1790 if (!name->IsAtom()) {
1791 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mAttrs[i]->NodeInfo()");
1792 cb.NoteNativeChild(name->NodeInfo(),
1793 NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo));
1797 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1799 NS_INTERFACE_MAP_BEGIN(FragmentOrElement)
1800 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(FragmentOrElement)
1801 NS_INTERFACE_MAP_END_INHERITING(nsIContent)
1803 //----------------------------------------------------------------------
1805 const nsTextFragment* FragmentOrElement::GetText() { return nullptr; }
1807 uint32_t FragmentOrElement::TextLength() const {
1808 // We can remove this assertion if it turns out to be useful to be able
1809 // to depend on this returning 0
1810 MOZ_ASSERT_UNREACHABLE("called FragmentOrElement::TextLength");
1812 return 0;
1815 bool FragmentOrElement::TextIsOnlyWhitespace() { return false; }
1817 bool FragmentOrElement::ThreadSafeTextIsOnlyWhitespace() const { return false; }
1819 static inline bool IsVoidTag(nsAtom* aTag) {
1820 static const nsAtom* voidElements[] = {
1821 nsGkAtoms::area, nsGkAtoms::base, nsGkAtoms::basefont,
1822 nsGkAtoms::bgsound, nsGkAtoms::br, nsGkAtoms::col,
1823 nsGkAtoms::embed, nsGkAtoms::frame, nsGkAtoms::hr,
1824 nsGkAtoms::img, nsGkAtoms::input, nsGkAtoms::keygen,
1825 nsGkAtoms::link, nsGkAtoms::meta, nsGkAtoms::param,
1826 nsGkAtoms::source, nsGkAtoms::track, nsGkAtoms::wbr};
1828 static mozilla::BitBloomFilter<12, nsAtom> sFilter;
1829 static bool sInitialized = false;
1830 if (!sInitialized) {
1831 sInitialized = true;
1832 for (uint32_t i = 0; i < std::size(voidElements); ++i) {
1833 sFilter.add(voidElements[i]);
1837 if (sFilter.mightContain(aTag)) {
1838 for (uint32_t i = 0; i < std::size(voidElements); ++i) {
1839 if (aTag == voidElements[i]) {
1840 return true;
1844 return false;
1847 /* static */
1848 bool FragmentOrElement::IsHTMLVoid(nsAtom* aLocalName) {
1849 return aLocalName && IsVoidTag(aLocalName);
1852 void FragmentOrElement::GetMarkup(bool aIncludeSelf, nsAString& aMarkup) {
1853 aMarkup.Truncate();
1855 Document* doc = OwnerDoc();
1856 if (IsInHTMLDocument()) {
1857 nsContentUtils::SerializeNodeToMarkup(this, !aIncludeSelf, aMarkup, false,
1858 {});
1859 return;
1862 nsAutoString contentType;
1863 doc->GetContentType(contentType);
1864 bool tryToCacheEncoder = !aIncludeSelf;
1866 nsCOMPtr<nsIDocumentEncoder> docEncoder = doc->GetCachedEncoder();
1867 if (!docEncoder) {
1868 docEncoder = do_createDocumentEncoder(
1869 PromiseFlatCString(NS_ConvertUTF16toUTF8(contentType)).get());
1871 if (!docEncoder) {
1872 // This could be some type for which we create a synthetic document. Try
1873 // again as XML
1874 contentType.AssignLiteral("application/xml");
1875 docEncoder = do_createDocumentEncoder("application/xml");
1876 // Don't try to cache the encoder since it would point to a different
1877 // contentType once it has been reinitialized.
1878 tryToCacheEncoder = false;
1881 NS_ENSURE_TRUE_VOID(docEncoder);
1883 uint32_t flags = nsIDocumentEncoder::OutputEncodeBasicEntities |
1884 // Output DOM-standard newlines
1885 nsIDocumentEncoder::OutputLFLineBreak |
1886 // Don't do linebreaking that's not present in
1887 // the source
1888 nsIDocumentEncoder::OutputRaw |
1889 // Only check for mozdirty when necessary (bug 599983)
1890 nsIDocumentEncoder::OutputIgnoreMozDirty;
1892 if (IsEditable()) {
1893 nsCOMPtr<Element> elem = do_QueryInterface(this);
1894 TextEditor* textEditor = elem ? elem->GetTextEditorInternal() : nullptr;
1895 if (textEditor && textEditor->OutputsMozDirty()) {
1896 flags &= ~nsIDocumentEncoder::OutputIgnoreMozDirty;
1900 DebugOnly<nsresult> rv = docEncoder->NativeInit(doc, contentType, flags);
1901 MOZ_ASSERT(NS_SUCCEEDED(rv));
1903 if (aIncludeSelf) {
1904 docEncoder->SetNode(this);
1905 } else {
1906 docEncoder->SetContainerNode(this);
1908 rv = docEncoder->EncodeToString(aMarkup);
1909 MOZ_ASSERT(NS_SUCCEEDED(rv));
1910 if (tryToCacheEncoder) {
1911 doc->SetCachedEncoder(docEncoder.forget());
1915 static bool ContainsMarkup(const nsAString& aStr) {
1916 // Note: we can't use FindCharInSet because null is one of the characters we
1917 // want to search for.
1918 const char16_t* start = aStr.BeginReading();
1919 const char16_t* end = aStr.EndReading();
1921 while (start != end) {
1922 char16_t c = *start;
1923 if (c == char16_t('<') || c == char16_t('&') || c == char16_t('\r') ||
1924 c == char16_t('\0')) {
1925 return true;
1927 ++start;
1930 return false;
1933 void FragmentOrElement::SetInnerHTMLInternal(const nsAString& aInnerHTML,
1934 ErrorResult& aError) {
1935 // Keep "this" alive should be guaranteed by the caller, and also the content
1936 // of a template element (if this is one) should never been released by from
1937 // this during this call. Therefore, using raw pointer here is safe.
1938 FragmentOrElement* target = this;
1939 // Handle template case.
1940 if (target->IsTemplateElement()) {
1941 DocumentFragment* frag =
1942 static_cast<HTMLTemplateElement*>(target)->Content();
1943 MOZ_ASSERT(frag);
1944 target = frag;
1946 // Fast-path for strings with no markup. Limit this to short strings, to
1947 // avoid ContainsMarkup taking too long. The choice for 100 is based on
1948 // gut feeling.
1950 // Don't do this for elements with a weird parser insertion mode, for
1951 // instance setting innerHTML = "" on a <html> element should add the
1952 // optional <head> and <body> elements.
1953 if (!target->HasWeirdParserInsertionMode() && aInnerHTML.Length() < 100 &&
1954 !ContainsMarkup(aInnerHTML)) {
1955 aError = nsContentUtils::SetNodeTextContent(target, aInnerHTML, false);
1956 return;
1959 // mozAutoSubtreeModified keeps the owner document alive. Therefore, using a
1960 // raw pointer here is safe.
1961 Document* const doc = target->OwnerDoc();
1963 // Batch possible DOMSubtreeModified events.
1964 mozAutoSubtreeModified subtree(doc, nullptr);
1966 target->FireNodeRemovedForChildren();
1968 // Needed when innerHTML is used in combination with contenteditable
1969 mozAutoDocUpdate updateBatch(doc, true);
1971 // Remove childnodes.
1972 nsAutoMutationBatch mb(target, true, false);
1973 target->RemoveAllChildren(true);
1974 mb.RemovalDone();
1976 nsAutoScriptLoaderDisabler sld(doc);
1978 FragmentOrElement* parseContext = this;
1979 if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(this)) {
1980 // Fix up the context to be the host of the ShadowRoot. See
1981 // https://w3c.github.io/DOM-Parsing/#dom-innerhtml-innerhtml setter step 1.
1982 parseContext = shadowRoot->GetHost();
1985 if (doc->IsHTMLDocument()) {
1986 doc->SuspendDOMNotifications();
1987 nsAtom* contextLocalName = parseContext->NodeInfo()->NameAtom();
1988 int32_t contextNameSpaceID = parseContext->GetNameSpaceID();
1990 int32_t oldChildCount = target->GetChildCount();
1991 aError = nsContentUtils::ParseFragmentHTML(
1992 aInnerHTML, target, contextLocalName, contextNameSpaceID,
1993 doc->GetCompatibilityMode() == eCompatibility_NavQuirks, true);
1994 doc->ResumeDOMNotifications();
1995 if (target->GetFirstChild()) {
1996 MutationObservers::NotifyContentAppended(target, target->GetFirstChild());
1998 mb.NodesAdded();
1999 // HTML5 parser has notified, but not fired mutation events.
2000 nsContentUtils::FireMutationEventsForDirectParsing(doc, target,
2001 oldChildCount);
2002 } else {
2003 RefPtr<DocumentFragment> df = nsContentUtils::CreateContextualFragment(
2004 parseContext, aInnerHTML, true, aError);
2005 if (!aError.Failed()) {
2006 // Suppress assertion about node removal mutation events that can't have
2007 // listeners anyway, because no one has had the chance to register
2008 // mutation listeners on the fragment that comes from the parser.
2009 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
2011 target->AppendChild(*df, aError);
2012 mb.NodesAdded();
2017 void FragmentOrElement::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
2018 size_t* aNodeSize) const {
2019 nsIContent::AddSizeOfExcludingThis(aSizes, aNodeSize);
2021 nsDOMSlots* slots = GetExistingDOMSlots();
2022 if (slots) {
2023 *aNodeSize += slots->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf);