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/. */
8 * Base class for all element classes and DocumentFragment.
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/DeclarationBlock.h"
20 #include "mozilla/EffectSet.h"
21 #include "mozilla/EventDispatcher.h"
22 #include "mozilla/EventListenerManager.h"
23 #include "mozilla/ElementAnimationData.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 "nsDOMAttributeMap.h"
35 #include "mozilla/dom/NodeInfo.h"
36 #include "mozilla/dom/Event.h"
37 #include "mozilla/dom/ScriptLoader.h"
38 #include "mozilla/dom/TouchEvent.h"
39 #include "mozilla/dom/CustomElementRegistry.h"
40 #include "mozilla/dom/Document.h"
41 #include "mozilla/dom/DocumentInlines.h"
42 #include "nsIControllers.h"
43 #include "nsIDocumentEncoder.h"
44 #include "nsFocusManager.h"
45 #include "nsIScriptGlobalObject.h"
46 #include "nsNetUtil.h"
48 #include "nsIAnonymousContentCreator.h"
49 #include "nsPresContext.h"
50 #include "nsStyleConsts.h"
52 #include "nsUnicharUtils.h"
54 #include "nsDOMCSSAttrDeclaration.h"
55 #include "nsNameSpaceManager.h"
56 #include "nsContentList.h"
57 #include "nsDOMTokenList.h"
59 #include "nsDOMString.h"
60 #include "nsXULElement.h"
61 #include "mozilla/InternalMutationEvent.h"
62 #include "mozilla/MouseEvents.h"
63 #include "nsAttrValueOrString.h"
64 #include "nsQueryObject.h"
65 #include "nsFrameSelection.h"
70 #include "nsFrameLoader.h"
71 #include "nsPIDOMWindow.h"
72 #include "nsLayoutUtils.h"
73 #include "nsGkAtoms.h"
74 #include "nsContentUtils.h"
75 #include "nsTextFragment.h"
76 #include "nsContentCID.h"
77 #include "nsWindowSizes.h"
79 #include "nsIWidget.h"
81 #include "nsNodeInfoManager.h"
82 #include "nsGenericHTMLElement.h"
83 #include "nsContentCreatorFunctions.h"
85 #include "nsViewManager.h"
86 #include "nsIScrollableFrame.h"
87 #include "ChildIterator.h"
88 #include "nsTextNode.h"
89 #include "mozilla/dom/NodeListBinding.h"
91 #include "nsCCUncollectableMarker.h"
93 #include "mozAutoDocUpdate.h"
95 #include "mozilla/Sprintf.h"
96 #include "nsDOMMutationObserver.h"
97 #include "nsWrapperCacheInlines.h"
98 #include "nsCycleCollector.h"
99 #include "xpcpublic.h"
100 #include "mozilla/Telemetry.h"
102 #include "mozilla/CORSMode.h"
104 #include "mozilla/dom/ShadowRoot.h"
105 #include "mozilla/dom/HTMLSlotElement.h"
106 #include "mozilla/dom/HTMLTemplateElement.h"
107 #include "mozilla/dom/SVGUseElement.h"
109 #include "nsStyledElement.h"
110 #include "nsIContentInlines.h"
111 #include "nsChildContentList.h"
112 #include "mozilla/BloomFilter.h"
114 #include "NodeUbiReporting.h"
117 # include "nsAccessibilityService.h"
120 using namespace mozilla
;
121 using namespace mozilla::dom
;
123 int32_t nsIContent::sTabFocusModel
= eTabFocus_any
;
124 bool nsIContent::sTabFocusModelAppliesToXUL
= false;
125 uint64_t nsMutationGuard::sGeneration
= 0;
127 NS_IMPL_CYCLE_COLLECTION_CLASS(nsIContent
)
129 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsIContent
)
130 MOZ_ASSERT_UNREACHABLE("Our subclasses don't call us");
131 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
133 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsIContent
)
134 MOZ_ASSERT_UNREACHABLE("Our subclasses don't call us");
135 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
137 NS_INTERFACE_MAP_BEGIN(nsIContent
)
138 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
139 // Don't bother to QI to cycle collection, because our CC impl is
140 // not doing anything anyway.
141 NS_INTERFACE_MAP_ENTRY(nsIContent
)
142 NS_INTERFACE_MAP_ENTRY(nsINode
)
143 NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget
)
144 NS_INTERFACE_MAP_ENTRY_TEAROFF(nsISupportsWeakReference
,
145 new nsNodeSupportsWeakRefTearoff(this))
146 // DOM bindings depend on the identity pointer being the
147 // same as nsINode (which nsIContent inherits).
148 NS_INTERFACE_MAP_ENTRY(nsISupports
)
151 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsIContent
)
153 NS_IMPL_DOMARENA_DESTROY(nsIContent
)
155 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE_AND_DESTROY(nsIContent
,
159 nsIContent
* nsIContent::FindFirstNonChromeOnlyAccessContent() const {
160 // This handles also nested native anonymous content.
161 for (const nsIContent
* content
= this; content
;
162 content
= content
->GetChromeOnlyAccessSubtreeRootParent()) {
163 if (!content
->ChromeOnlyAccess()) {
164 // Oops, this function signature allows casting const to
165 // non-const. (Then again, so does GetFirstChild()->GetParent().)
166 return const_cast<nsIContent
*>(content
);
172 // https://dom.spec.whatwg.org/#dom-slotable-assignedslot
173 HTMLSlotElement
* nsIContent::GetAssignedSlotByMode() const {
175 * Get slotable's assigned slot for the result of
176 * find a slot with open flag UNSET [1].
178 * [1] https://dom.spec.whatwg.org/#assign-a-slot
180 HTMLSlotElement
* slot
= GetAssignedSlot();
185 MOZ_ASSERT(GetParent());
186 MOZ_ASSERT(GetParent()->GetShadowRoot());
189 * Additional check for open flag SET:
190 * If slotable’s parent’s shadow root's mode is not "open",
193 if (GetParent()->GetShadowRoot()->IsClosed()) {
200 nsIContent::IMEState
nsIContent::GetDesiredIMEState() {
202 // Check for the special case where we're dealing with elements which don't
203 // have the editable flag set, but are readwrite (such as text controls).
205 !AsElement()->State().HasState(ElementState::READWRITE
)) {
206 return IMEState(IMEEnabled::Disabled
);
209 // NOTE: The content for independent editors (e.g., input[type=text],
210 // textarea) must override this method, so, we don't need to worry about
212 nsIContent
* editableAncestor
= GetEditingHost();
214 // This is in another editable content, use the result of it.
215 if (editableAncestor
&& editableAncestor
!= this) {
216 return editableAncestor
->GetDesiredIMEState();
218 Document
* doc
= GetComposedDoc();
220 return IMEState(IMEEnabled::Disabled
);
222 nsPresContext
* pc
= doc
->GetPresContext();
224 return IMEState(IMEEnabled::Disabled
);
226 HTMLEditor
* htmlEditor
= nsContentUtils::GetHTMLEditor(pc
);
228 return IMEState(IMEEnabled::Disabled
);
231 htmlEditor
->GetPreferredIMEState(&state
);
235 bool nsIContent::HasIndependentSelection() const {
236 nsIFrame
* frame
= GetPrimaryFrame();
237 return (frame
&& frame
->HasAnyStateBits(NS_FRAME_INDEPENDENT_SELECTION
));
240 dom::Element
* nsIContent::GetEditingHost() {
241 // If this isn't editable, return nullptr.
246 Document
* doc
= GetComposedDoc();
251 // If this is in designMode, we should return <body>
252 if (IsInDesignMode() && !IsInShadowTree()) {
253 return doc
->GetBodyElement();
256 dom::Element
* editableParentElement
= nullptr;
257 for (dom::Element
* parent
= GetParentElement();
258 parent
&& parent
->HasFlag(NODE_IS_EDITABLE
);
259 parent
= editableParentElement
->GetParentElement()) {
260 editableParentElement
= parent
;
262 return editableParentElement
? editableParentElement
263 : dom::Element::FromNode(this);
266 nsresult
nsIContent::LookupNamespaceURIInternal(
267 const nsAString
& aNamespacePrefix
, nsAString
& aNamespaceURI
) const {
268 if (aNamespacePrefix
.EqualsLiteral("xml")) {
269 // Special-case for xml prefix
270 aNamespaceURI
.AssignLiteral("http://www.w3.org/XML/1998/namespace");
274 if (aNamespacePrefix
.EqualsLiteral("xmlns")) {
275 // Special-case for xmlns prefix
276 aNamespaceURI
.AssignLiteral("http://www.w3.org/2000/xmlns/");
281 if (!aNamespacePrefix
.IsEmpty()) {
282 name
= NS_Atomize(aNamespacePrefix
);
283 NS_ENSURE_TRUE(name
, NS_ERROR_OUT_OF_MEMORY
);
285 name
= nsGkAtoms::xmlns
;
287 // Trace up the content parent chain looking for the namespace
288 // declaration that declares aNamespacePrefix.
289 for (Element
* element
= GetAsElementOrParentElement(); element
;
290 element
= element
->GetParentElement()) {
291 if (element
->GetAttr(kNameSpaceID_XMLNS
, name
, aNamespaceURI
)) {
295 return NS_ERROR_FAILURE
;
298 nsAtom
* nsIContent::GetLang() const {
299 for (const Element
* element
= GetAsElementOrParentElement(); element
;
300 element
= element
->GetParentElement()) {
301 if (!element
->GetAttrCount()) {
305 // xml:lang has precedence over lang on HTML elements (see
306 // XHTML1 section C.7).
307 const nsAttrValue
* attr
=
308 element
->GetParsedAttr(nsGkAtoms::lang
, kNameSpaceID_XML
);
309 if (!attr
&& element
->SupportsLangAttr()) {
310 attr
= element
->GetParsedAttr(nsGkAtoms::lang
);
313 MOZ_ASSERT(attr
->Type() == nsAttrValue::eAtom
);
314 MOZ_ASSERT(attr
->GetAtomValue());
315 return attr
->GetAtomValue();
322 nsIURI
* nsIContent::GetBaseURI(bool aTryUseXHRDocBaseURI
) const {
323 if (SVGUseElement
* use
= GetContainingSVGUseShadowHost()) {
324 if (URLExtraData
* data
= use
->GetContentURLData()) {
325 return data
->BaseURI();
329 return OwnerDoc()->GetBaseURI(aTryUseXHRDocBaseURI
);
332 nsIURI
* nsIContent::GetBaseURIForStyleAttr() const {
333 if (SVGUseElement
* use
= GetContainingSVGUseShadowHost()) {
334 if (URLExtraData
* data
= use
->GetContentURLData()) {
335 return data
->BaseURI();
338 // This also ignores the case that SVG inside XBL binding.
339 // But it is probably fine.
340 return OwnerDoc()->GetDocBaseURI();
343 already_AddRefed
<URLExtraData
> nsIContent::GetURLDataForStyleAttr(
344 nsIPrincipal
* aSubjectPrincipal
) const {
345 if (SVGUseElement
* use
= GetContainingSVGUseShadowHost()) {
346 if (URLExtraData
* data
= use
->GetContentURLData()) {
347 return do_AddRef(data
);
350 auto* doc
= OwnerDoc();
351 if (aSubjectPrincipal
&& aSubjectPrincipal
!= NodePrincipal()) {
352 nsCOMPtr
<nsIReferrerInfo
> referrerInfo
=
353 doc
->ReferrerInfoForInternalCSSAndSVGResources();
355 return MakeAndAddRef
<URLExtraData
>(doc
->GetDocBaseURI(), referrerInfo
,
358 return do_AddRef(doc
->DefaultStyleAttrURLData());
361 void nsIContent::ConstructUbiNode(void* storage
) {
362 JS::ubi::Concrete
<nsIContent
>::construct(storage
, this);
365 bool nsIContent::InclusiveDescendantMayNeedSpellchecking(HTMLEditor
* aEditor
) {
366 // Return true if the node may have elements as children, since those or their
367 // descendants may have spellcheck attributes.
368 return HasFlag(NODE_MAY_HAVE_ELEMENT_CHILDREN
) ||
369 mozInlineSpellChecker::ShouldSpellCheckNode(aEditor
, this);
372 //----------------------------------------------------------------------
374 static inline JSObject
* GetJSObjectChild(nsWrapperCache
* aCache
) {
375 return aCache
->PreservingWrapper() ? aCache
->GetWrapperPreserveColor()
379 static bool NeedsScriptTraverse(nsINode
* aNode
) {
380 return aNode
->PreservingWrapper() && aNode
->GetWrapperPreserveColor() &&
381 !aNode
->HasKnownLiveWrapperAndDoesNotNeedTracing(aNode
);
384 //----------------------------------------------------------------------
386 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAttrChildContentList
)
387 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAttrChildContentList
)
389 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsAttrChildContentList
, mNode
)
391 // If the wrapper is known-live, the list can't be part of a garbage cycle.
392 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsAttrChildContentList
)
393 return tmp
->HasKnownLiveWrapper();
394 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
396 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsAttrChildContentList
)
397 return tmp
->HasKnownLiveWrapperAndDoesNotNeedTracing(tmp
);
398 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
400 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsAttrChildContentList
)
401 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
403 NS_INTERFACE_TABLE_HEAD(nsAttrChildContentList
)
404 NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
405 NS_INTERFACE_TABLE(nsAttrChildContentList
, nsINodeList
)
406 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsAttrChildContentList
)
409 JSObject
* nsAttrChildContentList::WrapObject(
410 JSContext
* cx
, JS::Handle
<JSObject
*> aGivenProto
) {
411 return NodeList_Binding::Wrap(cx
, this, aGivenProto
);
414 uint32_t nsAttrChildContentList::Length() {
415 return mNode
? mNode
->GetChildCount() : 0;
418 nsIContent
* nsAttrChildContentList::Item(uint32_t aIndex
) {
420 return mNode
->GetChildAt_Deprecated(aIndex
);
426 int32_t nsAttrChildContentList::IndexOf(nsIContent
* aContent
) {
428 return mNode
->ComputeIndexOf_Deprecated(aContent
);
434 //----------------------------------------------------------------------
435 uint32_t nsParentNodeChildContentList::Length() {
436 if (!mIsCacheValid
&& !ValidateCache()) {
440 MOZ_ASSERT(mIsCacheValid
);
442 return mCachedChildArray
.Length();
445 nsIContent
* nsParentNodeChildContentList::Item(uint32_t aIndex
) {
446 if (!mIsCacheValid
&& !ValidateCache()) {
450 MOZ_ASSERT(mIsCacheValid
);
452 return mCachedChildArray
.SafeElementAt(aIndex
, nullptr);
455 int32_t nsParentNodeChildContentList::IndexOf(nsIContent
* aContent
) {
456 if (!mIsCacheValid
&& !ValidateCache()) {
460 MOZ_ASSERT(mIsCacheValid
);
462 return mCachedChildArray
.IndexOf(aContent
);
465 bool nsParentNodeChildContentList::ValidateCache() {
466 MOZ_ASSERT(!mIsCacheValid
);
467 MOZ_ASSERT(mCachedChildArray
.IsEmpty());
469 nsINode
* parent
= GetParentObject();
474 for (nsIContent
* node
= parent
->GetFirstChild(); node
;
475 node
= node
->GetNextSibling()) {
476 mCachedChildArray
.AppendElement(node
);
478 mIsCacheValid
= true;
483 //----------------------------------------------------------------------
485 nsIHTMLCollection
* FragmentOrElement::Children() {
486 nsDOMSlots
* slots
= DOMSlots();
488 if (!slots
->mChildrenList
) {
489 slots
->mChildrenList
=
490 new nsContentList(this, kNameSpaceID_Wildcard
, nsGkAtoms::_asterisk
,
491 nsGkAtoms::_asterisk
, false);
494 return slots
->mChildrenList
;
497 //----------------------------------------------------------------------
499 NS_IMPL_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff
, mNode
)
501 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff
)
502 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
503 NS_INTERFACE_MAP_END_AGGREGATED(mNode
)
505 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNodeSupportsWeakRefTearoff
)
506 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNodeSupportsWeakRefTearoff
)
509 nsNodeSupportsWeakRefTearoff::GetWeakReference(
510 nsIWeakReference
** aInstancePtr
) {
511 nsINode::nsSlots
* slots
= mNode
->Slots();
512 if (!slots
->mWeakReference
) {
513 slots
->mWeakReference
= new nsNodeWeakReference(mNode
);
516 NS_ADDREF(*aInstancePtr
= slots
->mWeakReference
);
521 //----------------------------------------------------------------------
523 static const size_t MaxDOMSlotSizeAllowed
=
524 #ifdef HAVE_64BIT_BUILD
530 static_assert(sizeof(nsINode::nsSlots
) <= MaxDOMSlotSizeAllowed
,
531 "DOM slots cannot be grown without consideration");
532 static_assert(sizeof(FragmentOrElement::nsDOMSlots
) <= MaxDOMSlotSizeAllowed
,
533 "DOM slots cannot be grown without consideration");
535 void nsIContent::nsExtendedContentSlots::UnlinkExtendedSlots(nsIContent
&) {
536 mContainingShadow
= nullptr;
537 mAssignedSlot
= nullptr;
540 void nsIContent::nsExtendedContentSlots::TraverseExtendedSlots(
541 nsCycleCollectionTraversalCallback
& aCb
) {
542 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb
, "mExtendedSlots->mContainingShadow");
543 aCb
.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent
*, mContainingShadow
));
545 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb
, "mExtendedSlots->mAssignedSlot");
546 aCb
.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent
*, mAssignedSlot
.get()));
549 nsIContent::nsExtendedContentSlots::nsExtendedContentSlots() = default;
551 nsIContent::nsExtendedContentSlots::~nsExtendedContentSlots() {
552 MOZ_ASSERT(!mManualSlotAssignment
);
555 size_t nsIContent::nsExtendedContentSlots::SizeOfExcludingThis(
556 MallocSizeOf aMallocSizeOf
) const {
557 // For now, nothing to measure here. We don't actually own any of our
562 FragmentOrElement::nsDOMSlots::nsDOMSlots()
563 : nsIContent::nsContentSlots(), mDataset(nullptr) {
564 MOZ_COUNT_CTOR(nsDOMSlots
);
567 FragmentOrElement::nsDOMSlots::~nsDOMSlots() {
568 MOZ_COUNT_DTOR(nsDOMSlots
);
571 mAttributeMap
->DropReference();
575 void FragmentOrElement::nsDOMSlots::Traverse(
576 nsCycleCollectionTraversalCallback
& aCb
) {
577 nsIContent::nsContentSlots::Traverse(aCb
);
579 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb
, "mSlots->mStyle");
580 aCb
.NoteXPCOMChild(mStyle
.get());
582 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb
, "mSlots->mAttributeMap");
583 aCb
.NoteXPCOMChild(mAttributeMap
.get());
585 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb
, "mSlots->mChildrenList");
586 aCb
.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsINodeList
*, mChildrenList
));
588 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb
, "mSlots->mClassList");
589 aCb
.NoteXPCOMChild(mClassList
.get());
591 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb
, "mSlots->mPart");
592 aCb
.NoteXPCOMChild(mPart
.get());
595 void FragmentOrElement::nsDOMSlots::Unlink(nsINode
& aNode
) {
596 nsIContent::nsContentSlots::Unlink(aNode
);
599 mAttributeMap
->DropReference();
600 mAttributeMap
= nullptr;
602 mChildrenList
= nullptr;
603 mClassList
= nullptr;
607 size_t FragmentOrElement::nsDOMSlots::SizeOfIncludingThis(
608 MallocSizeOf aMallocSizeOf
) const {
609 size_t n
= aMallocSizeOf(this);
611 nsExtendedContentSlots
* extendedSlots
= GetExtendedContentSlots();
613 if (OwnsExtendedSlots()) {
614 n
+= aMallocSizeOf(extendedSlots
);
617 n
+= extendedSlots
->SizeOfExcludingThis(aMallocSizeOf
);
621 n
+= mAttributeMap
->SizeOfIncludingThis(aMallocSizeOf
);
625 n
+= mChildrenList
->SizeOfIncludingThis(aMallocSizeOf
);
628 // Measurement of the following members may be added later if DMD finds it is
630 // - Superclass members (nsINode::nsSlots)
635 // The following member are not measured:
636 // - mControllers: because it is non-owning
640 FragmentOrElement::nsExtendedDOMSlots::nsExtendedDOMSlots() = default;
642 FragmentOrElement::nsExtendedDOMSlots::~nsExtendedDOMSlots() = default;
644 void FragmentOrElement::nsExtendedDOMSlots::UnlinkExtendedSlots(
645 nsIContent
& aContent
) {
646 nsIContent::nsExtendedContentSlots::UnlinkExtendedSlots(aContent
);
648 // mShadowRoot will similarly be cleared explicitly from
649 // FragmentOrElement::Unlink.
650 mSMILOverrideStyle
= nullptr;
651 mControllers
= nullptr;
652 mLabelsList
= nullptr;
653 mPopoverData
= nullptr;
654 if (mCustomElementData
) {
655 mCustomElementData
->Unlink();
656 mCustomElementData
= nullptr;
659 mAnimations
= nullptr;
660 aContent
.ClearMayHaveAnimations();
662 mExplicitlySetAttrElements
.Clear();
663 mRadioGroupContainer
= nullptr;
666 void FragmentOrElement::nsExtendedDOMSlots::TraverseExtendedSlots(
667 nsCycleCollectionTraversalCallback
& aCb
) {
668 nsIContent::nsExtendedContentSlots::TraverseExtendedSlots(aCb
);
670 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb
, "mExtendedSlots->mSMILOverrideStyle");
671 aCb
.NoteXPCOMChild(mSMILOverrideStyle
.get());
673 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb
, "mExtendedSlots->mControllers");
674 aCb
.NoteXPCOMChild(mControllers
);
676 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb
, "mExtendedSlots->mLabelsList");
677 aCb
.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsINodeList
*, mLabelsList
));
679 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb
, "mExtendedSlots->mShadowRoot");
680 aCb
.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent
*, mShadowRoot
));
682 if (mCustomElementData
) {
683 mCustomElementData
->Traverse(aCb
);
686 mAnimations
->Traverse(aCb
);
688 if (mRadioGroupContainer
) {
689 RadioGroupContainer::Traverse(mRadioGroupContainer
.get(), aCb
);
693 size_t FragmentOrElement::nsExtendedDOMSlots::SizeOfExcludingThis(
694 MallocSizeOf aMallocSizeOf
) const {
696 nsIContent::nsExtendedContentSlots::SizeOfExcludingThis(aMallocSizeOf
);
698 // We own mSMILOverrideStyle but there seems to be no memory reporting on CSS
699 // declarations? At least report the memory the declaration takes up
701 if (mSMILOverrideStyle
) {
702 n
+= aMallocSizeOf(mSMILOverrideStyle
);
705 // We don't really own mSMILOverrideStyleDeclaration. mSMILOverrideStyle owns
708 // We don't seem to have memory reporting for nsXULControllers. At least
709 // report the memory it's using directly.
711 n
+= aMallocSizeOf(mControllers
);
715 n
+= mLabelsList
->SizeOfIncludingThis(aMallocSizeOf
);
718 // mShadowRoot should be handled during normal DOM tree memory reporting, just
719 // like kids, siblings, etc.
721 if (mCustomElementData
) {
722 n
+= mCustomElementData
->SizeOfIncludingThis(aMallocSizeOf
);
725 if (mRadioGroupContainer
) {
726 n
+= mRadioGroupContainer
->SizeOfIncludingThis(aMallocSizeOf
);
732 FragmentOrElement::FragmentOrElement(
733 already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
)
734 : nsIContent(std::move(aNodeInfo
)) {}
736 FragmentOrElement::~FragmentOrElement() {
737 MOZ_ASSERT(!IsInUncomposedDoc(),
738 "Please remove this from the document properly");
744 static nsINode
* FindChromeAccessOnlySubtreeOwner(nsINode
* aNode
) {
745 if (!aNode
->ChromeOnlyAccess()) {
748 return const_cast<nsIContent
*>(aNode
->GetChromeOnlyAccessSubtreeRootParent());
751 nsINode
* FindChromeAccessOnlySubtreeOwner(EventTarget
* aTarget
) {
752 nsINode
* node
= nsINode::FromEventTargetOrNull(aTarget
);
756 return FindChromeAccessOnlySubtreeOwner(node
);
759 void nsIContent::GetEventTargetParent(EventChainPreVisitor
& aVisitor
) {
760 // FIXME! Document how this event retargeting works, Bug 329124.
761 aVisitor
.mCanHandle
= true;
762 aVisitor
.mMayHaveListenerManager
= HasListenerManager();
764 if (IsInShadowTree()) {
765 aVisitor
.mItemInShadowTree
= true;
768 // Don't propagate mouseover and mouseout events when mouse is moving
769 // inside chrome access only content.
770 bool isAnonForEvents
= IsRootOfChromeAccessOnlySubtree();
771 aVisitor
.mRootOfClosedTree
= isAnonForEvents
;
772 if ((aVisitor
.mEvent
->mMessage
== eMouseOver
||
773 aVisitor
.mEvent
->mMessage
== eMouseOut
||
774 aVisitor
.mEvent
->mMessage
== ePointerOver
||
775 aVisitor
.mEvent
->mMessage
== ePointerOut
) &&
776 // Check if we should stop event propagation when event has just been
777 // dispatched or when we're about to propagate from
778 // chrome access only subtree or if we are about to propagate out of
779 // a shadow root to a shadow root host.
780 ((this == aVisitor
.mEvent
->mOriginalTarget
&& !ChromeOnlyAccess()) ||
782 nsCOMPtr
<nsIContent
> relatedTarget
= nsIContent::FromEventTargetOrNull(
783 aVisitor
.mEvent
->AsMouseEvent()->mRelatedTarget
);
784 if (relatedTarget
&& relatedTarget
->OwnerDoc() == OwnerDoc()) {
785 // If current target is anonymous for events or we know that related
786 // target is descendant of an element which is anonymous for events,
787 // we may want to stop event propagation.
788 // If this is the original target, aVisitor.mRelatedTargetIsInAnon
790 if (isAnonForEvents
|| aVisitor
.mRelatedTargetIsInAnon
||
791 (aVisitor
.mEvent
->mOriginalTarget
== this &&
792 (aVisitor
.mRelatedTargetIsInAnon
=
793 relatedTarget
->ChromeOnlyAccess()))) {
794 nsINode
* anonOwner
= FindChromeAccessOnlySubtreeOwner(this);
796 nsINode
* anonOwnerRelated
=
797 FindChromeAccessOnlySubtreeOwner(relatedTarget
);
798 if (anonOwnerRelated
) {
799 // Note, anonOwnerRelated may still be inside some other
800 // native anonymous subtree. The case where anonOwner is still
801 // inside native anonymous subtree will be handled when event
802 // propagates up in the DOM tree.
803 while (anonOwner
!= anonOwnerRelated
&&
804 anonOwnerRelated
->ChromeOnlyAccess()) {
806 FindChromeAccessOnlySubtreeOwner(anonOwnerRelated
);
808 if (anonOwner
== anonOwnerRelated
) {
810 nsCOMPtr
<nsIContent
> originalTarget
=
811 nsIContent::FromEventTargetOrNull(
812 aVisitor
.mEvent
->mOriginalTarget
);
813 nsAutoString ot
, ct
, rt
;
814 if (originalTarget
) {
815 originalTarget
->NodeInfo()->NameAtom()->ToString(ot
);
817 NodeInfo()->NameAtom()->ToString(ct
);
818 relatedTarget
->NodeInfo()->NameAtom()->ToString(rt
);
820 "Stopping %s propagation:"
821 "\n\toriginalTarget=%s \n\tcurrentTarget=%s %s"
822 "\n\trelatedTarget=%s %s \n%s",
823 (aVisitor
.mEvent
->mMessage
== eMouseOver
) ? "mouseover"
825 NS_ConvertUTF16toUTF8(ot
).get(),
826 NS_ConvertUTF16toUTF8(ct
).get(),
828 ? "(is native anonymous)"
829 : (ChromeOnlyAccess() ? "(is in native anonymous subtree)"
831 NS_ConvertUTF16toUTF8(rt
).get(),
832 relatedTarget
->ChromeOnlyAccess()
833 ? "(is in native anonymous subtree)"
836 relatedTarget
->FindFirstNonChromeOnlyAccessContent() ==
837 originalTarget
->FindFirstNonChromeOnlyAccessContent())
839 : "Wrong event propagation!?!\n");
841 aVisitor
.SetParentTarget(nullptr, false);
842 // Event should not propagate to non-anon content.
843 aVisitor
.mCanHandle
= isAnonForEvents
;
852 // Event parent is the assigned slot, if node is assigned, or node's parent
854 HTMLSlotElement
* slot
= GetAssignedSlot();
855 nsIContent
* parent
= slot
? slot
: GetParent();
857 // Event may need to be retargeted if this is the root of a native
858 // anonymous content subtree or event is dispatched somewhere inside XBL.
859 if (isAnonForEvents
) {
861 // If a DOM event is explicitly dispatched using node.dispatchEvent(), then
862 // all the events are allowed even in the native anonymous content..
863 nsCOMPtr
<nsIContent
> t
=
864 nsIContent::FromEventTargetOrNull(aVisitor
.mEvent
->mOriginalTarget
);
865 NS_ASSERTION(!t
|| !t
->ChromeOnlyAccess() ||
866 aVisitor
.mEvent
->mClass
!= eMutationEventClass
||
868 "Mutation event dispatched in native anonymous content!?!");
870 aVisitor
.mEventTargetAtParent
= parent
;
871 } else if (parent
&& aVisitor
.mOriginalTargetIsInAnon
) {
872 nsCOMPtr
<nsIContent
> content(
873 nsIContent::FromEventTargetOrNull(aVisitor
.mEvent
->mTarget
));
875 content
->GetClosestNativeAnonymousSubtreeRootParentOrHost() == parent
) {
876 aVisitor
.mEventTargetAtParent
= parent
;
880 if (!aVisitor
.mEvent
->mFlags
.mComposedInNativeAnonymousContent
&&
881 IsRootOfNativeAnonymousSubtree() && OwnerDoc()->GetWindow()) {
882 aVisitor
.SetParentTarget(OwnerDoc()->GetWindow()->GetParentTarget(), true);
884 aVisitor
.SetParentTarget(parent
, false);
886 ShadowRoot
* root
= slot
->GetContainingShadow();
887 if (root
&& root
->IsClosed()) {
888 aVisitor
.mParentIsSlotInClosedTree
= true;
892 aVisitor
.SetParentTarget(GetComposedDoc(), false);
895 if (!ChromeOnlyAccess() && !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
901 // "Let relatedTarget be the result of retargeting event's relatedTarget
902 // against target if event's relatedTarget is non-null, and null
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
908 bool initialTarget
= this == aVisitor
.mEvent
->mOriginalTarget
;
909 nsCOMPtr
<nsINode
> originalTargetAsNode
;
910 // Use of mOriginalTargetIsInAnon is an optimization here.
911 if (!initialTarget
&& aVisitor
.mOriginalTargetIsInAnon
) {
912 originalTargetAsNode
=
913 FindChromeAccessOnlySubtreeOwner(aVisitor
.mEvent
->mOriginalTarget
);
914 initialTarget
= originalTargetAsNode
== this;
917 nsCOMPtr
<nsINode
> relatedTargetAsNode
=
918 FindChromeAccessOnlySubtreeOwner(
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
) {
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
;
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
;
949 nsCOMPtr
<nsINode
> relatedTargetAsNode
=
950 FindChromeAccessOnlySubtreeOwner(
951 aVisitor
.mEvent
->mOriginalRelatedTarget
);
952 if (relatedTargetAsNode
) {
954 // "Let relatedTarget be the result of retargeting event's
955 // relatedTarget against parent if event's relatedTarget is non-null,
956 // and null otherwise.".
957 nsINode
* retargetedRelatedTarget
=
958 nsContentUtils::Retarget(relatedTargetAsNode
, this);
959 nsCOMPtr
<nsINode
> targetInKnownToBeHandledScope
=
960 FindChromeAccessOnlySubtreeOwner(
961 aVisitor
.mTargetInKnownToBeHandledScope
);
962 // If aVisitor.mTargetInKnownToBeHandledScope wasn't nsINode,
963 // targetInKnownToBeHandledScope will be null. This may happen when
964 // dispatching event to Window object in a content page and
965 // propagating the event to a chrome Element.
966 if (targetInKnownToBeHandledScope
&&
967 IsShadowIncludingInclusiveDescendantOf(
968 targetInKnownToBeHandledScope
->SubtreeRoot())) {
969 // Part of step 11.4.
970 // "If target's root is a shadow-including inclusive ancestor of
972 // "...Append to an event path with event, parent, null,
973 // relatedTarget, " and slot-in-closed-tree."
974 aVisitor
.mRetargetedRelatedTarget
= retargetedRelatedTarget
;
975 } else if (this == retargetedRelatedTarget
) {
977 // "Otherwise, if parent and relatedTarget are identical, then set
979 aVisitor
.IgnoreCurrentTargetBecauseOfShadowDOMRetargeting();
980 // Old code relies on mTarget to point to the first element which
981 // was not added to the event target chain because of mCanHandle
982 // being false, but in Shadow DOM case mTarget really should
983 // point to a node in Shadow DOM.
984 aVisitor
.mEvent
->mTarget
= aVisitor
.mTargetInKnownToBeHandledScope
;
986 } else if (targetInKnownToBeHandledScope
) {
987 // Note, if targetInKnownToBeHandledScope is null,
988 // mTargetInKnownToBeHandledScope could be Window object in content
989 // page and we're in chrome document in the same process.
992 aVisitor
.mRetargetedRelatedTarget
= retargetedRelatedTarget
;
998 if (aVisitor
.mEvent
->mClass
== eTouchEventClass
) {
999 // Retarget touch objects.
1000 MOZ_ASSERT(!aVisitor
.mRetargetedTouchTargets
.isSome());
1001 aVisitor
.mRetargetedTouchTargets
.emplace();
1002 WidgetTouchEvent
* touchEvent
= aVisitor
.mEvent
->AsTouchEvent();
1003 WidgetTouchEvent::TouchArray
& touches
= touchEvent
->mTouches
;
1004 for (uint32_t i
= 0; i
< touches
.Length(); ++i
) {
1005 Touch
* touch
= touches
[i
];
1006 EventTarget
* originalTarget
= touch
->mOriginalTarget
;
1007 EventTarget
* touchTarget
= originalTarget
;
1008 nsCOMPtr
<nsINode
> targetAsNode
=
1009 nsINode::FromEventTargetOrNull(originalTarget
);
1011 EventTarget
* retargeted
=
1012 nsContentUtils::Retarget(targetAsNode
, this);
1014 touchTarget
= retargeted
;
1017 aVisitor
.mRetargetedTouchTargets
->AppendElement(touchTarget
);
1018 touch
->mTarget
= touchTarget
;
1020 MOZ_ASSERT(aVisitor
.mRetargetedTouchTargets
->Length() ==
1026 // Inform that we're about to exit the current scope.
1027 aVisitor
.mRelatedTargetRetargetedInCurrentScope
= false;
1031 bool nsIContent::IsFocusable(int32_t* aTabIndex
, bool aWithMouse
) {
1032 bool focusable
= IsFocusableInternal(aTabIndex
, aWithMouse
);
1033 // Ensure that the return value and aTabIndex are consistent in the case
1034 // we're in userfocusignored context.
1035 if (focusable
|| (aTabIndex
&& *aTabIndex
!= -1)) {
1041 Element
* nsIContent::GetFocusDelegate(bool aWithMouse
,
1042 bool aAutofocusOnly
) const {
1043 const nsIContent
* whereToLook
= this;
1044 if (ShadowRoot
* root
= GetShadowRoot()) {
1045 if (!root
->DelegatesFocus()) {
1046 // 1. If focusTarget is a shadow host and its shadow root 's delegates
1047 // focus is false, then return null.
1053 auto IsFocusable
= [&](Element
* aElement
) -> nsIFrame::Focusable
{
1054 nsIFrame
* frame
= aElement
->GetPrimaryFrame();
1060 return frame
->IsFocusable(aWithMouse
);
1063 Element
* potentialFocus
= nullptr;
1064 for (nsINode
* node
= whereToLook
->GetFirstChild(); node
;
1065 node
= node
->GetNextNode(whereToLook
)) {
1066 auto* el
= Element::FromNode(*node
);
1071 const bool autofocus
= el
->GetBoolAttr(nsGkAtoms::autofocus
);
1073 if (aAutofocusOnly
&& !autofocus
) {
1077 if (IsFocusable(el
)) {
1078 // Found an autofocus candidate.
1081 } else if (!potentialFocus
) {
1082 if (nsIFrame::Focusable focusable
= IsFocusable(el
)) {
1083 if (IsHTMLElement(nsGkAtoms::dialog
)) {
1084 if (focusable
.mTabIndex
>= 0) {
1085 // If focusTarget is a dialog element and descendant is sequentially
1086 // focusable, then set focusableArea to descendant.
1087 potentialFocus
= el
;
1090 // This element could be the one if we can't find an
1091 // autofocus candidate which has the precedence.
1092 potentialFocus
= el
;
1097 if (!autofocus
&& potentialFocus
) {
1098 // Nothing else to do, we are not looking for more focusable elements
1103 if (auto* shadow
= el
->GetShadowRoot()) {
1104 if (shadow
->DelegatesFocus()) {
1105 if (Element
* delegatedFocus
= shadow
->GetFocusDelegate(aWithMouse
)) {
1107 // This element has autofocus and we found an focus delegates
1108 // in its descendants, so use the focus delegates
1109 return delegatedFocus
;
1111 if (!potentialFocus
) {
1112 potentialFocus
= delegatedFocus
;
1119 return potentialFocus
;
1122 bool nsIContent::IsFocusableInternal(int32_t* aTabIndex
, bool aWithMouse
) {
1124 *aTabIndex
= -1; // Default, not tabbable
1129 void nsIContent::SetAssignedSlot(HTMLSlotElement
* aSlot
) {
1130 MOZ_ASSERT(aSlot
|| GetExistingExtendedContentSlots());
1131 ExtendedContentSlots()->mAssignedSlot
= aSlot
;
1134 static Maybe
<uint32_t> DoComputeFlatTreeIndexOf(FlattenedChildIterator
& aIter
,
1135 const nsINode
* aPossibleChild
) {
1136 if (aPossibleChild
->GetFlattenedTreeParentNode() != aIter
.Parent()) {
1140 uint32_t index
= 0u;
1141 for (nsIContent
* child
= aIter
.GetNextChild(); child
;
1142 child
= aIter
.GetNextChild()) {
1143 if (child
== aPossibleChild
) {
1153 Maybe
<uint32_t> nsIContent::ComputeFlatTreeIndexOf(
1154 const nsINode
* aPossibleChild
) const {
1155 if (!aPossibleChild
) {
1159 FlattenedChildIterator
iter(this);
1160 if (!iter
.ShadowDOMInvolved()) {
1161 auto index
= ComputeIndexOf(aPossibleChild
);
1162 MOZ_ASSERT(DoComputeFlatTreeIndexOf(iter
, aPossibleChild
) == index
);
1166 return DoComputeFlatTreeIndexOf(iter
, aPossibleChild
);
1170 void nsIContent::Dump() { List(); }
1173 void FragmentOrElement::GetTextContentInternal(nsAString
& aTextContent
,
1174 OOMReporter
& aError
) {
1175 if (!nsContentUtils::GetNodeTextContent(this, true, aTextContent
, fallible
)) {
1180 void FragmentOrElement::SetTextContentInternal(const nsAString
& aTextContent
,
1181 nsIPrincipal
* aSubjectPrincipal
,
1182 ErrorResult
& aError
) {
1183 bool tryReuse
= false;
1184 if (!aTextContent
.IsEmpty()) {
1185 if (nsIContent
* firstChild
= GetFirstChild()) {
1186 tryReuse
= firstChild
->NodeType() == TEXT_NODE
&&
1187 !firstChild
->GetNextSibling() &&
1188 firstChild
->OwnedOnlyByTheDOMAndFrameTrees() &&
1189 #ifdef ACCESSIBILITY
1192 !OwnerDoc()->MayHaveDOMMutationObservers() &&
1193 !nsContentUtils::HasMutationListeners(
1194 OwnerDoc(), NS_EVENT_BITS_MUTATION_ALL
);
1198 aError
= nsContentUtils::SetNodeTextContent(this, aTextContent
, tryReuse
);
1201 void FragmentOrElement::DestroyContent() {
1202 // Drop any servo data. We do this before the RemovedFromDocument call below
1203 // so that it doesn't need to try to keep the style state sane when shuffling
1204 // around the flattened tree.
1206 // TODO(emilio): I suspect this can be asserted against instead, with a bit of
1207 // effort to avoid calling Document::Destroy with a shell...
1209 AsElement()->ClearServoData();
1213 uint32_t oldChildCount
= GetChildCount();
1216 for (nsIContent
* child
= GetFirstChild(); child
;
1217 child
= child
->GetNextSibling()) {
1218 child
->DestroyContent();
1219 MOZ_ASSERT(child
->GetParent() == this,
1220 "Mutating the tree during XBL destructors is evil");
1223 MOZ_ASSERT(oldChildCount
== GetChildCount(),
1224 "Mutating the tree during XBL destructors is evil");
1226 if (ShadowRoot
* shadowRoot
= GetShadowRoot()) {
1227 shadowRoot
->DestroyContent();
1231 void FragmentOrElement::SaveSubtreeState() {
1232 for (nsIContent
* child
= GetFirstChild(); child
;
1233 child
= child
->GetNextSibling()) {
1234 child
->SaveSubtreeState();
1237 // FIXME(bug 1469277): Pretty sure this wants to dig into shadow trees as
1241 //----------------------------------------------------------------------
1243 // Generic DOMNode implementations
1245 void FragmentOrElement::FireNodeInserted(
1246 Document
* aDoc
, nsINode
* aParent
,
1247 const nsTArray
<nsCOMPtr
<nsIContent
>>& aNodes
) {
1248 for (const nsCOMPtr
<nsIContent
>& childContent
: aNodes
) {
1249 if (nsContentUtils::HasMutationListeners(
1250 childContent
, NS_EVENT_BITS_MUTATION_NODEINSERTED
, aParent
)) {
1251 InternalMutationEvent
mutation(true, eLegacyNodeInserted
);
1252 mutation
.mRelatedNode
= aParent
;
1254 mozAutoSubtreeModified
subtree(aDoc
, aParent
);
1255 AsyncEventDispatcher::RunDOMEventWhenSafe(*childContent
, mutation
);
1260 //----------------------------------------------------------------------
1262 // nsISupports implementation
1264 #define SUBTREE_UNBINDINGS_PER_RUNNABLE 500
1266 class ContentUnbinder
: public Runnable
{
1268 ContentUnbinder() : Runnable("ContentUnbinder") { mLast
= this; }
1270 ~ContentUnbinder() { Run(); }
1272 void UnbindSubtree(nsIContent
* aNode
) {
1273 if (aNode
->NodeType() != nsINode::ELEMENT_NODE
&&
1274 aNode
->NodeType() != nsINode::DOCUMENT_FRAGMENT_NODE
) {
1277 FragmentOrElement
* container
= static_cast<FragmentOrElement
*>(aNode
);
1278 if (container
->HasChildren()) {
1279 // Invalidate cached array of child nodes
1280 container
->InvalidateChildNodes();
1282 while (container
->HasChildren()) {
1283 // Hold a strong ref to the node when we remove it, because we may be
1284 // the last reference to it. We need to call DisconnectChild()
1285 // before calling UnbindFromTree, since this last can notify various
1286 // observers and they should really see consistent
1288 // If this code changes, change the corresponding code in
1289 // FragmentOrElement's and Document's unlink impls.
1290 nsCOMPtr
<nsIContent
> child
= container
->GetLastChild();
1291 container
->DisconnectChild(child
);
1292 UnbindSubtree(child
);
1293 child
->UnbindFromTree();
1298 NS_IMETHOD
Run() override
{
1299 nsAutoScriptBlocker scriptBlocker
;
1300 uint32_t len
= mSubtreeRoots
.Length();
1302 for (uint32_t i
= 0; i
< len
; ++i
) {
1303 UnbindSubtree(mSubtreeRoots
[i
]);
1305 mSubtreeRoots
.Clear();
1307 nsCycleCollector_dispatchDeferredDeletion();
1308 if (this == sContentUnbinder
) {
1309 sContentUnbinder
= nullptr;
1311 RefPtr
<ContentUnbinder
> next
;
1313 sContentUnbinder
= next
;
1314 next
->mLast
= mLast
;
1316 NS_DispatchToCurrentThreadQueue(next
.forget(),
1317 EventQueuePriority::Idle
);
1323 static void UnbindAll() {
1324 RefPtr
<ContentUnbinder
> ub
= sContentUnbinder
;
1325 sContentUnbinder
= nullptr;
1332 static void Append(nsIContent
* aSubtreeRoot
) {
1333 if (!sContentUnbinder
) {
1334 sContentUnbinder
= new ContentUnbinder();
1335 nsCOMPtr
<nsIRunnable
> e
= sContentUnbinder
;
1336 NS_DispatchToCurrentThreadQueue(e
.forget(), EventQueuePriority::Idle
);
1339 if (sContentUnbinder
->mLast
->mSubtreeRoots
.Length() >=
1340 SUBTREE_UNBINDINGS_PER_RUNNABLE
) {
1341 sContentUnbinder
->mLast
->mNext
= new ContentUnbinder();
1342 sContentUnbinder
->mLast
= sContentUnbinder
->mLast
->mNext
;
1344 sContentUnbinder
->mLast
->mSubtreeRoots
.AppendElement(aSubtreeRoot
);
1348 AutoTArray
<nsCOMPtr
<nsIContent
>, SUBTREE_UNBINDINGS_PER_RUNNABLE
>
1350 RefPtr
<ContentUnbinder
> mNext
;
1351 ContentUnbinder
* mLast
;
1352 static ContentUnbinder
* sContentUnbinder
;
1355 ContentUnbinder
* ContentUnbinder::sContentUnbinder
= nullptr;
1357 void FragmentOrElement::ClearContentUnbinder() { ContentUnbinder::UnbindAll(); }
1359 // Note, _INHERITED macro isn't used here since nsINode implementations are
1361 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(FragmentOrElement
)
1363 // We purposefully don't UNLINK_BEGIN_INHERITED here.
1364 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement
)
1365 nsIContent::Unlink(tmp
);
1367 if (tmp
->HasProperties()) {
1368 if (tmp
->IsElement()) {
1369 Element
* elem
= tmp
->AsElement();
1370 elem
->UnlinkIntersectionObservers();
1373 if (tmp
->IsHTMLElement() || tmp
->IsSVGElement()) {
1374 nsStaticAtom
* const* props
=
1375 Element::HTMLSVGPropertiesToTraverseAndUnlink();
1376 for (uint32_t i
= 0; props
[i
]; ++i
) {
1377 tmp
->RemoveProperty(props
[i
]);
1382 // Unlink child content (and unbind our subtree).
1383 if (tmp
->UnoptimizableCCNode() || !nsCCUncollectableMarker::sGeneration
) {
1384 // Don't allow script to run while we're unbinding everything.
1385 nsAutoScriptBlocker scriptBlocker
;
1386 while (tmp
->HasChildren()) {
1387 // Hold a strong ref to the node when we remove it, because we may be
1388 // the last reference to it.
1389 // If this code changes, change the corresponding code in Document's
1390 // unlink impl and ContentUnbinder::UnbindSubtree.
1391 nsCOMPtr
<nsIContent
> child
= tmp
->GetLastChild();
1392 tmp
->DisconnectChild(child
);
1393 child
->UnbindFromTree();
1395 } else if (!tmp
->GetParent() && tmp
->HasChildren()) {
1396 ContentUnbinder::Append(tmp
);
1398 The subtree root will end up to a ContentUnbinder, and that will
1399 unbind the child nodes.
1402 if (ShadowRoot
* shadowRoot
= tmp
->GetShadowRoot()) {
1403 shadowRoot
->Unbind();
1404 tmp
->ExtendedDOMSlots()->mShadowRoot
= nullptr;
1407 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1409 void FragmentOrElement::MarkNodeChildren(nsINode
* aNode
) {
1410 JSObject
* o
= GetJSObjectChild(aNode
);
1412 JS::ExposeObjectToActiveJS(o
);
1415 EventListenerManager
* elm
= aNode
->GetExistingListenerManager();
1421 nsINode
* FindOptimizableSubtreeRoot(nsINode
* aNode
) {
1423 while ((p
= aNode
->GetParentNode())) {
1424 if (aNode
->UnoptimizableCCNode()) {
1430 if (aNode
->UnoptimizableCCNode()) {
1436 StaticAutoPtr
<nsTHashSet
<nsINode
*>> gCCBlackMarkedNodes
;
1438 static void ClearBlackMarkedNodes() {
1439 if (!gCCBlackMarkedNodes
) {
1442 for (nsINode
* n
: *gCCBlackMarkedNodes
) {
1443 n
->SetCCMarkedRoot(false);
1444 n
->SetInCCBlackTree(false);
1446 gCCBlackMarkedNodes
= nullptr;
1450 void FragmentOrElement::RemoveBlackMarkedNode(nsINode
* aNode
) {
1451 if (!gCCBlackMarkedNodes
) {
1454 gCCBlackMarkedNodes
->Remove(aNode
);
1457 static bool IsCertainlyAliveNode(nsINode
* aNode
, Document
* aDoc
) {
1458 MOZ_ASSERT(aNode
->GetComposedDoc() == aDoc
);
1460 // Marked to be in-CC-generation or if the document is an svg image that's
1461 // being kept alive by the image cache. (Note that an svg image's internal
1462 // SVG document will receive an OnPageHide() call when it gets purged from
1463 // the image cache; hence, we use IsVisible() as a hint that the document is
1464 // actively being kept alive by the cache.)
1465 return nsCCUncollectableMarker::InGeneration(aDoc
->GetMarkedCCGeneration()) ||
1466 (nsCCUncollectableMarker::sGeneration
&& aDoc
->IsBeingUsedAsImage() &&
1471 bool FragmentOrElement::CanSkipInCC(nsINode
* aNode
) {
1472 // Don't try to optimize anything during shutdown.
1473 if (nsCCUncollectableMarker::sGeneration
== 0) {
1477 Document
* currentDoc
= aNode
->GetComposedDoc();
1478 if (currentDoc
&& IsCertainlyAliveNode(aNode
, currentDoc
)) {
1479 return !NeedsScriptTraverse(aNode
);
1482 // Bail out early if aNode is somewhere in anonymous content,
1483 // or otherwise unusual.
1484 if (aNode
->UnoptimizableCCNode()) {
1488 nsINode
* root
= currentDoc
? static_cast<nsINode
*>(currentDoc
)
1489 : FindOptimizableSubtreeRoot(aNode
);
1494 // Subtree has been traversed already.
1495 if (root
->CCMarkedRoot()) {
1496 return root
->InCCBlackTree() && !NeedsScriptTraverse(aNode
);
1499 if (!gCCBlackMarkedNodes
) {
1500 gCCBlackMarkedNodes
= new nsTHashSet
<nsINode
*>(1020);
1503 // nodesToUnpurple contains nodes which will be removed
1504 // from the purple buffer if the DOM tree is known-live.
1505 AutoTArray
<nsIContent
*, 1020> nodesToUnpurple
;
1506 // grayNodes need script traverse, so they aren't removed from
1507 // the purple buffer, but are marked to be in known-live subtree so that
1508 // traverse is faster.
1509 AutoTArray
<nsINode
*, 1020> grayNodes
;
1511 bool foundLiveWrapper
= root
->HasKnownLiveWrapper();
1512 if (root
!= currentDoc
) {
1513 currentDoc
= nullptr;
1514 if (NeedsScriptTraverse(root
)) {
1515 grayNodes
.AppendElement(root
);
1516 } else if (static_cast<nsIContent
*>(root
)->IsPurple()) {
1517 nodesToUnpurple
.AppendElement(static_cast<nsIContent
*>(root
));
1521 // Traverse the subtree and check if we could know without CC
1522 // that it is known-live.
1523 // Note, this traverse is non-virtual and inline, so it should be a lot faster
1524 // than CC's generic traverse.
1525 for (nsIContent
* node
= root
->GetFirstChild(); node
;
1526 node
= node
->GetNextNode(root
)) {
1527 foundLiveWrapper
= foundLiveWrapper
|| node
->HasKnownLiveWrapper();
1528 if (foundLiveWrapper
&& currentDoc
) {
1529 // If we can mark the whole document known-live, no need to optimize
1530 // so much, since when the next purple node in the document will be
1531 // handled, it is fast to check that currentDoc is in CCGeneration.
1534 if (NeedsScriptTraverse(node
)) {
1535 // Gray nodes need real CC traverse.
1536 grayNodes
.AppendElement(node
);
1537 } else if (node
->IsPurple()) {
1538 nodesToUnpurple
.AppendElement(node
);
1542 root
->SetCCMarkedRoot(true);
1543 root
->SetInCCBlackTree(foundLiveWrapper
);
1544 gCCBlackMarkedNodes
->Insert(root
);
1546 if (!foundLiveWrapper
) {
1551 // Special case documents. If we know the document is known-live,
1552 // we can mark the document to be in CCGeneration.
1553 currentDoc
->MarkUncollectableForCCGeneration(
1554 nsCCUncollectableMarker::sGeneration
);
1556 for (uint32_t i
= 0; i
< grayNodes
.Length(); ++i
) {
1557 nsINode
* node
= grayNodes
[i
];
1558 node
->SetInCCBlackTree(true);
1559 gCCBlackMarkedNodes
->Insert(node
);
1563 // Subtree is known-live, we can remove non-gray purple nodes from
1565 for (uint32_t i
= 0; i
< nodesToUnpurple
.Length(); ++i
) {
1566 nsIContent
* purple
= nodesToUnpurple
[i
];
1567 // Can't remove currently handled purple node.
1568 if (purple
!= aNode
) {
1569 purple
->RemovePurple();
1572 return !NeedsScriptTraverse(aNode
);
1575 AutoTArray
<nsINode
*, 1020>* gPurpleRoots
= nullptr;
1576 AutoTArray
<nsIContent
*, 1020>* gNodesToUnbind
= nullptr;
1578 void ClearCycleCollectorCleanupData() {
1580 uint32_t len
= gPurpleRoots
->Length();
1581 for (uint32_t i
= 0; i
< len
; ++i
) {
1582 nsINode
* n
= gPurpleRoots
->ElementAt(i
);
1583 n
->SetIsPurpleRoot(false);
1585 delete gPurpleRoots
;
1586 gPurpleRoots
= nullptr;
1588 if (gNodesToUnbind
) {
1589 uint32_t len
= gNodesToUnbind
->Length();
1590 for (uint32_t i
= 0; i
< len
; ++i
) {
1591 nsIContent
* c
= gNodesToUnbind
->ElementAt(i
);
1592 c
->SetIsPurpleRoot(false);
1593 ContentUnbinder::Append(c
);
1595 delete gNodesToUnbind
;
1596 gNodesToUnbind
= nullptr;
1600 static bool ShouldClearPurple(nsIContent
* aContent
) {
1601 MOZ_ASSERT(aContent
);
1602 if (aContent
->IsPurple()) {
1606 JSObject
* o
= GetJSObjectChild(aContent
);
1607 if (o
&& JS::ObjectIsMarkedGray(o
)) {
1611 if (aContent
->HasListenerManager()) {
1615 return aContent
->HasProperties();
1618 // If aNode is not optimizable, but is an element
1619 // with a frame in a document which has currently active presshell,
1620 // we can act as if it was optimizable. When the primary frame dies, aNode
1621 // will end up to the purple buffer because of the refcount change.
1622 bool NodeHasActiveFrame(Document
* aCurrentDoc
, nsINode
* aNode
) {
1623 return aCurrentDoc
->GetPresShell() && aNode
->IsElement() &&
1624 aNode
->AsElement()->GetPrimaryFrame();
1627 // CanSkip checks if aNode is known-live, and if it is, returns true. If aNode
1628 // is in a known-live DOM tree, CanSkip may also remove other objects from
1629 // purple buffer and unmark event listeners and user data. If the root of the
1630 // DOM tree is a document, less optimizations are done since checking the
1631 // liveness of the current document is usually fast and we don't want slow down
1632 // such common cases.
1633 bool FragmentOrElement::CanSkip(nsINode
* aNode
, bool aRemovingAllowed
) {
1634 // Don't try to optimize anything during shutdown.
1635 if (nsCCUncollectableMarker::sGeneration
== 0) {
1639 bool unoptimizable
= aNode
->UnoptimizableCCNode();
1640 Document
* currentDoc
= aNode
->GetComposedDoc();
1641 if (currentDoc
&& IsCertainlyAliveNode(aNode
, currentDoc
) &&
1642 (!unoptimizable
|| NodeHasActiveFrame(currentDoc
, aNode
))) {
1643 MarkNodeChildren(aNode
);
1647 if (unoptimizable
) {
1651 nsINode
* root
= currentDoc
? static_cast<nsINode
*>(currentDoc
)
1652 : FindOptimizableSubtreeRoot(aNode
);
1657 // Subtree has been traversed already, and aNode has
1658 // been handled in a way that doesn't require revisiting it.
1659 if (root
->IsPurpleRoot()) {
1663 // nodesToClear contains nodes which are either purple or
1665 AutoTArray
<nsIContent
*, 1020> nodesToClear
;
1667 bool foundLiveWrapper
= root
->HasKnownLiveWrapper();
1668 bool domOnlyCycle
= false;
1669 if (root
!= currentDoc
) {
1670 currentDoc
= nullptr;
1671 if (!foundLiveWrapper
) {
1672 domOnlyCycle
= static_cast<nsIContent
*>(root
)->OwnedOnlyByTheDOMTree();
1674 if (ShouldClearPurple(static_cast<nsIContent
*>(root
))) {
1675 nodesToClear
.AppendElement(static_cast<nsIContent
*>(root
));
1679 // Traverse the subtree and check if we could know without CC
1680 // that it is known-live.
1681 // Note, this traverse is non-virtual and inline, so it should be a lot faster
1682 // than CC's generic traverse.
1683 for (nsIContent
* node
= root
->GetFirstChild(); node
;
1684 node
= node
->GetNextNode(root
)) {
1685 foundLiveWrapper
= foundLiveWrapper
|| node
->HasKnownLiveWrapper();
1686 if (foundLiveWrapper
) {
1687 domOnlyCycle
= false;
1689 // If we can mark the whole document live, no need to optimize
1690 // so much, since when the next purple node in the document will be
1691 // handled, it is fast to check that the currentDoc is in CCGeneration.
1694 // No need to put stuff to the nodesToClear array, if we can clear it
1696 if (node
->IsPurple() && (node
!= aNode
|| aRemovingAllowed
)) {
1697 node
->RemovePurple();
1699 MarkNodeChildren(node
);
1701 domOnlyCycle
= domOnlyCycle
&& node
->OwnedOnlyByTheDOMTree();
1702 if (ShouldClearPurple(node
)) {
1703 // Collect interesting nodes which we can clear if we find that
1704 // they are kept alive in a known-live tree or are in a DOM-only cycle.
1705 nodesToClear
.AppendElement(node
);
1710 if (!currentDoc
|| !foundLiveWrapper
) {
1711 root
->SetIsPurpleRoot(true);
1713 if (!gNodesToUnbind
) {
1714 gNodesToUnbind
= new AutoTArray
<nsIContent
*, 1020>();
1716 gNodesToUnbind
->AppendElement(static_cast<nsIContent
*>(root
));
1717 for (uint32_t i
= 0; i
< nodesToClear
.Length(); ++i
) {
1718 nsIContent
* n
= nodesToClear
[i
];
1719 if ((n
!= aNode
|| aRemovingAllowed
) && n
->IsPurple()) {
1725 if (!gPurpleRoots
) {
1726 gPurpleRoots
= new AutoTArray
<nsINode
*, 1020>();
1728 gPurpleRoots
->AppendElement(root
);
1732 if (!foundLiveWrapper
) {
1737 // Special case documents. If we know the document is known-live,
1738 // we can mark the document to be in CCGeneration.
1739 currentDoc
->MarkUncollectableForCCGeneration(
1740 nsCCUncollectableMarker::sGeneration
);
1741 MarkNodeChildren(currentDoc
);
1744 // Subtree is known-live, so we can remove purple nodes from
1745 // purple buffer and mark stuff that to be certainly alive.
1746 for (uint32_t i
= 0; i
< nodesToClear
.Length(); ++i
) {
1747 nsIContent
* n
= nodesToClear
[i
];
1748 MarkNodeChildren(n
);
1749 // Can't remove currently handled purple node,
1750 // unless aRemovingAllowed is true.
1751 if ((n
!= aNode
|| aRemovingAllowed
) && n
->IsPurple()) {
1758 bool FragmentOrElement::CanSkipThis(nsINode
* aNode
) {
1759 if (nsCCUncollectableMarker::sGeneration
== 0) {
1762 if (aNode
->HasKnownLiveWrapper()) {
1765 Document
* c
= aNode
->GetComposedDoc();
1766 return ((c
&& IsCertainlyAliveNode(aNode
, c
)) || aNode
->InCCBlackTree()) &&
1767 !NeedsScriptTraverse(aNode
);
1770 void FragmentOrElement::InitCCCallbacks() {
1771 nsCycleCollector_setForgetSkippableCallback(ClearCycleCollectorCleanupData
);
1772 nsCycleCollector_setBeforeUnlinkCallback(ClearBlackMarkedNodes
);
1775 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(FragmentOrElement
)
1776 return FragmentOrElement::CanSkip(tmp
, aRemovingAllowed
);
1777 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1779 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(FragmentOrElement
)
1780 return FragmentOrElement::CanSkipInCC(tmp
);
1781 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1783 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(FragmentOrElement
)
1784 return FragmentOrElement::CanSkipThis(tmp
);
1785 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1787 // We purposefully don't TRAVERSE_BEGIN_INHERITED here. All the bits
1788 // we should traverse should be added here or in nsINode::Traverse.
1789 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement
)
1790 if (MOZ_UNLIKELY(cb
.WantDebugInfo())) {
1792 uint32_t nsid
= tmp
->GetNameSpaceID();
1793 nsAtomCString
localName(tmp
->NodeInfo()->NameAtom());
1795 if (tmp
->OwnerDoc()->GetDocumentURI()) {
1796 uri
= tmp
->OwnerDoc()->GetDocumentURI()->GetSpecOrDefault();
1800 nsAtom
* idAtom
= tmp
->GetID();
1802 id
.AppendLiteral(" id='");
1803 id
.Append(nsDependentAtomString(idAtom
));
1807 nsAutoString classes
;
1808 const nsAttrValue
* classAttrValue
=
1809 tmp
->IsElement() ? tmp
->AsElement()->GetClasses() : nullptr;
1810 if (classAttrValue
) {
1811 classes
.AppendLiteral(" class='");
1812 nsAutoString classString
;
1813 classAttrValue
->ToString(classString
);
1814 classString
.ReplaceChar(char16_t('\n'), char16_t(' '));
1815 classes
.Append(classString
);
1816 classes
.Append('\'');
1819 nsAutoCString orphan
;
1820 if (!tmp
->IsInComposedDoc()) {
1821 orphan
.AppendLiteral(" (orphan)");
1824 const char* nsuri
= nsNameSpaceManager::GetNameSpaceDisplayName(nsid
);
1825 SprintfLiteral(name
, "FragmentOrElement %s %s%s%s%s %s", nsuri
,
1826 localName
.get(), NS_ConvertUTF16toUTF8(id
).get(),
1827 NS_ConvertUTF16toUTF8(classes
).get(), orphan
.get(),
1829 cb
.DescribeRefCountedNode(tmp
->mRefCnt
.get(), name
);
1831 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(FragmentOrElement
, tmp
->mRefCnt
.get())
1834 if (!nsIContent::Traverse(tmp
, cb
)) {
1835 return NS_SUCCESS_INTERRUPTED_TRAVERSE
;
1838 if (tmp
->HasProperties()) {
1839 if (tmp
->IsElement()) {
1840 Element
* elem
= tmp
->AsElement();
1841 IntersectionObserverList
* observers
=
1842 static_cast<IntersectionObserverList
*>(
1843 elem
->GetProperty(nsGkAtoms::intersectionobserverlist
));
1845 for (DOMIntersectionObserver
* observer
: observers
->Keys()) {
1846 cb
.NoteXPCOMChild(observer
);
1850 if (tmp
->IsHTMLElement() || tmp
->IsSVGElement()) {
1851 nsStaticAtom
* const* props
=
1852 Element::HTMLSVGPropertiesToTraverseAndUnlink();
1853 for (uint32_t i
= 0; props
[i
]; ++i
) {
1854 nsISupports
* property
=
1855 static_cast<nsISupports
*>(tmp
->GetProperty(props
[i
]));
1856 cb
.NoteXPCOMChild(property
);
1860 if (tmp
->IsElement()) {
1861 Element
* element
= tmp
->AsElement();
1862 // Traverse attribute names.
1864 uint32_t attrs
= element
->GetAttrCount();
1865 for (i
= 0; i
< attrs
; i
++) {
1866 const nsAttrName
* name
= element
->GetUnsafeAttrNameAt(i
);
1867 if (!name
->IsAtom()) {
1868 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mAttrs[i]->NodeInfo()");
1869 cb
.NoteNativeChild(name
->NodeInfo(),
1870 NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo
));
1874 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1876 NS_INTERFACE_MAP_BEGIN(FragmentOrElement
)
1877 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(FragmentOrElement
)
1878 NS_INTERFACE_MAP_END_INHERITING(nsIContent
)
1880 //----------------------------------------------------------------------
1882 const nsTextFragment
* FragmentOrElement::GetText() { return nullptr; }
1884 uint32_t FragmentOrElement::TextLength() const {
1885 // We can remove this assertion if it turns out to be useful to be able
1886 // to depend on this returning 0
1887 MOZ_ASSERT_UNREACHABLE("called FragmentOrElement::TextLength");
1892 bool FragmentOrElement::TextIsOnlyWhitespace() { return false; }
1894 bool FragmentOrElement::ThreadSafeTextIsOnlyWhitespace() const { return false; }
1896 static inline bool IsVoidTag(nsAtom
* aTag
) {
1897 static const nsAtom
* voidElements
[] = {
1898 nsGkAtoms::area
, nsGkAtoms::base
, nsGkAtoms::basefont
,
1899 nsGkAtoms::bgsound
, nsGkAtoms::br
, nsGkAtoms::col
,
1900 nsGkAtoms::embed
, nsGkAtoms::frame
, nsGkAtoms::hr
,
1901 nsGkAtoms::img
, nsGkAtoms::input
, nsGkAtoms::keygen
,
1902 nsGkAtoms::link
, nsGkAtoms::meta
, nsGkAtoms::param
,
1903 nsGkAtoms::source
, nsGkAtoms::track
, nsGkAtoms::wbr
};
1905 static mozilla::BitBloomFilter
<12, nsAtom
> sFilter
;
1906 static bool sInitialized
= false;
1907 if (!sInitialized
) {
1908 sInitialized
= true;
1909 for (uint32_t i
= 0; i
< ArrayLength(voidElements
); ++i
) {
1910 sFilter
.add(voidElements
[i
]);
1914 if (sFilter
.mightContain(aTag
)) {
1915 for (uint32_t i
= 0; i
< ArrayLength(voidElements
); ++i
) {
1916 if (aTag
== voidElements
[i
]) {
1925 bool FragmentOrElement::IsHTMLVoid(nsAtom
* aLocalName
) {
1926 return aLocalName
&& IsVoidTag(aLocalName
);
1929 void FragmentOrElement::GetMarkup(bool aIncludeSelf
, nsAString
& aMarkup
) {
1932 Document
* doc
= OwnerDoc();
1933 if (IsInHTMLDocument()) {
1934 nsContentUtils::SerializeNodeToMarkup(this, !aIncludeSelf
, aMarkup
);
1938 nsAutoString contentType
;
1939 doc
->GetContentType(contentType
);
1940 bool tryToCacheEncoder
= !aIncludeSelf
;
1942 nsCOMPtr
<nsIDocumentEncoder
> docEncoder
= doc
->GetCachedEncoder();
1944 docEncoder
= do_createDocumentEncoder(
1945 PromiseFlatCString(NS_ConvertUTF16toUTF8(contentType
)).get());
1948 // This could be some type for which we create a synthetic document. Try
1950 contentType
.AssignLiteral("application/xml");
1951 docEncoder
= do_createDocumentEncoder("application/xml");
1952 // Don't try to cache the encoder since it would point to a different
1953 // contentType once it has been reinitialized.
1954 tryToCacheEncoder
= false;
1957 NS_ENSURE_TRUE_VOID(docEncoder
);
1959 uint32_t flags
= nsIDocumentEncoder::OutputEncodeBasicEntities
|
1960 // Output DOM-standard newlines
1961 nsIDocumentEncoder::OutputLFLineBreak
|
1962 // Don't do linebreaking that's not present in
1964 nsIDocumentEncoder::OutputRaw
|
1965 // Only check for mozdirty when necessary (bug 599983)
1966 nsIDocumentEncoder::OutputIgnoreMozDirty
;
1969 nsCOMPtr
<Element
> elem
= do_QueryInterface(this);
1970 TextEditor
* textEditor
= elem
? elem
->GetTextEditorInternal() : nullptr;
1971 if (textEditor
&& textEditor
->OutputsMozDirty()) {
1972 flags
&= ~nsIDocumentEncoder::OutputIgnoreMozDirty
;
1976 DebugOnly
<nsresult
> rv
= docEncoder
->NativeInit(doc
, contentType
, flags
);
1977 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1980 docEncoder
->SetNode(this);
1982 docEncoder
->SetContainerNode(this);
1984 rv
= docEncoder
->EncodeToString(aMarkup
);
1985 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1986 if (tryToCacheEncoder
) {
1987 doc
->SetCachedEncoder(docEncoder
.forget());
1991 static bool ContainsMarkup(const nsAString
& aStr
) {
1992 // Note: we can't use FindCharInSet because null is one of the characters we
1993 // want to search for.
1994 const char16_t
* start
= aStr
.BeginReading();
1995 const char16_t
* end
= aStr
.EndReading();
1997 while (start
!= end
) {
1998 char16_t c
= *start
;
1999 if (c
== char16_t('<') || c
== char16_t('&') || c
== char16_t('\r') ||
2000 c
== char16_t('\0')) {
2009 void FragmentOrElement::SetInnerHTMLInternal(const nsAString
& aInnerHTML
,
2010 ErrorResult
& aError
) {
2011 // Keep "this" alive should be guaranteed by the caller, and also the content
2012 // of a template element (if this is one) should never been released by from
2013 // this during this call. Therefore, using raw pointer here is safe.
2014 FragmentOrElement
* target
= this;
2015 // Handle template case.
2016 if (target
->IsTemplateElement()) {
2017 DocumentFragment
* frag
=
2018 static_cast<HTMLTemplateElement
*>(target
)->Content();
2022 // Fast-path for strings with no markup. Limit this to short strings, to
2023 // avoid ContainsMarkup taking too long. The choice for 100 is based on
2026 // Don't do this for elements with a weird parser insertion mode, for
2027 // instance setting innerHTML = "" on a <html> element should add the
2028 // optional <head> and <body> elements.
2029 if (!target
->HasWeirdParserInsertionMode() && aInnerHTML
.Length() < 100 &&
2030 !ContainsMarkup(aInnerHTML
)) {
2031 aError
= nsContentUtils::SetNodeTextContent(target
, aInnerHTML
, false);
2035 // mozAutoSubtreeModified keeps the owner document alive. Therefore, using a
2036 // raw pointer here is safe.
2037 Document
* const doc
= target
->OwnerDoc();
2039 // Batch possible DOMSubtreeModified events.
2040 mozAutoSubtreeModified
subtree(doc
, nullptr);
2042 target
->FireNodeRemovedForChildren();
2044 // Needed when innerHTML is used in combination with contenteditable
2045 mozAutoDocUpdate
updateBatch(doc
, true);
2047 // Remove childnodes.
2048 nsAutoMutationBatch
mb(target
, true, false);
2049 while (target
->HasChildren()) {
2050 target
->RemoveChildNode(target
->GetFirstChild(), true);
2054 nsAutoScriptLoaderDisabler
sld(doc
);
2056 FragmentOrElement
* parseContext
= this;
2057 if (ShadowRoot
* shadowRoot
= ShadowRoot::FromNode(this)) {
2058 // Fix up the context to be the host of the ShadowRoot. See
2059 // https://w3c.github.io/DOM-Parsing/#dom-innerhtml-innerhtml setter step 1.
2060 parseContext
= shadowRoot
->GetHost();
2063 if (doc
->IsHTMLDocument()) {
2064 nsAtom
* contextLocalName
= parseContext
->NodeInfo()->NameAtom();
2065 int32_t contextNameSpaceID
= parseContext
->GetNameSpaceID();
2067 int32_t oldChildCount
= target
->GetChildCount();
2068 aError
= nsContentUtils::ParseFragmentHTML(
2069 aInnerHTML
, target
, contextLocalName
, contextNameSpaceID
,
2070 doc
->GetCompatibilityMode() == eCompatibility_NavQuirks
, true);
2072 // HTML5 parser has notified, but not fired mutation events.
2073 nsContentUtils::FireMutationEventsForDirectParsing(doc
, target
,
2076 RefPtr
<DocumentFragment
> df
= nsContentUtils::CreateContextualFragment(
2077 parseContext
, aInnerHTML
, true, aError
);
2078 if (!aError
.Failed()) {
2079 // Suppress assertion about node removal mutation events that can't have
2080 // listeners anyway, because no one has had the chance to register
2081 // mutation listeners on the fragment that comes from the parser.
2082 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker
;
2084 target
->AppendChild(*df
, aError
);
2090 void FragmentOrElement::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
,
2091 size_t* aNodeSize
) const {
2092 nsIContent::AddSizeOfExcludingThis(aSizes
, aNodeSize
);
2094 nsDOMSlots
* slots
= GetExistingDOMSlots();
2096 *aNodeSize
+= slots
->SizeOfIncludingThis(aSizes
.mState
.mMallocSizeOf
);