Bug 1892041 - Part 1: Update test262 features. r=spidermonkey-reviewers,dminor
[gecko.git] / dom / base / FragmentOrElement.cpp
blob87fd81bfa32555ccaacd94830ee5576f19ac56cb
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * Base class for all element classes and DocumentFragment.
9 */
11 #include "mozilla/ArrayUtils.h"
12 #include "mozilla/Likely.h"
13 #include "mozilla/MemoryReporting.h"
14 #include "mozilla/StaticPtr.h"
16 #include "mozilla/dom/FragmentOrElement.h"
17 #include "DOMIntersectionObserver.h"
18 #include "mozilla/AsyncEventDispatcher.h"
19 #include "mozilla/EffectSet.h"
20 #include "mozilla/EventDispatcher.h"
21 #include "mozilla/EventListenerManager.h"
22 #include "mozilla/ElementAnimationData.h"
23 #include "mozilla/DeclarationBlock.h"
24 #include "mozilla/HTMLEditor.h"
25 #include "mozilla/mozInlineSpellChecker.h"
26 #include "mozilla/PresShell.h"
27 #include "mozilla/RestyleManager.h"
28 #include "mozilla/TextEditor.h"
29 #include "mozilla/TouchEvents.h"
30 #include "mozilla/URLExtraData.h"
31 #include "mozilla/dom/Attr.h"
32 #include "mozilla/dom/RadioGroupContainer.h"
33 #include "mozilla/dom/UnbindContext.h"
34 #include "nsDOMAttributeMap.h"
35 #include "nsAtom.h"
36 #include "mozilla/dom/NodeInfo.h"
37 #include "mozilla/dom/Event.h"
38 #include "mozilla/dom/ScriptLoader.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 "nsNetUtil.h"
46 #include "nsIFrame.h"
47 #include "nsIAnonymousContentCreator.h"
48 #include "nsPresContext.h"
49 #include "nsString.h"
50 #include "nsDOMCSSAttrDeclaration.h"
51 #include "nsNameSpaceManager.h"
52 #include "nsContentList.h"
53 #include "nsDOMTokenList.h"
54 #include "nsError.h"
55 #include "nsXULElement.h"
56 #include "mozilla/InternalMutationEvent.h"
57 #include "mozilla/MouseEvents.h"
58 #ifdef DEBUG
59 # include "nsRange.h"
60 #endif
62 #include "nsFrameLoader.h"
63 #include "nsPIDOMWindow.h"
64 #include "nsLayoutUtils.h"
65 #include "nsGkAtoms.h"
66 #include "nsContentUtils.h"
67 #include "nsTextFragment.h"
68 #include "nsWindowSizes.h"
70 #include "nsIWidget.h"
72 #include "nsNodeInfoManager.h"
73 #include "nsGenericHTMLElement.h"
74 #include "nsContentCreatorFunctions.h"
75 #include "nsView.h"
76 #include "nsIScrollableFrame.h"
77 #include "ChildIterator.h"
78 #include "mozilla/dom/NodeListBinding.h"
79 #include "mozilla/dom/MutationObservers.h"
80 #include "nsCCUncollectableMarker.h"
82 #include "mozAutoDocUpdate.h"
84 #include "mozilla/Sprintf.h"
85 #include "nsDOMMutationObserver.h"
86 #include "nsWrapperCacheInlines.h"
87 #include "nsCycleCollector.h"
88 #include "xpcpublic.h"
90 #include "mozilla/dom/ShadowRoot.h"
91 #include "mozilla/dom/HTMLSlotElement.h"
92 #include "mozilla/dom/HTMLTemplateElement.h"
93 #include "mozilla/dom/SVGUseElement.h"
95 #include "nsIContentInlines.h"
96 #include "nsChildContentList.h"
97 #include "mozilla/BloomFilter.h"
99 #include "NodeUbiReporting.h"
101 #ifdef ACCESSIBILITY
102 # include "nsAccessibilityService.h"
103 #endif
105 using namespace mozilla;
106 using namespace mozilla::dom;
108 int32_t nsIContent::sTabFocusModel = eTabFocus_any;
109 bool nsIContent::sTabFocusModelAppliesToXUL = false;
110 uint64_t nsMutationGuard::sGeneration = 0;
112 NS_IMPL_CYCLE_COLLECTION_CLASS(nsIContent)
114 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsIContent)
115 MOZ_ASSERT_UNREACHABLE("Our subclasses don't call us");
116 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
118 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsIContent)
119 MOZ_ASSERT_UNREACHABLE("Our subclasses don't call us");
120 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
122 NS_INTERFACE_MAP_BEGIN(nsIContent)
123 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
124 // Don't bother to QI to cycle collection, because our CC impl is
125 // not doing anything anyway.
126 NS_INTERFACE_MAP_ENTRY(nsIContent)
127 NS_INTERFACE_MAP_ENTRY(nsINode)
128 NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
129 NS_INTERFACE_MAP_ENTRY_TEAROFF(nsISupportsWeakReference,
130 new nsNodeSupportsWeakRefTearoff(this))
131 // DOM bindings depend on the identity pointer being the
132 // same as nsINode (which nsIContent inherits).
133 NS_INTERFACE_MAP_ENTRY(nsISupports)
134 NS_INTERFACE_MAP_END
136 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsIContent)
138 NS_IMPL_DOMARENA_DESTROY(nsIContent)
140 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE_AND_DESTROY(nsIContent,
141 LastRelease(),
142 Destroy())
144 nsIContent* nsIContent::FindFirstNonChromeOnlyAccessContent() const {
145 // This handles also nested native anonymous content.
146 for (const nsIContent* content = this; content;
147 content = content->GetChromeOnlyAccessSubtreeRootParent()) {
148 if (!content->ChromeOnlyAccess()) {
149 // Oops, this function signature allows casting const to
150 // non-const. (Then again, so does GetFirstChild()->GetParent().)
151 return const_cast<nsIContent*>(content);
154 return nullptr;
157 void nsIContent::UnbindFromTree() {
158 UnbindContext context(*this);
159 UnbindFromTree(context);
162 // https://dom.spec.whatwg.org/#dom-slotable-assignedslot
163 HTMLSlotElement* nsIContent::GetAssignedSlotByMode() const {
165 * Get slotable's assigned slot for the result of
166 * find a slot with open flag UNSET [1].
168 * [1] https://dom.spec.whatwg.org/#assign-a-slot
170 HTMLSlotElement* slot = GetAssignedSlot();
171 if (!slot) {
172 return nullptr;
175 MOZ_ASSERT(GetParent());
176 MOZ_ASSERT(GetParent()->GetShadowRoot());
179 * Additional check for open flag SET:
180 * If slotable’s parent’s shadow root's mode is not "open",
181 * then return null.
183 if (GetParent()->GetShadowRoot()->IsClosed()) {
184 return nullptr;
187 return slot;
190 nsIContent::IMEState nsIContent::GetDesiredIMEState() {
191 if (!IsEditable()) {
192 // Check for the special case where we're dealing with elements which don't
193 // have the editable flag set, but are readwrite (such as text controls).
194 if (!IsElement() ||
195 !AsElement()->State().HasState(ElementState::READWRITE)) {
196 return IMEState(IMEEnabled::Disabled);
199 // NOTE: The content for independent editors (e.g., input[type=text],
200 // textarea) must override this method, so, we don't need to worry about
201 // that here.
202 nsIContent* editableAncestor = GetEditingHost();
204 // This is in another editable content, use the result of it.
205 if (editableAncestor && editableAncestor != this) {
206 return editableAncestor->GetDesiredIMEState();
208 Document* doc = GetComposedDoc();
209 if (!doc) {
210 return IMEState(IMEEnabled::Disabled);
212 nsPresContext* pc = doc->GetPresContext();
213 if (!pc) {
214 return IMEState(IMEEnabled::Disabled);
216 HTMLEditor* htmlEditor = nsContentUtils::GetHTMLEditor(pc);
217 if (!htmlEditor) {
218 return IMEState(IMEEnabled::Disabled);
220 IMEState state;
221 htmlEditor->GetPreferredIMEState(&state);
222 return state;
225 bool nsIContent::HasIndependentSelection() const {
226 nsIFrame* frame = GetPrimaryFrame();
227 return (frame && frame->HasAnyStateBits(NS_FRAME_INDEPENDENT_SELECTION));
230 dom::Element* nsIContent::GetEditingHost() {
231 // If this isn't editable, return nullptr.
232 if (!IsEditable()) {
233 return nullptr;
236 Document* doc = GetComposedDoc();
237 if (!doc) {
238 return nullptr;
241 // If this is in designMode, we should return <body>
242 if (IsInDesignMode() && !IsInShadowTree()) {
243 return doc->GetBodyElement();
246 dom::Element* editableParentElement = nullptr;
247 for (dom::Element* parent = GetParentElement();
248 parent && parent->HasFlag(NODE_IS_EDITABLE);
249 parent = editableParentElement->GetParentElement()) {
250 editableParentElement = parent;
252 return editableParentElement ? editableParentElement
253 : dom::Element::FromNode(this);
256 nsresult nsIContent::LookupNamespaceURIInternal(
257 const nsAString& aNamespacePrefix, nsAString& aNamespaceURI) const {
258 if (aNamespacePrefix.EqualsLiteral("xml")) {
259 // Special-case for xml prefix
260 aNamespaceURI.AssignLiteral("http://www.w3.org/XML/1998/namespace");
261 return NS_OK;
264 if (aNamespacePrefix.EqualsLiteral("xmlns")) {
265 // Special-case for xmlns prefix
266 aNamespaceURI.AssignLiteral("http://www.w3.org/2000/xmlns/");
267 return NS_OK;
270 RefPtr<nsAtom> name;
271 if (!aNamespacePrefix.IsEmpty()) {
272 name = NS_Atomize(aNamespacePrefix);
273 NS_ENSURE_TRUE(name, NS_ERROR_OUT_OF_MEMORY);
274 } else {
275 name = nsGkAtoms::xmlns;
277 // Trace up the content parent chain looking for the namespace
278 // declaration that declares aNamespacePrefix.
279 for (Element* element = GetAsElementOrParentElement(); element;
280 element = element->GetParentElement()) {
281 if (element->GetAttr(kNameSpaceID_XMLNS, name, aNamespaceURI)) {
282 return NS_OK;
285 return NS_ERROR_FAILURE;
288 nsAtom* nsIContent::GetLang() const {
289 for (const Element* element = GetAsElementOrParentElement(); element;
290 element = element->GetParentElement()) {
291 if (!element->GetAttrCount()) {
292 continue;
295 // xml:lang has precedence over lang on HTML elements (see
296 // XHTML1 section C.7).
297 const nsAttrValue* attr =
298 element->GetParsedAttr(nsGkAtoms::lang, kNameSpaceID_XML);
299 if (!attr && element->SupportsLangAttr()) {
300 attr = element->GetParsedAttr(nsGkAtoms::lang);
302 if (attr) {
303 MOZ_ASSERT(attr->Type() == nsAttrValue::eAtom);
304 MOZ_ASSERT(attr->GetAtomValue());
305 return attr->GetAtomValue();
309 return nullptr;
312 nsIURI* nsIContent::GetBaseURI(bool aTryUseXHRDocBaseURI) const {
313 if (SVGUseElement* use = GetContainingSVGUseShadowHost()) {
314 if (URLExtraData* data = use->GetContentURLData()) {
315 return data->BaseURI();
319 return OwnerDoc()->GetBaseURI(aTryUseXHRDocBaseURI);
322 nsIURI* nsIContent::GetBaseURIForStyleAttr() const {
323 if (SVGUseElement* use = GetContainingSVGUseShadowHost()) {
324 if (URLExtraData* data = use->GetContentURLData()) {
325 return data->BaseURI();
328 // This also ignores the case that SVG inside XBL binding.
329 // But it is probably fine.
330 return OwnerDoc()->GetDocBaseURI();
333 already_AddRefed<URLExtraData> nsIContent::GetURLDataForStyleAttr(
334 nsIPrincipal* aSubjectPrincipal) const {
335 if (SVGUseElement* use = GetContainingSVGUseShadowHost()) {
336 if (URLExtraData* data = use->GetContentURLData()) {
337 return do_AddRef(data);
340 auto* doc = OwnerDoc();
341 if (aSubjectPrincipal && aSubjectPrincipal != NodePrincipal()) {
342 nsCOMPtr<nsIReferrerInfo> referrerInfo =
343 doc->ReferrerInfoForInternalCSSAndSVGResources();
344 // TODO: Cache this?
345 return MakeAndAddRef<URLExtraData>(doc->GetDocBaseURI(), referrerInfo,
346 aSubjectPrincipal);
348 return do_AddRef(doc->DefaultStyleAttrURLData());
351 void nsIContent::ConstructUbiNode(void* storage) {
352 JS::ubi::Concrete<nsIContent>::construct(storage, this);
355 bool nsIContent::InclusiveDescendantMayNeedSpellchecking(HTMLEditor* aEditor) {
356 // Return true if the node may have elements as children, since those or their
357 // descendants may have spellcheck attributes.
358 return HasFlag(NODE_MAY_HAVE_ELEMENT_CHILDREN) ||
359 mozInlineSpellChecker::ShouldSpellCheckNode(aEditor, this);
362 //----------------------------------------------------------------------
364 static inline JSObject* GetJSObjectChild(nsWrapperCache* aCache) {
365 return aCache->PreservingWrapper() ? aCache->GetWrapperPreserveColor()
366 : nullptr;
369 static bool NeedsScriptTraverse(nsINode* aNode) {
370 return aNode->PreservingWrapper() && aNode->GetWrapperPreserveColor() &&
371 !aNode->HasKnownLiveWrapperAndDoesNotNeedTracing(aNode);
374 //----------------------------------------------------------------------
376 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAttrChildContentList)
377 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAttrChildContentList)
379 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsAttrChildContentList, mNode)
381 // If the wrapper is known-live, the list can't be part of a garbage cycle.
382 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsAttrChildContentList)
383 return tmp->HasKnownLiveWrapper();
384 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
386 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsAttrChildContentList)
387 return tmp->HasKnownLiveWrapperAndDoesNotNeedTracing(tmp);
388 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
390 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsAttrChildContentList)
391 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
393 NS_INTERFACE_TABLE_HEAD(nsAttrChildContentList)
394 NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
395 NS_INTERFACE_TABLE(nsAttrChildContentList, nsINodeList)
396 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsAttrChildContentList)
397 NS_INTERFACE_MAP_END
399 JSObject* nsAttrChildContentList::WrapObject(
400 JSContext* cx, JS::Handle<JSObject*> aGivenProto) {
401 return NodeList_Binding::Wrap(cx, this, aGivenProto);
404 uint32_t nsAttrChildContentList::Length() {
405 return mNode ? mNode->GetChildCount() : 0;
408 nsIContent* nsAttrChildContentList::Item(uint32_t aIndex) {
409 if (mNode) {
410 return mNode->GetChildAt_Deprecated(aIndex);
413 return nullptr;
416 int32_t nsAttrChildContentList::IndexOf(nsIContent* aContent) {
417 if (mNode) {
418 return mNode->ComputeIndexOf_Deprecated(aContent);
421 return -1;
424 //----------------------------------------------------------------------
425 uint32_t nsParentNodeChildContentList::Length() {
426 if (!mIsCacheValid && !ValidateCache()) {
427 return 0;
430 MOZ_ASSERT(mIsCacheValid);
432 return mCachedChildArray.Length();
435 nsIContent* nsParentNodeChildContentList::Item(uint32_t aIndex) {
436 if (!mIsCacheValid && !ValidateCache()) {
437 return nullptr;
440 MOZ_ASSERT(mIsCacheValid);
442 return mCachedChildArray.SafeElementAt(aIndex, nullptr);
445 int32_t nsParentNodeChildContentList::IndexOf(nsIContent* aContent) {
446 if (!mIsCacheValid && !ValidateCache()) {
447 return -1;
450 MOZ_ASSERT(mIsCacheValid);
452 return mCachedChildArray.IndexOf(aContent);
455 bool nsParentNodeChildContentList::ValidateCache() {
456 MOZ_ASSERT(!mIsCacheValid);
457 MOZ_ASSERT(mCachedChildArray.IsEmpty());
459 nsINode* parent = GetParentObject();
460 if (!parent) {
461 return false;
464 for (nsIContent* node = parent->GetFirstChild(); node;
465 node = node->GetNextSibling()) {
466 mCachedChildArray.AppendElement(node);
468 mIsCacheValid = true;
470 return true;
473 //----------------------------------------------------------------------
475 nsIHTMLCollection* FragmentOrElement::Children() {
476 nsDOMSlots* slots = DOMSlots();
478 if (!slots->mChildrenList) {
479 slots->mChildrenList =
480 new nsContentList(this, kNameSpaceID_Wildcard, nsGkAtoms::_asterisk,
481 nsGkAtoms::_asterisk, false);
484 return slots->mChildrenList;
487 //----------------------------------------------------------------------
489 NS_IMPL_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff, mNode)
491 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff)
492 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
493 NS_INTERFACE_MAP_END_AGGREGATED(mNode)
495 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNodeSupportsWeakRefTearoff)
496 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNodeSupportsWeakRefTearoff)
498 NS_IMETHODIMP
499 nsNodeSupportsWeakRefTearoff::GetWeakReference(
500 nsIWeakReference** aInstancePtr) {
501 nsINode::nsSlots* slots = mNode->Slots();
502 if (!slots->mWeakReference) {
503 slots->mWeakReference = new nsNodeWeakReference(mNode);
506 NS_ADDREF(*aInstancePtr = slots->mWeakReference);
508 return NS_OK;
511 //----------------------------------------------------------------------
513 static const size_t MaxDOMSlotSizeAllowed =
514 #ifdef HAVE_64BIT_BUILD
515 128;
516 #else
518 #endif
520 static_assert(sizeof(nsINode::nsSlots) <= MaxDOMSlotSizeAllowed,
521 "DOM slots cannot be grown without consideration");
522 static_assert(sizeof(FragmentOrElement::nsDOMSlots) <= MaxDOMSlotSizeAllowed,
523 "DOM slots cannot be grown without consideration");
525 void nsIContent::nsExtendedContentSlots::UnlinkExtendedSlots(nsIContent&) {
526 mContainingShadow = nullptr;
527 mAssignedSlot = nullptr;
530 void nsIContent::nsExtendedContentSlots::TraverseExtendedSlots(
531 nsCycleCollectionTraversalCallback& aCb) {
532 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mContainingShadow");
533 aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mContainingShadow));
535 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mAssignedSlot");
536 aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mAssignedSlot.get()));
539 nsIContent::nsExtendedContentSlots::nsExtendedContentSlots() = default;
541 nsIContent::nsExtendedContentSlots::~nsExtendedContentSlots() {
542 MOZ_ASSERT(!mManualSlotAssignment);
545 size_t nsIContent::nsExtendedContentSlots::SizeOfExcludingThis(
546 MallocSizeOf aMallocSizeOf) const {
547 // For now, nothing to measure here. We don't actually own any of our
548 // members.
549 return 0;
552 FragmentOrElement::nsDOMSlots::nsDOMSlots() : mDataset(nullptr) {
553 MOZ_COUNT_CTOR(nsDOMSlots);
556 FragmentOrElement::nsDOMSlots::~nsDOMSlots() {
557 MOZ_COUNT_DTOR(nsDOMSlots);
559 if (mAttributeMap) {
560 mAttributeMap->DropReference();
564 void FragmentOrElement::nsDOMSlots::Traverse(
565 nsCycleCollectionTraversalCallback& aCb) {
566 nsIContent::nsContentSlots::Traverse(aCb);
568 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mStyle");
569 aCb.NoteXPCOMChild(mStyle.get());
571 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mAttributeMap");
572 aCb.NoteXPCOMChild(mAttributeMap.get());
574 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mChildrenList");
575 aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsINodeList*, mChildrenList));
577 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mClassList");
578 aCb.NoteXPCOMChild(mClassList.get());
580 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mPart");
581 aCb.NoteXPCOMChild(mPart.get());
584 void FragmentOrElement::nsDOMSlots::Unlink(nsINode& aNode) {
585 nsIContent::nsContentSlots::Unlink(aNode);
586 mStyle = nullptr;
587 if (mAttributeMap) {
588 mAttributeMap->DropReference();
589 mAttributeMap = nullptr;
591 mChildrenList = nullptr;
592 mClassList = nullptr;
593 mPart = nullptr;
596 size_t FragmentOrElement::nsDOMSlots::SizeOfIncludingThis(
597 MallocSizeOf aMallocSizeOf) const {
598 size_t n = aMallocSizeOf(this);
600 nsExtendedContentSlots* extendedSlots = GetExtendedContentSlots();
601 if (extendedSlots) {
602 if (OwnsExtendedSlots()) {
603 n += aMallocSizeOf(extendedSlots);
606 n += extendedSlots->SizeOfExcludingThis(aMallocSizeOf);
609 if (mAttributeMap) {
610 n += mAttributeMap->SizeOfIncludingThis(aMallocSizeOf);
613 if (mChildrenList) {
614 n += mChildrenList->SizeOfIncludingThis(aMallocSizeOf);
617 // Measurement of the following members may be added later if DMD finds it is
618 // worthwhile:
619 // - Superclass members (nsINode::nsSlots)
620 // - mStyle
621 // - mDataSet
622 // - mClassList
624 // The following member are not measured:
625 // - mControllers: because it is non-owning
626 return n;
629 FragmentOrElement::nsExtendedDOMSlots::nsExtendedDOMSlots() = default;
631 FragmentOrElement::nsExtendedDOMSlots::~nsExtendedDOMSlots() = default;
633 void FragmentOrElement::nsExtendedDOMSlots::UnlinkExtendedSlots(
634 nsIContent& aContent) {
635 nsIContent::nsExtendedContentSlots::UnlinkExtendedSlots(aContent);
637 // mShadowRoot will similarly be cleared explicitly from
638 // FragmentOrElement::Unlink.
639 mSMILOverrideStyle = nullptr;
640 mControllers = nullptr;
641 mLabelsList = nullptr;
642 mPopoverData = nullptr;
643 if (mCustomElementData) {
644 mCustomElementData->Unlink();
645 mCustomElementData = nullptr;
647 if (mAnimations) {
648 mAnimations = nullptr;
649 aContent.ClearMayHaveAnimations();
651 mExplicitlySetAttrElements.Clear();
652 mRadioGroupContainer = nullptr;
655 void FragmentOrElement::nsExtendedDOMSlots::TraverseExtendedSlots(
656 nsCycleCollectionTraversalCallback& aCb) {
657 nsIContent::nsExtendedContentSlots::TraverseExtendedSlots(aCb);
659 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mSMILOverrideStyle");
660 aCb.NoteXPCOMChild(mSMILOverrideStyle.get());
662 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mControllers");
663 aCb.NoteXPCOMChild(mControllers);
665 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mLabelsList");
666 aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsINodeList*, mLabelsList));
668 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mShadowRoot");
669 aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mShadowRoot));
671 if (mCustomElementData) {
672 mCustomElementData->Traverse(aCb);
674 if (mAnimations) {
675 mAnimations->Traverse(aCb);
677 if (mRadioGroupContainer) {
678 RadioGroupContainer::Traverse(mRadioGroupContainer.get(), aCb);
682 size_t FragmentOrElement::nsExtendedDOMSlots::SizeOfExcludingThis(
683 MallocSizeOf aMallocSizeOf) const {
684 size_t n =
685 nsIContent::nsExtendedContentSlots::SizeOfExcludingThis(aMallocSizeOf);
687 // We own mSMILOverrideStyle but there seems to be no memory reporting on CSS
688 // declarations? At least report the memory the declaration takes up
689 // directly.
690 if (mSMILOverrideStyle) {
691 n += aMallocSizeOf(mSMILOverrideStyle);
694 // We don't really own mSMILOverrideStyleDeclaration. mSMILOverrideStyle owns
695 // it.
697 // We don't seem to have memory reporting for nsXULControllers. At least
698 // report the memory it's using directly.
699 if (mControllers) {
700 n += aMallocSizeOf(mControllers);
703 if (mLabelsList) {
704 n += mLabelsList->SizeOfIncludingThis(aMallocSizeOf);
707 // mShadowRoot should be handled during normal DOM tree memory reporting, just
708 // like kids, siblings, etc.
710 if (mCustomElementData) {
711 n += mCustomElementData->SizeOfIncludingThis(aMallocSizeOf);
714 if (mRadioGroupContainer) {
715 n += mRadioGroupContainer->SizeOfIncludingThis(aMallocSizeOf);
718 return n;
721 FragmentOrElement::FragmentOrElement(
722 already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
723 : nsIContent(std::move(aNodeInfo)) {}
725 FragmentOrElement::~FragmentOrElement() {
726 MOZ_ASSERT(!IsInUncomposedDoc(),
727 "Please remove this from the document properly");
728 if (GetParent()) {
729 NS_RELEASE(mParent);
733 static nsINode* FindChromeAccessOnlySubtreeOwner(nsINode* aNode) {
734 if (!aNode->ChromeOnlyAccess()) {
735 return aNode;
737 return const_cast<nsIContent*>(aNode->GetChromeOnlyAccessSubtreeRootParent());
740 nsINode* FindChromeAccessOnlySubtreeOwner(EventTarget* aTarget) {
741 nsINode* node = nsINode::FromEventTargetOrNull(aTarget);
742 if (!node) {
743 return nullptr;
745 return FindChromeAccessOnlySubtreeOwner(node);
748 void nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
749 // FIXME! Document how this event retargeting works, Bug 329124.
750 aVisitor.mCanHandle = true;
751 aVisitor.mMayHaveListenerManager = HasListenerManager();
753 if (IsInShadowTree()) {
754 aVisitor.mItemInShadowTree = true;
757 // Don't propagate mouseover and mouseout events when mouse is moving
758 // inside chrome access only content.
759 const bool isAnonForEvents = IsRootOfChromeAccessOnlySubtree();
760 aVisitor.mRootOfClosedTree = isAnonForEvents;
761 if ((aVisitor.mEvent->mMessage == eMouseOver ||
762 aVisitor.mEvent->mMessage == eMouseOut ||
763 aVisitor.mEvent->mMessage == ePointerOver ||
764 aVisitor.mEvent->mMessage == ePointerOut) &&
765 // Check if we should stop event propagation when event has just been
766 // dispatched or when we're about to propagate from
767 // chrome access only subtree or if we are about to propagate out of
768 // a shadow root to a shadow root host.
769 ((this == aVisitor.mEvent->mOriginalTarget && !ChromeOnlyAccess()) ||
770 isAnonForEvents)) {
771 nsIContent* relatedTarget = nsIContent::FromEventTargetOrNull(
772 aVisitor.mEvent->AsMouseEvent()->mRelatedTarget);
773 if (relatedTarget && relatedTarget->OwnerDoc() == OwnerDoc()) {
774 // If current target is anonymous for events or we know that related
775 // target is descendant of an element which is anonymous for events,
776 // we may want to stop event propagation.
777 // If this is the original target, aVisitor.mRelatedTargetIsInAnon
778 // must be updated.
779 if (isAnonForEvents || aVisitor.mRelatedTargetIsInAnon ||
780 (aVisitor.mEvent->mOriginalTarget == this &&
781 (aVisitor.mRelatedTargetIsInAnon =
782 relatedTarget->ChromeOnlyAccess()))) {
783 nsINode* anonOwner = FindChromeAccessOnlySubtreeOwner(this);
784 if (anonOwner) {
785 nsINode* anonOwnerRelated =
786 FindChromeAccessOnlySubtreeOwner(relatedTarget);
787 if (anonOwnerRelated) {
788 // Note, anonOwnerRelated may still be inside some other
789 // native anonymous subtree. The case where anonOwner is still
790 // inside native anonymous subtree will be handled when event
791 // propagates up in the DOM tree.
792 while (anonOwner != anonOwnerRelated &&
793 anonOwnerRelated->ChromeOnlyAccess()) {
794 anonOwnerRelated =
795 FindChromeAccessOnlySubtreeOwner(anonOwnerRelated);
797 if (anonOwner == anonOwnerRelated) {
798 #ifdef DEBUG_smaug
799 nsIContent* originalTarget = nsIContent::FromEventTargetOrNull(
800 aVisitor.mEvent->mOriginalTarget);
801 nsAutoString ot, ct, rt;
802 if (originalTarget) {
803 originalTarget->NodeInfo()->NameAtom()->ToString(ot);
805 NodeInfo()->NameAtom()->ToString(ct);
806 relatedTarget->NodeInfo()->NameAtom()->ToString(rt);
807 printf(
808 "Stopping %s propagation:"
809 "\n\toriginalTarget=%s \n\tcurrentTarget=%s %s"
810 "\n\trelatedTarget=%s %s \n%s",
811 (aVisitor.mEvent->mMessage == eMouseOver) ? "mouseover"
812 : "mouseout",
813 NS_ConvertUTF16toUTF8(ot).get(),
814 NS_ConvertUTF16toUTF8(ct).get(),
815 isAnonForEvents
816 ? "(is native anonymous)"
817 : (ChromeOnlyAccess() ? "(is in native anonymous subtree)"
818 : ""),
819 NS_ConvertUTF16toUTF8(rt).get(),
820 relatedTarget->ChromeOnlyAccess()
821 ? "(is in native anonymous subtree)"
822 : "",
823 (originalTarget &&
824 relatedTarget->FindFirstNonChromeOnlyAccessContent() ==
825 originalTarget->FindFirstNonChromeOnlyAccessContent())
826 ? ""
827 : "Wrong event propagation!?!\n");
828 #endif
829 aVisitor.SetParentTarget(nullptr, false);
830 // Event should not propagate to non-anon content.
831 aVisitor.mCanHandle = isAnonForEvents;
832 return;
840 // Event parent is the assigned slot, if node is assigned, or node's parent
841 // otherwise.
842 HTMLSlotElement* slot = GetAssignedSlot();
843 nsIContent* parent = slot ? slot : GetParent();
845 // Event may need to be retargeted if this is the root of a native anonymous
846 // content subtree.
847 if (isAnonForEvents) {
848 #ifdef DEBUG
849 // If a DOM event is explicitly dispatched using node.dispatchEvent(), then
850 // all the events are allowed even in the native anonymous content..
851 nsIContent* t =
852 nsIContent::FromEventTargetOrNull(aVisitor.mEvent->mOriginalTarget);
853 NS_ASSERTION(!t || !t->ChromeOnlyAccess() ||
854 aVisitor.mEvent->mClass != eMutationEventClass ||
855 aVisitor.mDOMEvent,
856 "Mutation event dispatched in native anonymous content!?!");
857 #endif
858 if (aVisitor.mEvent->mClass == eTransitionEventClass ||
859 aVisitor.mEvent->mClass == eAnimationEventClass) {
860 // Event should not propagate to non-anon content.
861 aVisitor.SetParentTarget(nullptr, false);
862 return;
864 aVisitor.mEventTargetAtParent = parent;
865 } else if (parent && aVisitor.mOriginalTargetIsInAnon) {
866 nsIContent* content =
867 nsIContent::FromEventTargetOrNull(aVisitor.mEvent->mTarget);
868 if (content &&
869 content->GetClosestNativeAnonymousSubtreeRootParentOrHost() == parent) {
870 aVisitor.mEventTargetAtParent = parent;
874 if (!aVisitor.mEvent->mFlags.mComposedInNativeAnonymousContent &&
875 IsRootOfNativeAnonymousSubtree() && OwnerDoc()->GetWindow()) {
876 aVisitor.SetParentTarget(OwnerDoc()->GetWindow()->GetParentTarget(), true);
877 } else if (parent) {
878 aVisitor.SetParentTarget(parent, false);
879 if (slot) {
880 ShadowRoot* root = slot->GetContainingShadow();
881 if (root && root->IsClosed()) {
882 aVisitor.mParentIsSlotInClosedTree = true;
885 } else {
886 aVisitor.SetParentTarget(GetComposedDoc(), false);
889 if (!ChromeOnlyAccess() && !aVisitor.mRelatedTargetRetargetedInCurrentScope) {
890 // We don't support Shadow DOM in native anonymous content yet.
891 aVisitor.mRelatedTargetRetargetedInCurrentScope = true;
892 if (aVisitor.mEvent->mOriginalRelatedTarget) {
893 // https://dom.spec.whatwg.org/#concept-event-dispatch
894 // Step 3.
895 // "Let relatedTarget be the result of retargeting event's relatedTarget
896 // against target if event's relatedTarget is non-null, and null
897 // otherwise."
899 // This is a bit complicated because the event might be from native
900 // anonymous content, but we need to deal with non-native anonymous
901 // content there.
902 bool initialTarget = this == aVisitor.mEvent->mOriginalTarget;
903 nsCOMPtr<nsINode> originalTargetAsNode;
904 // Use of mOriginalTargetIsInAnon is an optimization here.
905 if (!initialTarget && aVisitor.mOriginalTargetIsInAnon) {
906 originalTargetAsNode =
907 FindChromeAccessOnlySubtreeOwner(aVisitor.mEvent->mOriginalTarget);
908 initialTarget = originalTargetAsNode == this;
910 if (initialTarget) {
911 nsCOMPtr<nsINode> relatedTargetAsNode =
912 FindChromeAccessOnlySubtreeOwner(
913 aVisitor.mEvent->mOriginalRelatedTarget);
914 if (!originalTargetAsNode) {
915 originalTargetAsNode =
916 nsINode::FromEventTargetOrNull(aVisitor.mEvent->mOriginalTarget);
919 if (relatedTargetAsNode && originalTargetAsNode) {
920 nsINode* retargetedRelatedTarget = nsContentUtils::Retarget(
921 relatedTargetAsNode, originalTargetAsNode);
922 if (originalTargetAsNode == retargetedRelatedTarget &&
923 retargetedRelatedTarget != relatedTargetAsNode) {
924 // Step 4.
925 // "If target is relatedTarget and target is not event's
926 // relatedTarget, then return true."
927 aVisitor.IgnoreCurrentTargetBecauseOfShadowDOMRetargeting();
928 // Old code relies on mTarget to point to the first element which
929 // was not added to the event target chain because of mCanHandle
930 // being false, but in Shadow DOM case mTarget really should
931 // point to a node in Shadow DOM.
932 aVisitor.mEvent->mTarget = aVisitor.mTargetInKnownToBeHandledScope;
933 return;
936 // Part of step 5. Retargeting target has happened already higher
937 // up in this method.
938 // "Append to an event path with event, target, targetOverride,
939 // relatedTarget, and false."
940 aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
942 } else {
943 nsCOMPtr<nsINode> relatedTargetAsNode =
944 FindChromeAccessOnlySubtreeOwner(
945 aVisitor.mEvent->mOriginalRelatedTarget);
946 if (relatedTargetAsNode) {
947 // Step 11.3.
948 // "Let relatedTarget be the result of retargeting event's
949 // relatedTarget against parent if event's relatedTarget is non-null,
950 // and null otherwise.".
951 nsINode* retargetedRelatedTarget =
952 nsContentUtils::Retarget(relatedTargetAsNode, this);
953 nsCOMPtr<nsINode> targetInKnownToBeHandledScope =
954 FindChromeAccessOnlySubtreeOwner(
955 aVisitor.mTargetInKnownToBeHandledScope);
956 // If aVisitor.mTargetInKnownToBeHandledScope wasn't nsINode,
957 // targetInKnownToBeHandledScope will be null. This may happen when
958 // dispatching event to Window object in a content page and
959 // propagating the event to a chrome Element.
960 if (targetInKnownToBeHandledScope &&
961 IsShadowIncludingInclusiveDescendantOf(
962 targetInKnownToBeHandledScope->SubtreeRoot())) {
963 // Part of step 11.4.
964 // "If target's root is a shadow-including inclusive ancestor of
965 // parent, then"
966 // "...Append to an event path with event, parent, null,
967 // relatedTarget, " and slot-in-closed-tree."
968 aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
969 } else if (this == retargetedRelatedTarget) {
970 // Step 11.5
971 // "Otherwise, if parent and relatedTarget are identical, then set
972 // parent to null."
973 aVisitor.IgnoreCurrentTargetBecauseOfShadowDOMRetargeting();
974 // Old code relies on mTarget to point to the first element which
975 // was not added to the event target chain because of mCanHandle
976 // being false, but in Shadow DOM case mTarget really should
977 // point to a node in Shadow DOM.
978 aVisitor.mEvent->mTarget = aVisitor.mTargetInKnownToBeHandledScope;
979 return;
980 } else if (targetInKnownToBeHandledScope) {
981 // Note, if targetInKnownToBeHandledScope is null,
982 // mTargetInKnownToBeHandledScope could be Window object in content
983 // page and we're in chrome document in the same process.
985 // Step 11.6
986 aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
992 if (aVisitor.mEvent->mClass == eTouchEventClass) {
993 // Retarget touch objects.
994 MOZ_ASSERT(!aVisitor.mRetargetedTouchTargets.isSome());
995 aVisitor.mRetargetedTouchTargets.emplace();
996 WidgetTouchEvent* touchEvent = aVisitor.mEvent->AsTouchEvent();
997 WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
998 for (uint32_t i = 0; i < touches.Length(); ++i) {
999 Touch* touch = touches[i];
1000 EventTarget* originalTarget = touch->mOriginalTarget;
1001 EventTarget* touchTarget = originalTarget;
1002 nsCOMPtr<nsINode> targetAsNode =
1003 nsINode::FromEventTargetOrNull(originalTarget);
1004 if (targetAsNode) {
1005 EventTarget* retargeted =
1006 nsContentUtils::Retarget(targetAsNode, this);
1007 if (retargeted) {
1008 touchTarget = retargeted;
1011 aVisitor.mRetargetedTouchTargets->AppendElement(touchTarget);
1012 touch->mTarget = touchTarget;
1014 MOZ_ASSERT(aVisitor.mRetargetedTouchTargets->Length() ==
1015 touches.Length());
1019 if (slot) {
1020 // Inform that we're about to exit the current scope.
1021 aVisitor.mRelatedTargetRetargetedInCurrentScope = false;
1025 Element* nsIContent::GetAutofocusDelegate(bool aWithMouse) const {
1026 for (nsINode* node = GetFirstChild(); node; node = node->GetNextNode(this)) {
1027 auto* descendant = Element::FromNode(*node);
1028 if (!descendant || !descendant->GetBoolAttr(nsGkAtoms::autofocus)) {
1029 continue;
1032 nsIFrame* frame = descendant->GetPrimaryFrame();
1033 if (frame && frame->IsFocusable(aWithMouse)) {
1034 return descendant;
1037 return nullptr;
1040 Element* nsIContent::GetFocusDelegate(bool aWithMouse) const {
1041 const nsIContent* whereToLook = this;
1042 if (ShadowRoot* root = GetShadowRoot()) {
1043 if (!root->DelegatesFocus()) {
1044 // 1. If focusTarget is a shadow host and its shadow root 's delegates
1045 // focus is false, then return null.
1046 return nullptr;
1048 whereToLook = root;
1051 auto IsFocusable = [&](Element* aElement) -> Focusable {
1052 nsIFrame* frame = aElement->GetPrimaryFrame();
1054 if (!frame) {
1055 return {};
1058 return frame->IsFocusable(aWithMouse);
1061 Element* potentialFocus = nullptr;
1062 for (nsINode* node = whereToLook->GetFirstChild(); node;
1063 node = node->GetNextNode(whereToLook)) {
1064 auto* el = Element::FromNode(*node);
1065 if (!el) {
1066 continue;
1069 const bool autofocus = el->GetBoolAttr(nsGkAtoms::autofocus);
1071 if (autofocus) {
1072 if (IsFocusable(el)) {
1073 // Found an autofocus candidate.
1074 return el;
1076 } else if (!potentialFocus) {
1077 if (Focusable focusable = IsFocusable(el)) {
1078 if (IsHTMLElement(nsGkAtoms::dialog)) {
1079 if (focusable.mTabIndex >= 0) {
1080 // If focusTarget is a dialog element and descendant is sequentially
1081 // focusable, then set focusableArea to descendant.
1082 potentialFocus = el;
1084 } else {
1085 // This element could be the one if we can't find an
1086 // autofocus candidate which has the precedence.
1087 potentialFocus = el;
1092 if (!autofocus && potentialFocus) {
1093 // Nothing else to do, we are not looking for more focusable elements
1094 // here.
1095 continue;
1098 if (auto* shadow = el->GetShadowRoot()) {
1099 if (shadow->DelegatesFocus()) {
1100 if (Element* delegatedFocus = shadow->GetFocusDelegate(aWithMouse)) {
1101 if (autofocus) {
1102 // This element has autofocus and we found an focus delegates
1103 // in its descendants, so use the focus delegates
1104 return delegatedFocus;
1106 if (!potentialFocus) {
1107 potentialFocus = delegatedFocus;
1114 return potentialFocus;
1117 Focusable nsIContent::IsFocusableWithoutStyle(bool aWithMouse) {
1118 // Default, not tabbable
1119 return {};
1122 void nsIContent::SetAssignedSlot(HTMLSlotElement* aSlot) {
1123 MOZ_ASSERT(aSlot || GetExistingExtendedContentSlots());
1124 ExtendedContentSlots()->mAssignedSlot = aSlot;
1127 #ifdef MOZ_DOM_LIST
1128 void nsIContent::Dump() { List(); }
1129 #endif
1131 void FragmentOrElement::GetTextContentInternal(nsAString& aTextContent,
1132 OOMReporter& aError) {
1133 if (!nsContentUtils::GetNodeTextContent(this, true, aTextContent, fallible)) {
1134 aError.ReportOOM();
1138 void FragmentOrElement::SetTextContentInternal(const nsAString& aTextContent,
1139 nsIPrincipal* aSubjectPrincipal,
1140 ErrorResult& aError) {
1141 bool tryReuse = false;
1142 if (!aTextContent.IsEmpty()) {
1143 if (nsIContent* firstChild = GetFirstChild()) {
1144 tryReuse = firstChild->NodeType() == TEXT_NODE &&
1145 !firstChild->GetNextSibling() &&
1146 firstChild->OwnedOnlyByTheDOMAndFrameTrees() &&
1147 #ifdef ACCESSIBILITY
1148 !GetAccService() &&
1149 #endif
1150 !OwnerDoc()->MayHaveDOMMutationObservers() &&
1151 !nsContentUtils::HasMutationListeners(
1152 OwnerDoc(), NS_EVENT_BITS_MUTATION_ALL);
1156 aError = nsContentUtils::SetNodeTextContent(this, aTextContent, tryReuse);
1159 void FragmentOrElement::DestroyContent() {
1160 // Drop any servo data. We do this before the RemovedFromDocument call below
1161 // so that it doesn't need to try to keep the style state sane when shuffling
1162 // around the flattened tree.
1164 // TODO(emilio): I suspect this can be asserted against instead, with a bit of
1165 // effort to avoid calling Document::Destroy with a shell...
1166 if (IsElement()) {
1167 AsElement()->ClearServoData();
1170 #ifdef DEBUG
1171 uint32_t oldChildCount = GetChildCount();
1172 #endif
1174 for (nsIContent* child = GetFirstChild(); child;
1175 child = child->GetNextSibling()) {
1176 child->DestroyContent();
1177 MOZ_ASSERT(child->GetParent() == this,
1178 "Mutating the tree during XBL destructors is evil");
1181 MOZ_ASSERT(oldChildCount == GetChildCount(),
1182 "Mutating the tree during XBL destructors is evil");
1184 if (ShadowRoot* shadowRoot = GetShadowRoot()) {
1185 shadowRoot->DestroyContent();
1189 void FragmentOrElement::SaveSubtreeState() {
1190 for (nsIContent* child = GetFirstChild(); child;
1191 child = child->GetNextSibling()) {
1192 child->SaveSubtreeState();
1195 // FIXME(bug 1469277): Pretty sure this wants to dig into shadow trees as
1196 // well.
1199 //----------------------------------------------------------------------
1201 // Generic DOMNode implementations
1203 void FragmentOrElement::FireNodeInserted(
1204 Document* aDoc, nsINode* aParent,
1205 const nsTArray<nsCOMPtr<nsIContent>>& aNodes) {
1206 for (const nsCOMPtr<nsIContent>& childContent : aNodes) {
1207 if (nsContentUtils::HasMutationListeners(
1208 childContent, NS_EVENT_BITS_MUTATION_NODEINSERTED, aParent)) {
1209 InternalMutationEvent mutation(true, eLegacyNodeInserted);
1210 mutation.mRelatedNode = aParent;
1212 mozAutoSubtreeModified subtree(aDoc, aParent);
1213 AsyncEventDispatcher::RunDOMEventWhenSafe(*childContent, mutation);
1218 //----------------------------------------------------------------------
1220 // nsISupports implementation
1222 #define SUBTREE_UNBINDINGS_PER_RUNNABLE 500
1224 class ContentUnbinder : public Runnable {
1225 public:
1226 ContentUnbinder() : Runnable("ContentUnbinder") { mLast = this; }
1228 ~ContentUnbinder() { Run(); }
1230 void UnbindSubtree(nsIContent* aNode) {
1231 if (aNode->NodeType() != nsINode::ELEMENT_NODE &&
1232 aNode->NodeType() != nsINode::DOCUMENT_FRAGMENT_NODE) {
1233 return;
1235 FragmentOrElement* container = static_cast<FragmentOrElement*>(aNode);
1236 if (container->HasChildren()) {
1237 // Invalidate cached array of child nodes
1238 container->InvalidateChildNodes();
1240 while (container->HasChildren()) {
1241 // Hold a strong ref to the node when we remove it, because we may be
1242 // the last reference to it. We need to call DisconnectChild()
1243 // before calling UnbindFromTree, since this last can notify various
1244 // observers and they should really see consistent
1245 // tree state.
1246 // If this code changes, change the corresponding code in
1247 // FragmentOrElement's and Document's unlink impls.
1248 nsCOMPtr<nsIContent> child = container->GetLastChild();
1249 container->DisconnectChild(child);
1250 UnbindSubtree(child);
1251 child->UnbindFromTree();
1256 NS_IMETHOD Run() override {
1257 nsAutoScriptBlocker scriptBlocker;
1258 uint32_t len = mSubtreeRoots.Length();
1259 if (len) {
1260 for (uint32_t i = 0; i < len; ++i) {
1261 UnbindSubtree(mSubtreeRoots[i]);
1263 mSubtreeRoots.Clear();
1265 nsCycleCollector_dispatchDeferredDeletion();
1266 if (this == sContentUnbinder) {
1267 sContentUnbinder = nullptr;
1268 if (mNext) {
1269 RefPtr<ContentUnbinder> next;
1270 next.swap(mNext);
1271 sContentUnbinder = next;
1272 next->mLast = mLast;
1273 mLast = nullptr;
1274 NS_DispatchToCurrentThreadQueue(next.forget(),
1275 EventQueuePriority::Idle);
1278 return NS_OK;
1281 static void UnbindAll() {
1282 RefPtr<ContentUnbinder> ub = sContentUnbinder;
1283 sContentUnbinder = nullptr;
1284 while (ub) {
1285 ub->Run();
1286 ub = ub->mNext;
1290 static void Append(nsIContent* aSubtreeRoot) {
1291 if (!sContentUnbinder) {
1292 sContentUnbinder = new ContentUnbinder();
1293 nsCOMPtr<nsIRunnable> e = sContentUnbinder;
1294 NS_DispatchToCurrentThreadQueue(e.forget(), EventQueuePriority::Idle);
1297 if (sContentUnbinder->mLast->mSubtreeRoots.Length() >=
1298 SUBTREE_UNBINDINGS_PER_RUNNABLE) {
1299 sContentUnbinder->mLast->mNext = new ContentUnbinder();
1300 sContentUnbinder->mLast = sContentUnbinder->mLast->mNext;
1302 sContentUnbinder->mLast->mSubtreeRoots.AppendElement(aSubtreeRoot);
1305 private:
1306 AutoTArray<nsCOMPtr<nsIContent>, SUBTREE_UNBINDINGS_PER_RUNNABLE>
1307 mSubtreeRoots;
1308 RefPtr<ContentUnbinder> mNext;
1309 ContentUnbinder* mLast;
1310 static ContentUnbinder* sContentUnbinder;
1313 ContentUnbinder* ContentUnbinder::sContentUnbinder = nullptr;
1315 void FragmentOrElement::ClearContentUnbinder() { ContentUnbinder::UnbindAll(); }
1317 // Note, _INHERITED macro isn't used here since nsINode implementations are
1318 // rather special.
1319 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(FragmentOrElement)
1321 // We purposefully don't UNLINK_BEGIN_INHERITED here.
1322 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement)
1323 nsIContent::Unlink(tmp);
1325 if (tmp->HasProperties()) {
1326 if (tmp->IsElement()) {
1327 Element* elem = tmp->AsElement();
1328 elem->UnlinkIntersectionObservers();
1332 // Unlink child content (and unbind our subtree).
1333 if (tmp->UnoptimizableCCNode() || !nsCCUncollectableMarker::sGeneration) {
1334 // Don't allow script to run while we're unbinding everything.
1335 nsAutoScriptBlocker scriptBlocker;
1336 while (tmp->HasChildren()) {
1337 // Hold a strong ref to the node when we remove it, because we may be
1338 // the last reference to it.
1339 // If this code changes, change the corresponding code in Document's
1340 // unlink impl and ContentUnbinder::UnbindSubtree.
1341 nsCOMPtr<nsIContent> child = tmp->GetLastChild();
1342 tmp->DisconnectChild(child);
1343 child->UnbindFromTree();
1345 } else if (!tmp->GetParent() && tmp->HasChildren()) {
1346 ContentUnbinder::Append(tmp);
1347 } /* else {
1348 The subtree root will end up to a ContentUnbinder, and that will
1349 unbind the child nodes.
1350 } */
1352 if (ShadowRoot* shadowRoot = tmp->GetShadowRoot()) {
1353 shadowRoot->Unbind();
1354 tmp->ExtendedDOMSlots()->mShadowRoot = nullptr;
1357 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1359 void FragmentOrElement::MarkNodeChildren(nsINode* aNode) {
1360 JSObject* o = GetJSObjectChild(aNode);
1361 if (o) {
1362 JS::ExposeObjectToActiveJS(o);
1365 EventListenerManager* elm = aNode->GetExistingListenerManager();
1366 if (elm) {
1367 elm->MarkForCC();
1371 nsINode* FindOptimizableSubtreeRoot(nsINode* aNode) {
1372 nsINode* p;
1373 while ((p = aNode->GetParentNode())) {
1374 if (aNode->UnoptimizableCCNode()) {
1375 return nullptr;
1377 aNode = p;
1380 if (aNode->UnoptimizableCCNode()) {
1381 return nullptr;
1383 return aNode;
1386 StaticAutoPtr<nsTHashSet<nsINode*>> gCCBlackMarkedNodes;
1388 static void ClearBlackMarkedNodes() {
1389 if (!gCCBlackMarkedNodes) {
1390 return;
1392 for (nsINode* n : *gCCBlackMarkedNodes) {
1393 n->SetCCMarkedRoot(false);
1394 n->SetInCCBlackTree(false);
1396 gCCBlackMarkedNodes = nullptr;
1399 // static
1400 void FragmentOrElement::RemoveBlackMarkedNode(nsINode* aNode) {
1401 if (!gCCBlackMarkedNodes) {
1402 return;
1404 gCCBlackMarkedNodes->Remove(aNode);
1407 static bool IsCertainlyAliveNode(nsINode* aNode, Document* aDoc) {
1408 MOZ_ASSERT(aNode->GetComposedDoc() == aDoc);
1410 // Marked to be in-CC-generation or if the document is an svg image that's
1411 // being kept alive by the image cache. (Note that an svg image's internal
1412 // SVG document will receive an OnPageHide() call when it gets purged from
1413 // the image cache; hence, we use IsVisible() as a hint that the document is
1414 // actively being kept alive by the cache.)
1415 return nsCCUncollectableMarker::InGeneration(aDoc->GetMarkedCCGeneration()) ||
1416 (nsCCUncollectableMarker::sGeneration && aDoc->IsBeingUsedAsImage() &&
1417 aDoc->IsVisible());
1420 // static
1421 bool FragmentOrElement::CanSkipInCC(nsINode* aNode) {
1422 // Don't try to optimize anything during shutdown.
1423 if (nsCCUncollectableMarker::sGeneration == 0) {
1424 return false;
1427 Document* currentDoc = aNode->GetComposedDoc();
1428 if (currentDoc && IsCertainlyAliveNode(aNode, currentDoc)) {
1429 return !NeedsScriptTraverse(aNode);
1432 // Bail out early if aNode is somewhere in anonymous content,
1433 // or otherwise unusual.
1434 if (aNode->UnoptimizableCCNode()) {
1435 return false;
1438 nsINode* root = currentDoc ? static_cast<nsINode*>(currentDoc)
1439 : FindOptimizableSubtreeRoot(aNode);
1440 if (!root) {
1441 return false;
1444 // Subtree has been traversed already.
1445 if (root->CCMarkedRoot()) {
1446 return root->InCCBlackTree() && !NeedsScriptTraverse(aNode);
1449 if (!gCCBlackMarkedNodes) {
1450 gCCBlackMarkedNodes = new nsTHashSet<nsINode*>(1020);
1453 // nodesToUnpurple contains nodes which will be removed
1454 // from the purple buffer if the DOM tree is known-live.
1455 AutoTArray<nsIContent*, 1020> nodesToUnpurple;
1456 // grayNodes need script traverse, so they aren't removed from
1457 // the purple buffer, but are marked to be in known-live subtree so that
1458 // traverse is faster.
1459 AutoTArray<nsINode*, 1020> grayNodes;
1461 bool foundLiveWrapper = root->HasKnownLiveWrapper();
1462 if (root != currentDoc) {
1463 currentDoc = nullptr;
1464 if (NeedsScriptTraverse(root)) {
1465 grayNodes.AppendElement(root);
1466 } else if (static_cast<nsIContent*>(root)->IsPurple()) {
1467 nodesToUnpurple.AppendElement(static_cast<nsIContent*>(root));
1471 // Traverse the subtree and check if we could know without CC
1472 // that it is known-live.
1473 // Note, this traverse is non-virtual and inline, so it should be a lot faster
1474 // than CC's generic traverse.
1475 for (nsIContent* node = root->GetFirstChild(); node;
1476 node = node->GetNextNode(root)) {
1477 foundLiveWrapper = foundLiveWrapper || node->HasKnownLiveWrapper();
1478 if (foundLiveWrapper && currentDoc) {
1479 // If we can mark the whole document known-live, no need to optimize
1480 // so much, since when the next purple node in the document will be
1481 // handled, it is fast to check that currentDoc is in CCGeneration.
1482 break;
1484 if (NeedsScriptTraverse(node)) {
1485 // Gray nodes need real CC traverse.
1486 grayNodes.AppendElement(node);
1487 } else if (node->IsPurple()) {
1488 nodesToUnpurple.AppendElement(node);
1492 root->SetCCMarkedRoot(true);
1493 root->SetInCCBlackTree(foundLiveWrapper);
1494 gCCBlackMarkedNodes->Insert(root);
1496 if (!foundLiveWrapper) {
1497 return false;
1500 if (currentDoc) {
1501 // Special case documents. If we know the document is known-live,
1502 // we can mark the document to be in CCGeneration.
1503 currentDoc->MarkUncollectableForCCGeneration(
1504 nsCCUncollectableMarker::sGeneration);
1505 } else {
1506 for (uint32_t i = 0; i < grayNodes.Length(); ++i) {
1507 nsINode* node = grayNodes[i];
1508 node->SetInCCBlackTree(true);
1509 gCCBlackMarkedNodes->Insert(node);
1513 // Subtree is known-live, we can remove non-gray purple nodes from
1514 // purple buffer.
1515 for (uint32_t i = 0; i < nodesToUnpurple.Length(); ++i) {
1516 nsIContent* purple = nodesToUnpurple[i];
1517 // Can't remove currently handled purple node.
1518 if (purple != aNode) {
1519 purple->RemovePurple();
1522 return !NeedsScriptTraverse(aNode);
1525 AutoTArray<nsINode*, 1020>* gPurpleRoots = nullptr;
1526 AutoTArray<nsIContent*, 1020>* gNodesToUnbind = nullptr;
1528 void ClearCycleCollectorCleanupData() {
1529 if (gPurpleRoots) {
1530 uint32_t len = gPurpleRoots->Length();
1531 for (uint32_t i = 0; i < len; ++i) {
1532 nsINode* n = gPurpleRoots->ElementAt(i);
1533 n->SetIsPurpleRoot(false);
1535 delete gPurpleRoots;
1536 gPurpleRoots = nullptr;
1538 if (gNodesToUnbind) {
1539 uint32_t len = gNodesToUnbind->Length();
1540 for (uint32_t i = 0; i < len; ++i) {
1541 nsIContent* c = gNodesToUnbind->ElementAt(i);
1542 c->SetIsPurpleRoot(false);
1543 ContentUnbinder::Append(c);
1545 delete gNodesToUnbind;
1546 gNodesToUnbind = nullptr;
1550 static bool ShouldClearPurple(nsIContent* aContent) {
1551 MOZ_ASSERT(aContent);
1552 if (aContent->IsPurple()) {
1553 return true;
1556 JSObject* o = GetJSObjectChild(aContent);
1557 if (o && JS::ObjectIsMarkedGray(o)) {
1558 return true;
1561 if (aContent->HasListenerManager()) {
1562 return true;
1565 return aContent->HasProperties();
1568 // If aNode is not optimizable, but is an element
1569 // with a frame in a document which has currently active presshell,
1570 // we can act as if it was optimizable. When the primary frame dies, aNode
1571 // will end up to the purple buffer because of the refcount change.
1572 bool NodeHasActiveFrame(Document* aCurrentDoc, nsINode* aNode) {
1573 return aCurrentDoc->GetPresShell() && aNode->IsElement() &&
1574 aNode->AsElement()->GetPrimaryFrame();
1577 // CanSkip checks if aNode is known-live, and if it is, returns true. If aNode
1578 // is in a known-live DOM tree, CanSkip may also remove other objects from
1579 // purple buffer and unmark event listeners and user data. If the root of the
1580 // DOM tree is a document, less optimizations are done since checking the
1581 // liveness of the current document is usually fast and we don't want slow down
1582 // such common cases.
1583 bool FragmentOrElement::CanSkip(nsINode* aNode, bool aRemovingAllowed) {
1584 // Don't try to optimize anything during shutdown.
1585 if (nsCCUncollectableMarker::sGeneration == 0) {
1586 return false;
1589 bool unoptimizable = aNode->UnoptimizableCCNode();
1590 Document* currentDoc = aNode->GetComposedDoc();
1591 if (currentDoc && IsCertainlyAliveNode(aNode, currentDoc) &&
1592 (!unoptimizable || NodeHasActiveFrame(currentDoc, aNode))) {
1593 MarkNodeChildren(aNode);
1594 return true;
1597 if (unoptimizable) {
1598 return false;
1601 nsINode* root = currentDoc ? static_cast<nsINode*>(currentDoc)
1602 : FindOptimizableSubtreeRoot(aNode);
1603 if (!root) {
1604 return false;
1607 // Subtree has been traversed already, and aNode has
1608 // been handled in a way that doesn't require revisiting it.
1609 if (root->IsPurpleRoot()) {
1610 return false;
1613 // nodesToClear contains nodes which are either purple or
1614 // gray.
1615 AutoTArray<nsIContent*, 1020> nodesToClear;
1617 bool foundLiveWrapper = root->HasKnownLiveWrapper();
1618 bool domOnlyCycle = false;
1619 if (root != currentDoc) {
1620 currentDoc = nullptr;
1621 if (!foundLiveWrapper) {
1622 domOnlyCycle = static_cast<nsIContent*>(root)->OwnedOnlyByTheDOMTree();
1624 if (ShouldClearPurple(static_cast<nsIContent*>(root))) {
1625 nodesToClear.AppendElement(static_cast<nsIContent*>(root));
1629 // Traverse the subtree and check if we could know without CC
1630 // that it is known-live.
1631 // Note, this traverse is non-virtual and inline, so it should be a lot faster
1632 // than CC's generic traverse.
1633 for (nsIContent* node = root->GetFirstChild(); node;
1634 node = node->GetNextNode(root)) {
1635 foundLiveWrapper = foundLiveWrapper || node->HasKnownLiveWrapper();
1636 if (foundLiveWrapper) {
1637 domOnlyCycle = false;
1638 if (currentDoc) {
1639 // If we can mark the whole document live, no need to optimize
1640 // so much, since when the next purple node in the document will be
1641 // handled, it is fast to check that the currentDoc is in CCGeneration.
1642 break;
1644 // No need to put stuff to the nodesToClear array, if we can clear it
1645 // already here.
1646 if (node->IsPurple() && (node != aNode || aRemovingAllowed)) {
1647 node->RemovePurple();
1649 MarkNodeChildren(node);
1650 } else {
1651 domOnlyCycle = domOnlyCycle && node->OwnedOnlyByTheDOMTree();
1652 if (ShouldClearPurple(node)) {
1653 // Collect interesting nodes which we can clear if we find that
1654 // they are kept alive in a known-live tree or are in a DOM-only cycle.
1655 nodesToClear.AppendElement(node);
1660 if (!currentDoc || !foundLiveWrapper) {
1661 root->SetIsPurpleRoot(true);
1662 if (domOnlyCycle) {
1663 if (!gNodesToUnbind) {
1664 gNodesToUnbind = new AutoTArray<nsIContent*, 1020>();
1666 gNodesToUnbind->AppendElement(static_cast<nsIContent*>(root));
1667 for (uint32_t i = 0; i < nodesToClear.Length(); ++i) {
1668 nsIContent* n = nodesToClear[i];
1669 if ((n != aNode || aRemovingAllowed) && n->IsPurple()) {
1670 n->RemovePurple();
1673 return true;
1674 } else {
1675 if (!gPurpleRoots) {
1676 gPurpleRoots = new AutoTArray<nsINode*, 1020>();
1678 gPurpleRoots->AppendElement(root);
1682 if (!foundLiveWrapper) {
1683 return false;
1686 if (currentDoc) {
1687 // Special case documents. If we know the document is known-live,
1688 // we can mark the document to be in CCGeneration.
1689 currentDoc->MarkUncollectableForCCGeneration(
1690 nsCCUncollectableMarker::sGeneration);
1691 MarkNodeChildren(currentDoc);
1694 // Subtree is known-live, so we can remove purple nodes from
1695 // purple buffer and mark stuff that to be certainly alive.
1696 for (uint32_t i = 0; i < nodesToClear.Length(); ++i) {
1697 nsIContent* n = nodesToClear[i];
1698 MarkNodeChildren(n);
1699 // Can't remove currently handled purple node,
1700 // unless aRemovingAllowed is true.
1701 if ((n != aNode || aRemovingAllowed) && n->IsPurple()) {
1702 n->RemovePurple();
1705 return true;
1708 bool FragmentOrElement::CanSkipThis(nsINode* aNode) {
1709 if (nsCCUncollectableMarker::sGeneration == 0) {
1710 return false;
1712 if (aNode->HasKnownLiveWrapper()) {
1713 return true;
1715 Document* c = aNode->GetComposedDoc();
1716 return ((c && IsCertainlyAliveNode(aNode, c)) || aNode->InCCBlackTree()) &&
1717 !NeedsScriptTraverse(aNode);
1720 void FragmentOrElement::InitCCCallbacks() {
1721 nsCycleCollector_setForgetSkippableCallback(ClearCycleCollectorCleanupData);
1722 nsCycleCollector_setBeforeUnlinkCallback(ClearBlackMarkedNodes);
1725 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(FragmentOrElement)
1726 return FragmentOrElement::CanSkip(tmp, aRemovingAllowed);
1727 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1729 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(FragmentOrElement)
1730 return FragmentOrElement::CanSkipInCC(tmp);
1731 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1733 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(FragmentOrElement)
1734 return FragmentOrElement::CanSkipThis(tmp);
1735 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1737 // We purposefully don't TRAVERSE_BEGIN_INHERITED here. All the bits
1738 // we should traverse should be added here or in nsINode::Traverse.
1739 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement)
1740 if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
1741 char name[512];
1742 uint32_t nsid = tmp->GetNameSpaceID();
1743 nsAtomCString localName(tmp->NodeInfo()->NameAtom());
1744 nsAutoCString uri;
1745 if (tmp->OwnerDoc()->GetDocumentURI()) {
1746 uri = tmp->OwnerDoc()->GetDocumentURI()->GetSpecOrDefault();
1749 nsAutoString id;
1750 nsAtom* idAtom = tmp->GetID();
1751 if (idAtom) {
1752 id.AppendLiteral(" id='");
1753 id.Append(nsDependentAtomString(idAtom));
1754 id.Append('\'');
1757 nsAutoString classes;
1758 const nsAttrValue* classAttrValue =
1759 tmp->IsElement() ? tmp->AsElement()->GetClasses() : nullptr;
1760 if (classAttrValue) {
1761 classes.AppendLiteral(" class='");
1762 nsAutoString classString;
1763 classAttrValue->ToString(classString);
1764 classString.ReplaceChar(char16_t('\n'), char16_t(' '));
1765 classes.Append(classString);
1766 classes.Append('\'');
1769 nsAutoCString orphan;
1770 if (!tmp->IsInComposedDoc()) {
1771 orphan.AppendLiteral(" (orphan)");
1774 const char* nsuri = nsNameSpaceManager::GetNameSpaceDisplayName(nsid);
1775 SprintfLiteral(name, "FragmentOrElement %s %s%s%s%s %s", nsuri,
1776 localName.get(), NS_ConvertUTF16toUTF8(id).get(),
1777 NS_ConvertUTF16toUTF8(classes).get(), orphan.get(),
1778 uri.get());
1779 cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
1780 } else {
1781 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(FragmentOrElement, tmp->mRefCnt.get())
1784 if (!nsIContent::Traverse(tmp, cb)) {
1785 return NS_SUCCESS_INTERRUPTED_TRAVERSE;
1788 if (tmp->HasProperties()) {
1789 if (tmp->IsElement()) {
1790 Element* elem = tmp->AsElement();
1791 IntersectionObserverList* observers =
1792 static_cast<IntersectionObserverList*>(
1793 elem->GetProperty(nsGkAtoms::intersectionobserverlist));
1794 if (observers) {
1795 for (DOMIntersectionObserver* observer : observers->Keys()) {
1796 cb.NoteXPCOMChild(observer);
1801 if (tmp->IsElement()) {
1802 Element* element = tmp->AsElement();
1803 // Traverse attribute names.
1804 uint32_t i;
1805 uint32_t attrs = element->GetAttrCount();
1806 for (i = 0; i < attrs; i++) {
1807 const nsAttrName* name = element->GetUnsafeAttrNameAt(i);
1808 if (!name->IsAtom()) {
1809 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mAttrs[i]->NodeInfo()");
1810 cb.NoteNativeChild(name->NodeInfo(),
1811 NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo));
1815 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1817 NS_INTERFACE_MAP_BEGIN(FragmentOrElement)
1818 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(FragmentOrElement)
1819 NS_INTERFACE_MAP_END_INHERITING(nsIContent)
1821 //----------------------------------------------------------------------
1823 const nsTextFragment* FragmentOrElement::GetText() { return nullptr; }
1825 uint32_t FragmentOrElement::TextLength() const {
1826 // We can remove this assertion if it turns out to be useful to be able
1827 // to depend on this returning 0
1828 MOZ_ASSERT_UNREACHABLE("called FragmentOrElement::TextLength");
1830 return 0;
1833 bool FragmentOrElement::TextIsOnlyWhitespace() { return false; }
1835 bool FragmentOrElement::ThreadSafeTextIsOnlyWhitespace() const { return false; }
1837 static inline bool IsVoidTag(nsAtom* aTag) {
1838 static const nsAtom* voidElements[] = {
1839 nsGkAtoms::area, nsGkAtoms::base, nsGkAtoms::basefont,
1840 nsGkAtoms::bgsound, nsGkAtoms::br, nsGkAtoms::col,
1841 nsGkAtoms::embed, nsGkAtoms::frame, nsGkAtoms::hr,
1842 nsGkAtoms::img, nsGkAtoms::input, nsGkAtoms::keygen,
1843 nsGkAtoms::link, nsGkAtoms::meta, nsGkAtoms::param,
1844 nsGkAtoms::source, nsGkAtoms::track, nsGkAtoms::wbr};
1846 static mozilla::BitBloomFilter<12, nsAtom> sFilter;
1847 static bool sInitialized = false;
1848 if (!sInitialized) {
1849 sInitialized = true;
1850 for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
1851 sFilter.add(voidElements[i]);
1855 if (sFilter.mightContain(aTag)) {
1856 for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
1857 if (aTag == voidElements[i]) {
1858 return true;
1862 return false;
1865 /* static */
1866 bool FragmentOrElement::IsHTMLVoid(nsAtom* aLocalName) {
1867 return aLocalName && IsVoidTag(aLocalName);
1870 void FragmentOrElement::GetMarkup(bool aIncludeSelf, nsAString& aMarkup) {
1871 aMarkup.Truncate();
1873 Document* doc = OwnerDoc();
1874 if (IsInHTMLDocument()) {
1875 nsContentUtils::SerializeNodeToMarkup(this, !aIncludeSelf, aMarkup);
1876 return;
1879 nsAutoString contentType;
1880 doc->GetContentType(contentType);
1881 bool tryToCacheEncoder = !aIncludeSelf;
1883 nsCOMPtr<nsIDocumentEncoder> docEncoder = doc->GetCachedEncoder();
1884 if (!docEncoder) {
1885 docEncoder = do_createDocumentEncoder(
1886 PromiseFlatCString(NS_ConvertUTF16toUTF8(contentType)).get());
1888 if (!docEncoder) {
1889 // This could be some type for which we create a synthetic document. Try
1890 // again as XML
1891 contentType.AssignLiteral("application/xml");
1892 docEncoder = do_createDocumentEncoder("application/xml");
1893 // Don't try to cache the encoder since it would point to a different
1894 // contentType once it has been reinitialized.
1895 tryToCacheEncoder = false;
1898 NS_ENSURE_TRUE_VOID(docEncoder);
1900 uint32_t flags = nsIDocumentEncoder::OutputEncodeBasicEntities |
1901 // Output DOM-standard newlines
1902 nsIDocumentEncoder::OutputLFLineBreak |
1903 // Don't do linebreaking that's not present in
1904 // the source
1905 nsIDocumentEncoder::OutputRaw |
1906 // Only check for mozdirty when necessary (bug 599983)
1907 nsIDocumentEncoder::OutputIgnoreMozDirty;
1909 if (IsEditable()) {
1910 nsCOMPtr<Element> elem = do_QueryInterface(this);
1911 TextEditor* textEditor = elem ? elem->GetTextEditorInternal() : nullptr;
1912 if (textEditor && textEditor->OutputsMozDirty()) {
1913 flags &= ~nsIDocumentEncoder::OutputIgnoreMozDirty;
1917 DebugOnly<nsresult> rv = docEncoder->NativeInit(doc, contentType, flags);
1918 MOZ_ASSERT(NS_SUCCEEDED(rv));
1920 if (aIncludeSelf) {
1921 docEncoder->SetNode(this);
1922 } else {
1923 docEncoder->SetContainerNode(this);
1925 rv = docEncoder->EncodeToString(aMarkup);
1926 MOZ_ASSERT(NS_SUCCEEDED(rv));
1927 if (tryToCacheEncoder) {
1928 doc->SetCachedEncoder(docEncoder.forget());
1932 static bool ContainsMarkup(const nsAString& aStr) {
1933 // Note: we can't use FindCharInSet because null is one of the characters we
1934 // want to search for.
1935 const char16_t* start = aStr.BeginReading();
1936 const char16_t* end = aStr.EndReading();
1938 while (start != end) {
1939 char16_t c = *start;
1940 if (c == char16_t('<') || c == char16_t('&') || c == char16_t('\r') ||
1941 c == char16_t('\0')) {
1942 return true;
1944 ++start;
1947 return false;
1950 void FragmentOrElement::SetInnerHTMLInternal(const nsAString& aInnerHTML,
1951 ErrorResult& aError) {
1952 // Keep "this" alive should be guaranteed by the caller, and also the content
1953 // of a template element (if this is one) should never been released by from
1954 // this during this call. Therefore, using raw pointer here is safe.
1955 FragmentOrElement* target = this;
1956 // Handle template case.
1957 if (target->IsTemplateElement()) {
1958 DocumentFragment* frag =
1959 static_cast<HTMLTemplateElement*>(target)->Content();
1960 MOZ_ASSERT(frag);
1961 target = frag;
1963 // Fast-path for strings with no markup. Limit this to short strings, to
1964 // avoid ContainsMarkup taking too long. The choice for 100 is based on
1965 // gut feeling.
1967 // Don't do this for elements with a weird parser insertion mode, for
1968 // instance setting innerHTML = "" on a <html> element should add the
1969 // optional <head> and <body> elements.
1970 if (!target->HasWeirdParserInsertionMode() && aInnerHTML.Length() < 100 &&
1971 !ContainsMarkup(aInnerHTML)) {
1972 aError = nsContentUtils::SetNodeTextContent(target, aInnerHTML, false);
1973 return;
1976 // mozAutoSubtreeModified keeps the owner document alive. Therefore, using a
1977 // raw pointer here is safe.
1978 Document* const doc = target->OwnerDoc();
1980 // Batch possible DOMSubtreeModified events.
1981 mozAutoSubtreeModified subtree(doc, nullptr);
1983 target->FireNodeRemovedForChildren();
1985 // Needed when innerHTML is used in combination with contenteditable
1986 mozAutoDocUpdate updateBatch(doc, true);
1988 // Remove childnodes.
1989 nsAutoMutationBatch mb(target, true, false);
1990 while (target->HasChildren()) {
1991 target->RemoveChildNode(target->GetFirstChild(), true);
1993 mb.RemovalDone();
1995 nsAutoScriptLoaderDisabler sld(doc);
1997 FragmentOrElement* parseContext = this;
1998 if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(this)) {
1999 // Fix up the context to be the host of the ShadowRoot. See
2000 // https://w3c.github.io/DOM-Parsing/#dom-innerhtml-innerhtml setter step 1.
2001 parseContext = shadowRoot->GetHost();
2004 if (doc->IsHTMLDocument()) {
2005 doc->SuspendDOMNotifications();
2006 nsAtom* contextLocalName = parseContext->NodeInfo()->NameAtom();
2007 int32_t contextNameSpaceID = parseContext->GetNameSpaceID();
2009 int32_t oldChildCount = target->GetChildCount();
2010 aError = nsContentUtils::ParseFragmentHTML(
2011 aInnerHTML, target, contextLocalName, contextNameSpaceID,
2012 doc->GetCompatibilityMode() == eCompatibility_NavQuirks, true);
2013 doc->ResumeDOMNotifications();
2014 if (target->GetFirstChild()) {
2015 MutationObservers::NotifyContentAppended(target, target->GetFirstChild());
2017 mb.NodesAdded();
2018 // HTML5 parser has notified, but not fired mutation events.
2019 nsContentUtils::FireMutationEventsForDirectParsing(doc, target,
2020 oldChildCount);
2021 } else {
2022 RefPtr<DocumentFragment> df = nsContentUtils::CreateContextualFragment(
2023 parseContext, aInnerHTML, true, aError);
2024 if (!aError.Failed()) {
2025 // Suppress assertion about node removal mutation events that can't have
2026 // listeners anyway, because no one has had the chance to register
2027 // mutation listeners on the fragment that comes from the parser.
2028 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
2030 target->AppendChild(*df, aError);
2031 mb.NodesAdded();
2036 void FragmentOrElement::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
2037 size_t* aNodeSize) const {
2038 nsIContent::AddSizeOfExcludingThis(aSizes, aNodeSize);
2040 nsDOMSlots* slots = GetExistingDOMSlots();
2041 if (slots) {
2042 *aNodeSize += slots->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf);