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