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/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"
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"
48 #include "nsIAnonymousContentCreator.h"
49 #include "nsPresContext.h"
51 #include "nsDOMCSSAttrDeclaration.h"
52 #include "nsNameSpaceManager.h"
53 #include "nsContentList.h"
54 #include "nsDOMTokenList.h"
56 #include "nsXULElement.h"
57 #include "mozilla/InternalMutationEvent.h"
58 #include "mozilla/MouseEvents.h"
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"
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"
102 # include "nsAccessibilityService.h"
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
)
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
,
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
);
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();
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",
181 if (GetParent()->GetShadowRoot()->IsClosed()) {
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).
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
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();
208 return IMEState(IMEEnabled::Disabled
);
210 nsPresContext
* pc
= doc
->GetPresContext();
212 return IMEState(IMEEnabled::Disabled
);
214 HTMLEditor
* htmlEditor
= nsContentUtils::GetHTMLEditor(pc
);
216 return IMEState(IMEEnabled::Disabled
);
219 htmlEditor
->GetPreferredIMEState(&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.
234 Document
* doc
= GetComposedDoc();
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");
264 if (aNamespacePrefix
.EqualsLiteral("xmlns")) {
265 // Special-case for xmlns prefix
266 aNamespaceURI
.AssignLiteral("http://www.w3.org/2000/xmlns/");
271 if (!aNamespacePrefix
.IsEmpty()) {
272 name
= NS_Atomize(aNamespacePrefix
);
273 NS_ENSURE_TRUE(name
, NS_ERROR_OUT_OF_MEMORY
);
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
)) {
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()) {
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
);
303 MOZ_ASSERT(attr
->Type() == nsAttrValue::eAtom
);
304 MOZ_ASSERT(attr
->GetAtomValue());
305 return attr
->GetAtomValue();
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();
345 return MakeAndAddRef
<URLExtraData
>(doc
->GetDocBaseURI(), referrerInfo
,
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()
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
)
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
) {
410 return mNode
->GetChildAt_Deprecated(aIndex
);
416 int32_t nsAttrChildContentList::IndexOf(nsIContent
* aContent
) {
418 return mNode
->ComputeIndexOf_Deprecated(aContent
);
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
)) {
434 // Try to avoid the cache for some common cases, see bug 1917511.
436 return mNode
->GetFirstChild();
438 uint32_t childCount
= mNode
->GetChildCount();
439 if (aIndex
>= childCount
) {
442 if (aIndex
+ 1 == childCount
) {
443 return mNode
->GetLastChild();
446 MOZ_ASSERT(mIsCacheValid
);
448 return mCachedChildArray
.SafeElementAt(aIndex
, nullptr);
451 int32_t nsParentNodeChildContentList::IndexOf(nsIContent
* aContent
) {
453 return mCachedChildArray
.IndexOf(aContent
);
456 void nsParentNodeChildContentList::ValidateCache() {
457 MOZ_ASSERT(!mIsCacheValid
);
458 MOZ_ASSERT(mCachedChildArray
.IsEmpty());
460 if (MOZ_UNLIKELY(!mNode
)) {
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
)
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
);
509 //----------------------------------------------------------------------
511 static const size_t MaxDOMSlotSizeAllowed
=
512 #ifdef HAVE_64BIT_BUILD
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
550 FragmentOrElement::nsDOMSlots::nsDOMSlots() { MOZ_COUNT_CTOR(nsDOMSlots
); }
552 FragmentOrElement::nsDOMSlots::~nsDOMSlots() {
553 MOZ_COUNT_DTOR(nsDOMSlots
);
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
);
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();
594 if (OwnsExtendedSlots()) {
595 n
+= aMallocSizeOf(extendedSlots
);
598 n
+= extendedSlots
->SizeOfExcludingThis(aMallocSizeOf
);
602 n
+= mAttributeMap
->SizeOfIncludingThis(aMallocSizeOf
);
606 n
+= mChildrenList
->SizeOfIncludingThis(aMallocSizeOf
);
609 // Measurement of the following members may be added later if DMD finds it is
611 // - Superclass members (nsINode::nsSlots)
615 // The following member are not measured:
616 // - mControllers: because it is non-owning
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;
639 mAnimations
= nullptr;
640 aContent
.ClearMayHaveAnimations();
642 mExplicitlySetAttrElementMap
.Clear();
643 mAttrElementsMap
.Clear();
644 mRadioGroupContainer
= 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
);
680 mAnimations
->Traverse(aCb
);
682 if (mRadioGroupContainer
) {
683 RadioGroupContainer::Traverse(mRadioGroupContainer
.get(), aCb
);
687 size_t FragmentOrElement::nsExtendedDOMSlots::SizeOfExcludingThis(
688 MallocSizeOf aMallocSizeOf
) const {
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
695 if (mSMILOverrideStyle
) {
696 n
+= aMallocSizeOf(mSMILOverrideStyle
);
699 // We don't really own mSMILOverrideStyleDeclaration. mSMILOverrideStyle owns
702 // We don't seem to have memory reporting for nsXULControllers. At least
703 // report the memory it's using directly.
705 n
+= aMallocSizeOf(mControllers
);
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
);
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");
738 static nsINode
* FindChromeAccessOnlySubtreeOwnerForEvents(nsINode
* aNode
) {
739 if (!aNode
->ChromeOnlyAccessForEvents()) {
742 return const_cast<nsIContent
*>(aNode
->GetChromeOnlyAccessSubtreeRootParent());
745 nsINode
* FindChromeAccessOnlySubtreeOwnerForEvents(EventTarget
* aTarget
) {
746 nsINode
* node
= nsINode::FromEventTargetOrNull(aTarget
);
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()) ||
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
784 if (isAnonForEvents
|| aVisitor
.mRelatedTargetIsInAnon
||
785 (aVisitor
.mEvent
->mOriginalTarget
== this &&
786 (aVisitor
.mRelatedTargetIsInAnon
=
787 relatedTarget
->ChromeOnlyAccessForEvents()))) {
788 nsINode
* anonOwner
= FindChromeAccessOnlySubtreeOwnerForEvents(this);
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()) {
800 FindChromeAccessOnlySubtreeOwnerForEvents(anonOwnerRelated
);
802 if (anonOwner
== anonOwnerRelated
) {
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
);
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"
818 NS_ConvertUTF16toUTF8(ot
).get(),
819 NS_ConvertUTF16toUTF8(ct
).get(),
821 ? "(is native anonymous)"
822 : (ChromeOnlyAccess() ? "(is in native anonymous subtree)"
824 NS_ConvertUTF16toUTF8(rt
).get(),
825 relatedTarget
->ChromeOnlyAccess()
826 ? "(is in native anonymous subtree)"
829 relatedTarget
->FindFirstNonChromeOnlyAccessContent() ==
830 originalTarget
->FindFirstNonChromeOnlyAccessContent())
832 : "Wrong event propagation!?!\n");
834 aVisitor
.SetParentTarget(nullptr, false);
835 // Event should not propagate to non-anon content.
836 aVisitor
.mCanHandle
= isAnonForEvents
;
845 // Event parent is the assigned slot, if node is assigned, or node's parent
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
852 if (isAnonForEvents
) {
854 // If a DOM event is explicitly dispatched using node.dispatchEvent(), then
855 // all the events are allowed even in the native anonymous content..
857 nsIContent::FromEventTargetOrNull(aVisitor
.mEvent
->mOriginalTarget
);
858 NS_ASSERTION(!t
|| !t
->ChromeOnlyAccessForEvents() ||
859 aVisitor
.mEvent
->mClass
!= eMutationEventClass
||
861 "Mutation event dispatched in native anonymous content!?!");
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);
869 aVisitor
.mEventTargetAtParent
= parent
;
870 } else if (parent
&& aVisitor
.mOriginalTargetIsInAnon
) {
871 nsIContent
* content
=
872 nsIContent::FromEventTargetOrNull(aVisitor
.mEvent
->mTarget
);
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);
883 aVisitor
.SetParentTarget(parent
, false);
885 ShadowRoot
* root
= slot
->GetContainingShadow();
886 if (root
&& root
->IsClosed()) {
887 aVisitor
.mParentIsSlotInClosedTree
= true;
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
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 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;
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
) {
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
;
948 } else if (nsINode
* relatedTargetAsNode
=
949 FindChromeAccessOnlySubtreeOwnerForEvents(
950 aVisitor
.mEvent
->mOriginalRelatedTarget
)) {
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
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
) {
975 // "Otherwise, if parent and relatedTarget are identical, then set
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
;
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.
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
);
1008 EventTarget
* retargeted
=
1009 nsContentUtils::Retarget(targetAsNode
, this);
1011 touchTarget
= retargeted
;
1014 aVisitor
.mRetargetedTouchTargets
->AppendElement(touchTarget
);
1015 touch
->mTarget
= touchTarget
;
1017 MOZ_ASSERT(aVisitor
.mRetargetedTouchTargets
->Length() ==
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
)) {
1035 nsIFrame
* frame
= descendant
->GetPrimaryFrame();
1036 if (frame
&& frame
->IsFocusable(aFlags
)) {
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.
1054 auto IsFocusable
= [&](Element
* aElement
) -> Focusable
{
1055 nsIFrame
* frame
= aElement
->GetPrimaryFrame();
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
);
1072 const bool autofocus
= el
->GetBoolAttr(nsGkAtoms::autofocus
);
1075 if (IsFocusable(el
)) {
1076 // Found an autofocus candidate.
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
;
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
1101 if (auto* shadow
= el
->GetShadowRoot()) {
1102 if (shadow
->DelegatesFocus()) {
1103 if (Element
* delegatedFocus
= shadow
->GetFocusDelegate(aFlags
)) {
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
1125 void nsIContent::SetAssignedSlot(HTMLSlotElement
* aSlot
) {
1126 MOZ_ASSERT(aSlot
|| GetExistingExtendedContentSlots());
1127 ExtendedContentSlots()->mAssignedSlot
= aSlot
;
1131 void nsIContent::Dump() { List(); }
1134 void FragmentOrElement::GetTextContentInternal(nsAString
& aTextContent
,
1135 OOMReporter
& aError
) {
1136 if (!nsContentUtils::GetNodeTextContent(this, true, aTextContent
, fallible
)) {
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
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...
1170 AsElement()->ClearServoData();
1174 uint32_t oldChildCount
= GetChildCount();
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
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
{
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
) {
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
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();
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;
1272 RefPtr
<ContentUnbinder
> next
;
1274 sContentUnbinder
= next
;
1275 next
->mLast
= mLast
;
1277 NS_DispatchToCurrentThreadQueue(next
.forget(),
1278 EventQueuePriority::Idle
);
1284 static void UnbindAll() {
1285 RefPtr
<ContentUnbinder
> ub
= sContentUnbinder
;
1286 sContentUnbinder
= nullptr;
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
);
1309 AutoTArray
<nsCOMPtr
<nsIContent
>, SUBTREE_UNBINDINGS_PER_RUNNABLE
>
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
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
);
1344 The subtree root will end up to a ContentUnbinder, and that will
1345 unbind the child nodes.
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
);
1358 JS::ExposeObjectToActiveJS(o
);
1361 EventListenerManager
* elm
= aNode
->GetExistingListenerManager();
1367 nsINode
* FindOptimizableSubtreeRoot(nsINode
* aNode
) {
1369 while ((p
= aNode
->GetParentNode())) {
1370 if (aNode
->UnoptimizableCCNode()) {
1376 if (aNode
->UnoptimizableCCNode()) {
1382 StaticAutoPtr
<nsTHashSet
<nsINode
*>> gCCBlackMarkedNodes
;
1384 static void ClearBlackMarkedNodes() {
1385 if (!gCCBlackMarkedNodes
) {
1388 for (nsINode
* n
: *gCCBlackMarkedNodes
) {
1389 n
->SetCCMarkedRoot(false);
1390 n
->SetInCCBlackTree(false);
1392 gCCBlackMarkedNodes
= nullptr;
1396 void FragmentOrElement::RemoveBlackMarkedNode(nsINode
* aNode
) {
1397 if (!gCCBlackMarkedNodes
) {
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() &&
1417 bool FragmentOrElement::CanSkipInCC(nsINode
* aNode
) {
1418 // Don't try to optimize anything during shutdown.
1419 if (nsCCUncollectableMarker::sGeneration
== 0) {
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()) {
1434 nsINode
* root
= currentDoc
? static_cast<nsINode
*>(currentDoc
)
1435 : FindOptimizableSubtreeRoot(aNode
);
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.
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
) {
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
);
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
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() {
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()) {
1552 JSObject
* o
= GetJSObjectChild(aContent
);
1553 if (o
&& JS::ObjectIsMarkedGray(o
)) {
1557 if (aContent
->HasListenerManager()) {
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) {
1585 bool unoptimizable
= aNode
->UnoptimizableCCNode();
1586 Document
* currentDoc
= aNode
->GetComposedDoc();
1587 if (currentDoc
&& IsCertainlyAliveNode(aNode
, currentDoc
) &&
1588 (!unoptimizable
|| NodeHasActiveFrame(currentDoc
, aNode
))) {
1589 MarkNodeChildren(aNode
);
1593 if (unoptimizable
) {
1597 nsINode
* root
= currentDoc
? static_cast<nsINode
*>(currentDoc
)
1598 : FindOptimizableSubtreeRoot(aNode
);
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()) {
1609 // nodesToClear contains nodes which are either purple or
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;
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.
1640 // No need to put stuff to the nodesToClear array, if we can clear it
1642 if (node
->IsPurple() && (node
!= aNode
|| aRemovingAllowed
)) {
1643 node
->RemovePurple();
1645 MarkNodeChildren(node
);
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);
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()) {
1671 if (!gPurpleRoots
) {
1672 gPurpleRoots
= new AutoTArray
<nsINode
*, 1020>();
1674 gPurpleRoots
->AppendElement(root
);
1678 if (!foundLiveWrapper
) {
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()) {
1704 bool FragmentOrElement::CanSkipThis(nsINode
* aNode
) {
1705 if (nsCCUncollectableMarker::sGeneration
== 0) {
1708 if (aNode
->HasKnownLiveWrapper()) {
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())) {
1738 uint32_t nsid
= tmp
->GetNameSpaceID();
1739 nsAtomCString
localName(tmp
->NodeInfo()->NameAtom());
1741 if (tmp
->OwnerDoc()->GetDocumentURI()) {
1742 uri
= tmp
->OwnerDoc()->GetDocumentURI()->GetSpecOrDefault();
1746 nsAtom
* idAtom
= tmp
->GetID();
1748 id
.AppendLiteral(" id='");
1749 id
.Append(nsDependentAtomString(idAtom
));
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(),
1775 cb
.DescribeRefCountedNode(tmp
->mRefCnt
.get(), name
);
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.
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");
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
]) {
1848 bool FragmentOrElement::IsHTMLVoid(nsAtom
* aLocalName
) {
1849 return aLocalName
&& IsVoidTag(aLocalName
);
1852 void FragmentOrElement::GetMarkup(bool aIncludeSelf
, nsAString
& aMarkup
) {
1855 Document
* doc
= OwnerDoc();
1856 if (IsInHTMLDocument()) {
1857 nsContentUtils::SerializeNodeToMarkup(this, !aIncludeSelf
, aMarkup
, false,
1862 nsAutoString contentType
;
1863 doc
->GetContentType(contentType
);
1864 bool tryToCacheEncoder
= !aIncludeSelf
;
1866 nsCOMPtr
<nsIDocumentEncoder
> docEncoder
= doc
->GetCachedEncoder();
1868 docEncoder
= do_createDocumentEncoder(
1869 PromiseFlatCString(NS_ConvertUTF16toUTF8(contentType
)).get());
1872 // This could be some type for which we create a synthetic document. Try
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
1888 nsIDocumentEncoder::OutputRaw
|
1889 // Only check for mozdirty when necessary (bug 599983)
1890 nsIDocumentEncoder::OutputIgnoreMozDirty
;
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
));
1904 docEncoder
->SetNode(this);
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')) {
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();
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
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);
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);
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());
1999 // HTML5 parser has notified, but not fired mutation events.
2000 nsContentUtils::FireMutationEventsForDirectParsing(doc
, target
,
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
);
2017 void FragmentOrElement::AddSizeOfExcludingThis(nsWindowSizes
& aSizes
,
2018 size_t* aNodeSize
) const {
2019 nsIContent::AddSizeOfExcludingThis(aSizes
, aNodeSize
);
2021 nsDOMSlots
* slots
= GetExistingDOMSlots();
2023 *aNodeSize
+= slots
->SizeOfIncludingThis(aSizes
.mState
.mMallocSizeOf
);