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/. */
6 
7 /*
8  * Base class for all element classes and DocumentFragment.
9  */
10 
11 #include "mozilla/ArrayUtils.h"
12 #include "mozilla/Likely.h"
13 #include "mozilla/MemoryReporting.h"
14 #include "mozilla/StaticPtr.h"
15 
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/EventStates.h"
24 #include "mozilla/HTMLEditor.h"
25 #include "mozilla/PresShell.h"
26 #include "mozilla/RestyleManager.h"
27 #include "mozilla/TextEditor.h"
28 #include "mozilla/TouchEvents.h"
29 #include "mozilla/URLExtraData.h"
30 #include "mozilla/dom/Attr.h"
31 #include "nsDOMAttributeMap.h"
32 #include "nsAtom.h"
33 #include "mozilla/dom/NodeInfo.h"
34 #include "mozilla/dom/Event.h"
35 #include "mozilla/dom/ScriptLoader.h"
36 #include "mozilla/dom/TouchEvent.h"
37 #include "mozilla/dom/CustomElementRegistry.h"
38 #include "mozilla/dom/Document.h"
39 #include "mozilla/dom/DocumentInlines.h"
40 #include "nsIControllers.h"
41 #include "nsIDocumentEncoder.h"
42 #include "nsFocusManager.h"
43 #include "nsIScriptGlobalObject.h"
44 #include "nsNetUtil.h"
45 #include "nsIFrame.h"
46 #include "nsIAnonymousContentCreator.h"
47 #include "nsPresContext.h"
48 #include "nsStyleConsts.h"
49 #include "nsString.h"
50 #include "nsUnicharUtils.h"
51 #include "nsDOMCID.h"
52 #include "nsDOMCSSAttrDeclaration.h"
53 #include "nsNameSpaceManager.h"
54 #include "nsContentList.h"
55 #include "nsDOMTokenList.h"
56 #include "nsError.h"
57 #include "nsDOMString.h"
58 #include "nsXULElement.h"
59 #include "mozilla/InternalMutationEvent.h"
60 #include "mozilla/MouseEvents.h"
61 #include "nsAttrValueOrString.h"
62 #include "nsQueryObject.h"
63 #include "nsFrameSelection.h"
64 #ifdef DEBUG
65 #  include "nsRange.h"
66 #endif
67 
68 #include "nsFrameLoader.h"
69 #include "nsPIDOMWindow.h"
70 #include "nsLayoutUtils.h"
71 #include "nsGkAtoms.h"
72 #include "nsContentUtils.h"
73 #include "nsTextFragment.h"
74 #include "nsContentCID.h"
75 #include "nsWindowSizes.h"
76 
77 #include "nsIWidget.h"
78 
79 #include "nsNodeInfoManager.h"
80 #include "nsGenericHTMLElement.h"
81 #include "nsContentCreatorFunctions.h"
82 #include "nsView.h"
83 #include "nsViewManager.h"
84 #include "nsIScrollableFrame.h"
85 #include "ChildIterator.h"
86 #include "nsTextNode.h"
87 #include "mozilla/dom/NodeListBinding.h"
88 
89 #include "nsCCUncollectableMarker.h"
90 
91 #include "mozAutoDocUpdate.h"
92 
93 #include "mozilla/Sprintf.h"
94 #include "nsDOMMutationObserver.h"
95 #include "nsWrapperCacheInlines.h"
96 #include "nsCycleCollector.h"
97 #include "xpcpublic.h"
98 #include "mozilla/Telemetry.h"
99 
100 #include "mozilla/CORSMode.h"
101 
102 #include "mozilla/dom/ShadowRoot.h"
103 #include "mozilla/dom/HTMLSlotElement.h"
104 #include "mozilla/dom/HTMLTemplateElement.h"
105 #include "mozilla/dom/SVGUseElement.h"
106 
107 #include "nsStyledElement.h"
108 #include "nsIContentInlines.h"
109 #include "nsChildContentList.h"
110 #include "mozilla/BloomFilter.h"
111 
112 #include "NodeUbiReporting.h"
113 
114 using namespace mozilla;
115 using namespace mozilla::dom;
116 
117 int32_t nsIContent::sTabFocusModel = eTabFocus_any;
118 bool nsIContent::sTabFocusModelAppliesToXUL = false;
119 uint64_t nsMutationGuard::sGeneration = 0;
120 
121 NS_IMPL_CYCLE_COLLECTION_CLASS(nsIContent)
122 
123 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsIContent)
124   MOZ_ASSERT_UNREACHABLE("Our subclasses don't call us");
125 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
126 
127 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsIContent)
128   MOZ_ASSERT_UNREACHABLE("Our subclasses don't call us");
129 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
130 
NS_INTERFACE_MAP_BEGIN(nsIContent)131 NS_INTERFACE_MAP_BEGIN(nsIContent)
132   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
133   // Don't bother to QI to cycle collection, because our CC impl is
134   // not doing anything anyway.
135   NS_INTERFACE_MAP_ENTRY(nsIContent)
136   NS_INTERFACE_MAP_ENTRY(nsINode)
137   NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
138   NS_INTERFACE_MAP_ENTRY_TEAROFF(nsISupportsWeakReference,
139                                  new nsNodeSupportsWeakRefTearoff(this))
140   // DOM bindings depend on the identity pointer being the
141   // same as nsINode (which nsIContent inherits).
142   NS_INTERFACE_MAP_ENTRY(nsISupports)
143 NS_INTERFACE_MAP_END
144 
145 NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_ADDREF(nsIContent)
146 
147 NS_IMPL_DOMARENA_DESTROY(nsIContent)
148 
149 NS_IMPL_MAIN_THREAD_ONLY_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE_AND_DESTROY(
150     nsIContent, LastRelease(), Destroy())
151 
152 nsIContent* nsIContent::FindFirstNonChromeOnlyAccessContent() const {
153   // This handles also nested native anonymous content.
154   for (const nsIContent* content = this; content;
155        content = content->GetChromeOnlyAccessSubtreeRootParent()) {
156     if (!content->ChromeOnlyAccess()) {
157       // Oops, this function signature allows casting const to
158       // non-const.  (Then again, so does GetFirstChild()->GetParent().)
159       return const_cast<nsIContent*>(content);
160     }
161   }
162   return nullptr;
163 }
164 
165 // https://dom.spec.whatwg.org/#dom-slotable-assignedslot
GetAssignedSlotByMode() const166 HTMLSlotElement* nsIContent::GetAssignedSlotByMode() const {
167   /**
168    * Get slotable's assigned slot for the result of
169    * find a slot with open flag UNSET [1].
170    *
171    * [1] https://dom.spec.whatwg.org/#assign-a-slot
172    */
173   HTMLSlotElement* slot = GetAssignedSlot();
174   if (!slot) {
175     return nullptr;
176   }
177 
178   MOZ_ASSERT(GetParent());
179   MOZ_ASSERT(GetParent()->GetShadowRoot());
180 
181   /**
182    * Additional check for open flag SET:
183    *   If slotable’s parent’s shadow root's mode is not "open",
184    *   then return null.
185    */
186   if (GetParent()->GetShadowRoot()->IsClosed()) {
187     return nullptr;
188   }
189 
190   return slot;
191 }
192 
GetDesiredIMEState()193 nsIContent::IMEState nsIContent::GetDesiredIMEState() {
194   if (!IsEditable()) {
195     // Check for the special case where we're dealing with elements which don't
196     // have the editable flag set, but are readwrite (such as text controls).
197     if (!IsElement() ||
198         !AsElement()->State().HasState(NS_EVENT_STATE_READWRITE)) {
199       return IMEState(IMEEnabled::Disabled);
200     }
201   }
202   // NOTE: The content for independent editors (e.g., input[type=text],
203   // textarea) must override this method, so, we don't need to worry about
204   // that here.
205   nsIContent* editableAncestor = GetEditingHost();
206 
207   // This is in another editable content, use the result of it.
208   if (editableAncestor && editableAncestor != this) {
209     return editableAncestor->GetDesiredIMEState();
210   }
211   Document* doc = GetComposedDoc();
212   if (!doc) {
213     return IMEState(IMEEnabled::Disabled);
214   }
215   nsPresContext* pc = doc->GetPresContext();
216   if (!pc) {
217     return IMEState(IMEEnabled::Disabled);
218   }
219   HTMLEditor* htmlEditor = nsContentUtils::GetHTMLEditor(pc);
220   if (!htmlEditor) {
221     return IMEState(IMEEnabled::Disabled);
222   }
223   IMEState state;
224   htmlEditor->GetPreferredIMEState(&state);
225   return state;
226 }
227 
HasIndependentSelection() const228 bool nsIContent::HasIndependentSelection() const {
229   nsIFrame* frame = GetPrimaryFrame();
230   return (frame && frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION);
231 }
232 
GetEditingHost()233 dom::Element* nsIContent::GetEditingHost() {
234   // If this isn't editable, return nullptr.
235   if (!IsEditable()) {
236     return nullptr;
237   }
238 
239   Document* doc = GetComposedDoc();
240   if (!doc) {
241     return nullptr;
242   }
243 
244   // If this is in designMode, we should return <body>
245   if (IsInDesignMode() && !IsInShadowTree()) {
246     return doc->GetBodyElement();
247   }
248 
249   dom::Element* editableParentElement = nullptr;
250   for (dom::Element* parent = GetParentElement();
251        parent && parent->HasFlag(NODE_IS_EDITABLE);
252        parent = editableParentElement->GetParentElement()) {
253     editableParentElement = parent;
254   }
255   return editableParentElement ? editableParentElement
256                                : dom::Element::FromNode(this);
257 }
258 
LookupNamespaceURIInternal(const nsAString & aNamespacePrefix,nsAString & aNamespaceURI) const259 nsresult nsIContent::LookupNamespaceURIInternal(
260     const nsAString& aNamespacePrefix, nsAString& aNamespaceURI) const {
261   if (aNamespacePrefix.EqualsLiteral("xml")) {
262     // Special-case for xml prefix
263     aNamespaceURI.AssignLiteral("http://www.w3.org/XML/1998/namespace");
264     return NS_OK;
265   }
266 
267   if (aNamespacePrefix.EqualsLiteral("xmlns")) {
268     // Special-case for xmlns prefix
269     aNamespaceURI.AssignLiteral("http://www.w3.org/2000/xmlns/");
270     return NS_OK;
271   }
272 
273   RefPtr<nsAtom> name;
274   if (!aNamespacePrefix.IsEmpty()) {
275     name = NS_Atomize(aNamespacePrefix);
276     NS_ENSURE_TRUE(name, NS_ERROR_OUT_OF_MEMORY);
277   } else {
278     name = nsGkAtoms::xmlns;
279   }
280   // Trace up the content parent chain looking for the namespace
281   // declaration that declares aNamespacePrefix.
282   for (Element* element = GetAsElementOrParentElement(); element;
283        element = element->GetParentElement()) {
284     if (element->GetAttr(kNameSpaceID_XMLNS, name, aNamespaceURI)) {
285       return NS_OK;
286     }
287   }
288   return NS_ERROR_FAILURE;
289 }
290 
GetLang() const291 nsAtom* nsIContent::GetLang() const {
292   for (const Element* element = GetAsElementOrParentElement(); element;
293        element = element->GetParentElement()) {
294     if (!element->GetAttrCount()) {
295       continue;
296     }
297 
298     // xml:lang has precedence over lang on HTML elements (see
299     // XHTML1 section C.7).
300     const nsAttrValue* attr =
301         element->GetParsedAttr(nsGkAtoms::lang, kNameSpaceID_XML);
302     if (!attr && element->SupportsLangAttr()) {
303       attr = element->GetParsedAttr(nsGkAtoms::lang);
304     }
305     if (attr) {
306       MOZ_ASSERT(attr->Type() == nsAttrValue::eAtom);
307       MOZ_ASSERT(attr->GetAtomValue());
308       return attr->GetAtomValue();
309     }
310   }
311 
312   return nullptr;
313 }
314 
GetBaseURI(bool aTryUseXHRDocBaseURI) const315 nsIURI* nsIContent::GetBaseURI(bool aTryUseXHRDocBaseURI) const {
316   if (SVGUseElement* use = GetContainingSVGUseShadowHost()) {
317     if (URLExtraData* data = use->GetContentURLData()) {
318       return data->BaseURI();
319     }
320   }
321 
322   return OwnerDoc()->GetBaseURI(aTryUseXHRDocBaseURI);
323 }
324 
GetBaseURIForStyleAttr() const325 nsIURI* nsIContent::GetBaseURIForStyleAttr() const {
326   if (SVGUseElement* use = GetContainingSVGUseShadowHost()) {
327     if (URLExtraData* data = use->GetContentURLData()) {
328       return data->BaseURI();
329     }
330   }
331   // This also ignores the case that SVG inside XBL binding.
332   // But it is probably fine.
333   return OwnerDoc()->GetDocBaseURI();
334 }
335 
GetURLDataForStyleAttr(nsIPrincipal * aSubjectPrincipal) const336 already_AddRefed<URLExtraData> nsIContent::GetURLDataForStyleAttr(
337     nsIPrincipal* aSubjectPrincipal) const {
338   if (SVGUseElement* use = GetContainingSVGUseShadowHost()) {
339     if (URLExtraData* data = use->GetContentURLData()) {
340       return do_AddRef(data);
341     }
342   }
343   if (aSubjectPrincipal && aSubjectPrincipal != NodePrincipal()) {
344     // TODO: Cache this?
345     nsCOMPtr<nsIReferrerInfo> referrerInfo =
346         ReferrerInfo::CreateForInternalCSSResources(OwnerDoc());
347     return MakeAndAddRef<URLExtraData>(OwnerDoc()->GetDocBaseURI(),
348                                        referrerInfo, aSubjectPrincipal);
349   }
350   // This also ignores the case that SVG inside XBL binding.
351   // But it is probably fine.
352   return do_AddRef(OwnerDoc()->DefaultStyleAttrURLData());
353 }
354 
ConstructUbiNode(void * storage)355 void nsIContent::ConstructUbiNode(void* storage) {
356   JS::ubi::Concrete<nsIContent>::construct(storage, this);
357 }
358 
359 //----------------------------------------------------------------------
360 
GetJSObjectChild(nsWrapperCache * aCache)361 static inline JSObject* GetJSObjectChild(nsWrapperCache* aCache) {
362   return aCache->PreservingWrapper() ? aCache->GetWrapperPreserveColor()
363                                      : nullptr;
364 }
365 
NeedsScriptTraverse(nsINode * aNode)366 static bool NeedsScriptTraverse(nsINode* aNode) {
367   return aNode->PreservingWrapper() && aNode->GetWrapperPreserveColor() &&
368          !aNode->HasKnownLiveWrapperAndDoesNotNeedTracing(aNode);
369 }
370 
371 //----------------------------------------------------------------------
372 
373 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsAttrChildContentList)
374 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsAttrChildContentList)
375 
376 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsAttrChildContentList, mNode)
377 
378 // If the wrapper is known-live, the list can't be part of a garbage cycle.
379 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsAttrChildContentList)
380   return tmp->HasKnownLiveWrapper();
381 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
382 
383 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsAttrChildContentList)
384   return tmp->HasKnownLiveWrapperAndDoesNotNeedTracing(tmp);
385 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
386 
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsAttrChildContentList)387 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsAttrChildContentList)
388 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
389 
390 NS_INTERFACE_TABLE_HEAD(nsAttrChildContentList)
391   NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
392   NS_INTERFACE_TABLE(nsAttrChildContentList, nsINodeList)
393   NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsAttrChildContentList)
394 NS_INTERFACE_MAP_END
395 
396 JSObject* nsAttrChildContentList::WrapObject(
397     JSContext* cx, JS::Handle<JSObject*> aGivenProto) {
398   return NodeList_Binding::Wrap(cx, this, aGivenProto);
399 }
400 
Length()401 uint32_t nsAttrChildContentList::Length() {
402   return mNode ? mNode->GetChildCount() : 0;
403 }
404 
Item(uint32_t aIndex)405 nsIContent* nsAttrChildContentList::Item(uint32_t aIndex) {
406   if (mNode) {
407     return mNode->GetChildAt_Deprecated(aIndex);
408   }
409 
410   return nullptr;
411 }
412 
IndexOf(nsIContent * aContent)413 int32_t nsAttrChildContentList::IndexOf(nsIContent* aContent) {
414   if (mNode) {
415     return mNode->ComputeIndexOf_Deprecated(aContent);
416   }
417 
418   return -1;
419 }
420 
421 //----------------------------------------------------------------------
Length()422 uint32_t nsParentNodeChildContentList::Length() {
423   if (!mIsCacheValid && !ValidateCache()) {
424     return 0;
425   }
426 
427   MOZ_ASSERT(mIsCacheValid);
428 
429   return mCachedChildArray.Length();
430 }
431 
Item(uint32_t aIndex)432 nsIContent* nsParentNodeChildContentList::Item(uint32_t aIndex) {
433   if (!mIsCacheValid && !ValidateCache()) {
434     return nullptr;
435   }
436 
437   MOZ_ASSERT(mIsCacheValid);
438 
439   return mCachedChildArray.SafeElementAt(aIndex, nullptr);
440 }
441 
IndexOf(nsIContent * aContent)442 int32_t nsParentNodeChildContentList::IndexOf(nsIContent* aContent) {
443   if (!mIsCacheValid && !ValidateCache()) {
444     return -1;
445   }
446 
447   MOZ_ASSERT(mIsCacheValid);
448 
449   return mCachedChildArray.IndexOf(aContent);
450 }
451 
ValidateCache()452 bool nsParentNodeChildContentList::ValidateCache() {
453   MOZ_ASSERT(!mIsCacheValid);
454   MOZ_ASSERT(mCachedChildArray.IsEmpty());
455 
456   nsINode* parent = GetParentObject();
457   if (!parent) {
458     return false;
459   }
460 
461   for (nsIContent* node = parent->GetFirstChild(); node;
462        node = node->GetNextSibling()) {
463     mCachedChildArray.AppendElement(node);
464   }
465   mIsCacheValid = true;
466 
467   return true;
468 }
469 
470 //----------------------------------------------------------------------
471 
Children()472 nsIHTMLCollection* FragmentOrElement::Children() {
473   nsDOMSlots* slots = DOMSlots();
474 
475   if (!slots->mChildrenList) {
476     slots->mChildrenList =
477         new nsContentList(this, kNameSpaceID_Wildcard, nsGkAtoms::_asterisk,
478                           nsGkAtoms::_asterisk, false);
479   }
480 
481   return slots->mChildrenList;
482 }
483 
484 //----------------------------------------------------------------------
485 
NS_IMPL_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff,mNode)486 NS_IMPL_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff, mNode)
487 
488 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff)
489   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
490 NS_INTERFACE_MAP_END_AGGREGATED(mNode)
491 
492 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNodeSupportsWeakRefTearoff)
493 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNodeSupportsWeakRefTearoff)
494 
495 NS_IMETHODIMP
496 nsNodeSupportsWeakRefTearoff::GetWeakReference(
497     nsIWeakReference** aInstancePtr) {
498   nsINode::nsSlots* slots = mNode->Slots();
499   if (!slots->mWeakReference) {
500     slots->mWeakReference = new nsNodeWeakReference(mNode);
501   }
502 
503   NS_ADDREF(*aInstancePtr = slots->mWeakReference);
504 
505   return NS_OK;
506 }
507 
508 //----------------------------------------------------------------------
509 
510 static const size_t MaxDOMSlotSizeAllowed =
511 #ifdef HAVE_64BIT_BUILD
512     128;
513 #else
514     64;
515 #endif
516 
517 static_assert(sizeof(nsINode::nsSlots) <= MaxDOMSlotSizeAllowed,
518               "DOM slots cannot be grown without consideration");
519 static_assert(sizeof(FragmentOrElement::nsDOMSlots) <= MaxDOMSlotSizeAllowed,
520               "DOM slots cannot be grown without consideration");
521 
UnlinkExtendedSlots()522 void nsIContent::nsExtendedContentSlots::UnlinkExtendedSlots() {
523   mContainingShadow = nullptr;
524   mAssignedSlot = nullptr;
525 }
526 
TraverseExtendedSlots(nsCycleCollectionTraversalCallback & aCb)527 void nsIContent::nsExtendedContentSlots::TraverseExtendedSlots(
528     nsCycleCollectionTraversalCallback& aCb) {
529   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mContainingShadow");
530   aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mContainingShadow));
531 
532   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mAssignedSlot");
533   aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mAssignedSlot.get()));
534 }
535 
536 nsIContent::nsExtendedContentSlots::nsExtendedContentSlots() = default;
537 
~nsExtendedContentSlots()538 nsIContent::nsExtendedContentSlots::~nsExtendedContentSlots() {
539   MOZ_ASSERT(!mManualSlotAssignment);
540 }
541 
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const542 size_t nsIContent::nsExtendedContentSlots::SizeOfExcludingThis(
543     MallocSizeOf aMallocSizeOf) const {
544   // For now, nothing to measure here.  We don't actually own any of our
545   // members.
546   return 0;
547 }
548 
nsDOMSlots()549 FragmentOrElement::nsDOMSlots::nsDOMSlots()
550     : nsIContent::nsContentSlots(), mDataset(nullptr) {
551   MOZ_COUNT_CTOR(nsDOMSlots);
552 }
553 
~nsDOMSlots()554 FragmentOrElement::nsDOMSlots::~nsDOMSlots() {
555   MOZ_COUNT_DTOR(nsDOMSlots);
556 
557   if (mAttributeMap) {
558     mAttributeMap->DropReference();
559   }
560 }
561 
Traverse(nsCycleCollectionTraversalCallback & aCb)562 void FragmentOrElement::nsDOMSlots::Traverse(
563     nsCycleCollectionTraversalCallback& aCb) {
564   nsIContent::nsContentSlots::Traverse(aCb);
565 
566   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mStyle");
567   aCb.NoteXPCOMChild(mStyle.get());
568 
569   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mAttributeMap");
570   aCb.NoteXPCOMChild(mAttributeMap.get());
571 
572   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mChildrenList");
573   aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsINodeList*, mChildrenList));
574 
575   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mClassList");
576   aCb.NoteXPCOMChild(mClassList.get());
577 
578   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mSlots->mPart");
579   aCb.NoteXPCOMChild(mPart.get());
580 }
581 
Unlink()582 void FragmentOrElement::nsDOMSlots::Unlink() {
583   nsIContent::nsContentSlots::Unlink();
584   mStyle = nullptr;
585   if (mAttributeMap) {
586     mAttributeMap->DropReference();
587     mAttributeMap = nullptr;
588   }
589   mChildrenList = nullptr;
590   mClassList = nullptr;
591   mPart = nullptr;
592 }
593 
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const594 size_t FragmentOrElement::nsDOMSlots::SizeOfIncludingThis(
595     MallocSizeOf aMallocSizeOf) const {
596   size_t n = aMallocSizeOf(this);
597 
598   nsExtendedContentSlots* extendedSlots = GetExtendedContentSlots();
599   if (extendedSlots) {
600     if (OwnsExtendedSlots()) {
601       n += aMallocSizeOf(extendedSlots);
602     }
603 
604     n += extendedSlots->SizeOfExcludingThis(aMallocSizeOf);
605   }
606 
607   if (mAttributeMap) {
608     n += mAttributeMap->SizeOfIncludingThis(aMallocSizeOf);
609   }
610 
611   if (mChildrenList) {
612     n += mChildrenList->SizeOfIncludingThis(aMallocSizeOf);
613   }
614 
615   // Measurement of the following members may be added later if DMD finds it is
616   // worthwhile:
617   // - Superclass members (nsINode::nsSlots)
618   // - mStyle
619   // - mDataSet
620   // - mClassList
621 
622   // The following member are not measured:
623   // - mControllers: because it is non-owning
624   return n;
625 }
626 
627 FragmentOrElement::nsExtendedDOMSlots::nsExtendedDOMSlots() = default;
628 
629 FragmentOrElement::nsExtendedDOMSlots::~nsExtendedDOMSlots() = default;
630 
UnlinkExtendedSlots()631 void FragmentOrElement::nsExtendedDOMSlots::UnlinkExtendedSlots() {
632   nsIContent::nsExtendedContentSlots::UnlinkExtendedSlots();
633 
634   // Don't clear mXBLBinding, it'll be done in
635   // BindingManager::RemovedFromDocument from FragmentOrElement::Unlink.
636   //
637   // mShadowRoot will similarly be cleared explicitly from
638   // FragmentOrElement::Unlink.
639   mSMILOverrideStyle = nullptr;
640   mControllers = nullptr;
641   mLabelsList = nullptr;
642   if (mCustomElementData) {
643     mCustomElementData->Unlink();
644     mCustomElementData = nullptr;
645   }
646 }
647 
TraverseExtendedSlots(nsCycleCollectionTraversalCallback & aCb)648 void FragmentOrElement::nsExtendedDOMSlots::TraverseExtendedSlots(
649     nsCycleCollectionTraversalCallback& aCb) {
650   nsIContent::nsExtendedContentSlots::TraverseExtendedSlots(aCb);
651 
652   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mSMILOverrideStyle");
653   aCb.NoteXPCOMChild(mSMILOverrideStyle.get());
654 
655   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mControllers");
656   aCb.NoteXPCOMChild(mControllers);
657 
658   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mLabelsList");
659   aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsINodeList*, mLabelsList));
660 
661   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "mExtendedSlots->mShadowRoot");
662   aCb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mShadowRoot));
663 
664   if (mCustomElementData) {
665     mCustomElementData->Traverse(aCb);
666   }
667 }
668 
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const669 size_t FragmentOrElement::nsExtendedDOMSlots::SizeOfExcludingThis(
670     MallocSizeOf aMallocSizeOf) const {
671   size_t n =
672       nsIContent::nsExtendedContentSlots::SizeOfExcludingThis(aMallocSizeOf);
673 
674   // We own mSMILOverrideStyle but there seems to be no memory reporting on CSS
675   // declarations?  At least report the memory the declaration takes up
676   // directly.
677   if (mSMILOverrideStyle) {
678     n += aMallocSizeOf(mSMILOverrideStyle);
679   }
680 
681   // We don't really own mSMILOverrideStyleDeclaration.  mSMILOverrideStyle owns
682   // it.
683 
684   // We don't seem to have memory reporting for nsXULControllers.  At least
685   // report the memory it's using directly.
686   if (mControllers) {
687     n += aMallocSizeOf(mControllers);
688   }
689 
690   if (mLabelsList) {
691     n += mLabelsList->SizeOfIncludingThis(aMallocSizeOf);
692   }
693 
694   // mShadowRoot should be handled during normal DOM tree memory reporting, just
695   // like kids, siblings, etc.
696 
697   if (mCustomElementData) {
698     n += mCustomElementData->SizeOfIncludingThis(aMallocSizeOf);
699   }
700 
701   return n;
702 }
703 
FragmentOrElement(already_AddRefed<mozilla::dom::NodeInfo> && aNodeInfo)704 FragmentOrElement::FragmentOrElement(
705     already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
706     : nsIContent(std::move(aNodeInfo)) {}
707 
~FragmentOrElement()708 FragmentOrElement::~FragmentOrElement() {
709   MOZ_ASSERT(!IsInUncomposedDoc(),
710              "Please remove this from the document properly");
711   if (GetParent()) {
712     NS_RELEASE(mParent);
713   }
714 }
715 
GetChildren(uint32_t aFilter)716 already_AddRefed<nsINodeList> FragmentOrElement::GetChildren(uint32_t aFilter) {
717   RefPtr<nsSimpleContentList> list = new nsSimpleContentList(this);
718   AllChildrenIterator iter(this, aFilter);
719   while (nsIContent* kid = iter.GetNextChild()) {
720     list->AppendElement(kid);
721   }
722 
723   return list.forget();
724 }
725 
FindChromeAccessOnlySubtreeOwner(nsINode * aNode)726 static nsINode* FindChromeAccessOnlySubtreeOwner(nsINode* aNode) {
727   if (!aNode->ChromeOnlyAccess()) {
728     return aNode;
729   }
730 
731   while (aNode && !aNode->IsRootOfChromeAccessOnlySubtree()) {
732     aNode = aNode->GetParentNode();
733   }
734 
735   return aNode ? aNode->GetParentOrShadowHostNode() : nullptr;
736 }
737 
FindChromeAccessOnlySubtreeOwner(EventTarget * aTarget)738 already_AddRefed<nsINode> FindChromeAccessOnlySubtreeOwner(
739     EventTarget* aTarget) {
740   nsCOMPtr<nsINode> node = nsINode::FromEventTargetOrNull(aTarget);
741   if (!node || !node->ChromeOnlyAccess()) {
742     return node.forget();
743   }
744 
745   node = FindChromeAccessOnlySubtreeOwner(node);
746   return node.forget();
747 }
748 
GetEventTargetParent(EventChainPreVisitor & aVisitor)749 void nsIContent::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
750   // FIXME! Document how this event retargeting works, Bug 329124.
751   aVisitor.mCanHandle = true;
752   aVisitor.mMayHaveListenerManager = HasListenerManager();
753 
754   if (IsInShadowTree()) {
755     aVisitor.mItemInShadowTree = true;
756   }
757 
758   // Don't propagate mouseover and mouseout events when mouse is moving
759   // inside chrome access only content.
760   bool isAnonForEvents = IsRootOfChromeAccessOnlySubtree();
761   aVisitor.mRootOfClosedTree = isAnonForEvents;
762   if ((aVisitor.mEvent->mMessage == eMouseOver ||
763        aVisitor.mEvent->mMessage == eMouseOut ||
764        aVisitor.mEvent->mMessage == ePointerOver ||
765        aVisitor.mEvent->mMessage == ePointerOut) &&
766       // Check if we should stop event propagation when event has just been
767       // dispatched or when we're about to propagate from
768       // chrome access only subtree or if we are about to propagate out of
769       // a shadow root to a shadow root host.
770       ((this == aVisitor.mEvent->mOriginalTarget && !ChromeOnlyAccess()) ||
771        isAnonForEvents)) {
772     nsCOMPtr<nsIContent> relatedTarget = nsIContent::FromEventTargetOrNull(
773         aVisitor.mEvent->AsMouseEvent()->mRelatedTarget);
774     if (relatedTarget && relatedTarget->OwnerDoc() == OwnerDoc()) {
775       // If current target is anonymous for events or we know that related
776       // target is descendant of an element which is anonymous for events,
777       // we may want to stop event propagation.
778       // If this is the original target, aVisitor.mRelatedTargetIsInAnon
779       // must be updated.
780       if (isAnonForEvents || aVisitor.mRelatedTargetIsInAnon ||
781           (aVisitor.mEvent->mOriginalTarget == this &&
782            (aVisitor.mRelatedTargetIsInAnon =
783                 relatedTarget->ChromeOnlyAccess()))) {
784         nsINode* anonOwner = FindChromeAccessOnlySubtreeOwner(this);
785         if (anonOwner) {
786           nsINode* anonOwnerRelated =
787               FindChromeAccessOnlySubtreeOwner(relatedTarget);
788           if (anonOwnerRelated) {
789             // Note, anonOwnerRelated may still be inside some other
790             // native anonymous subtree. The case where anonOwner is still
791             // inside native anonymous subtree will be handled when event
792             // propagates up in the DOM tree.
793             while (anonOwner != anonOwnerRelated &&
794                    anonOwnerRelated->ChromeOnlyAccess()) {
795               anonOwnerRelated =
796                   FindChromeAccessOnlySubtreeOwner(anonOwnerRelated);
797             }
798             if (anonOwner == anonOwnerRelated) {
799 #ifdef DEBUG_smaug
800               nsCOMPtr<nsIContent> originalTarget =
801                   nsIContent::FromEventTargetOrNull(
802                       aVisitor.mEvent->mOriginalTarget);
803               nsAutoString ot, ct, rt;
804               if (originalTarget) {
805                 originalTarget->NodeInfo()->NameAtom()->ToString(ot);
806               }
807               NodeInfo()->NameAtom()->ToString(ct);
808               relatedTarget->NodeInfo()->NameAtom()->ToString(rt);
809               printf(
810                   "Stopping %s propagation:"
811                   "\n\toriginalTarget=%s \n\tcurrentTarget=%s %s"
812                   "\n\trelatedTarget=%s %s \n%s",
813                   (aVisitor.mEvent->mMessage == eMouseOver) ? "mouseover"
814                                                             : "mouseout",
815                   NS_ConvertUTF16toUTF8(ot).get(),
816                   NS_ConvertUTF16toUTF8(ct).get(),
817                   isAnonForEvents
818                       ? "(is native anonymous)"
819                       : (ChromeOnlyAccess() ? "(is in native anonymous subtree)"
820                                             : ""),
821                   NS_ConvertUTF16toUTF8(rt).get(),
822                   relatedTarget->ChromeOnlyAccess()
823                       ? "(is in native anonymous subtree)"
824                       : "",
825                   (originalTarget &&
826                    relatedTarget->FindFirstNonChromeOnlyAccessContent() ==
827                        originalTarget->FindFirstNonChromeOnlyAccessContent())
828                       ? ""
829                       : "Wrong event propagation!?!\n");
830 #endif
831               aVisitor.SetParentTarget(nullptr, false);
832               // Event should not propagate to non-anon content.
833               aVisitor.mCanHandle = isAnonForEvents;
834               return;
835             }
836           }
837         }
838       }
839     }
840   }
841 
842   // Event parent is the assigned slot, if node is assigned, or node's parent
843   // otherwise.
844   HTMLSlotElement* slot = GetAssignedSlot();
845   nsIContent* parent = slot ? slot : GetParent();
846 
847   // Event may need to be retargeted if this is the root of a native
848   // anonymous content subtree or event is dispatched somewhere inside XBL.
849   if (isAnonForEvents) {
850 #ifdef DEBUG
851     // If a DOM event is explicitly dispatched using node.dispatchEvent(), then
852     // all the events are allowed even in the native anonymous content..
853     nsCOMPtr<nsIContent> t =
854         nsIContent::FromEventTargetOrNull(aVisitor.mEvent->mOriginalTarget);
855     NS_ASSERTION(!t || !t->ChromeOnlyAccess() ||
856                      aVisitor.mEvent->mClass != eMutationEventClass ||
857                      aVisitor.mDOMEvent,
858                  "Mutation event dispatched in native anonymous content!?!");
859 #endif
860     aVisitor.mEventTargetAtParent = parent;
861   } else if (parent && aVisitor.mOriginalTargetIsInAnon) {
862     nsCOMPtr<nsIContent> content(
863         nsIContent::FromEventTargetOrNull(aVisitor.mEvent->mTarget));
864     if (content &&
865         content->GetClosestNativeAnonymousSubtreeRootParent() == parent) {
866       aVisitor.mEventTargetAtParent = parent;
867     }
868   }
869 
870   if (!aVisitor.mEvent->mFlags.mComposedInNativeAnonymousContent &&
871       IsRootOfNativeAnonymousSubtree() && OwnerDoc()->GetWindow()) {
872     aVisitor.SetParentTarget(OwnerDoc()->GetWindow()->GetParentTarget(), true);
873   } else if (parent) {
874     aVisitor.SetParentTarget(parent, false);
875     if (slot) {
876       ShadowRoot* root = slot->GetContainingShadow();
877       if (root && root->IsClosed()) {
878         aVisitor.mParentIsSlotInClosedTree = true;
879       }
880     }
881   } else {
882     aVisitor.SetParentTarget(GetComposedDoc(), false);
883   }
884 
885   if (!ChromeOnlyAccess() && !aVisitor.mRelatedTargetRetargetedInCurrentScope) {
886     // We don't support Shadow DOM in native anonymous content yet.
887     aVisitor.mRelatedTargetRetargetedInCurrentScope = true;
888     if (aVisitor.mEvent->mOriginalRelatedTarget) {
889       // https://dom.spec.whatwg.org/#concept-event-dispatch
890       // Step 3.
891       // "Let relatedTarget be the result of retargeting event's relatedTarget
892       //  against target if event's relatedTarget is non-null, and null
893       //  otherwise."
894       //
895       // This is a bit complicated because the event might be from native
896       // anonymous content, but we need to deal with non-native anonymous
897       // content there.
898       bool initialTarget = this == aVisitor.mEvent->mOriginalTarget;
899       nsCOMPtr<nsINode> originalTargetAsNode;
900       // Use of mOriginalTargetIsInAnon is an optimization here.
901       if (!initialTarget && aVisitor.mOriginalTargetIsInAnon) {
902         originalTargetAsNode =
903             FindChromeAccessOnlySubtreeOwner(aVisitor.mEvent->mOriginalTarget);
904         initialTarget = originalTargetAsNode == this;
905       }
906       if (initialTarget) {
907         nsCOMPtr<nsINode> relatedTargetAsNode =
908             FindChromeAccessOnlySubtreeOwner(
909                 aVisitor.mEvent->mOriginalRelatedTarget);
910         if (!originalTargetAsNode) {
911           originalTargetAsNode =
912               nsINode::FromEventTargetOrNull(aVisitor.mEvent->mOriginalTarget);
913         }
914 
915         if (relatedTargetAsNode && originalTargetAsNode) {
916           nsINode* retargetedRelatedTarget = nsContentUtils::Retarget(
917               relatedTargetAsNode, originalTargetAsNode);
918           if (originalTargetAsNode == retargetedRelatedTarget &&
919               retargetedRelatedTarget != relatedTargetAsNode) {
920             // Step 4.
921             // "If target is relatedTarget and target is not event's
922             //  relatedTarget, then return true."
923             aVisitor.IgnoreCurrentTargetBecauseOfShadowDOMRetargeting();
924             // Old code relies on mTarget to point to the first element which
925             // was not added to the event target chain because of mCanHandle
926             // being false, but in Shadow DOM case mTarget really should
927             // point to a node in Shadow DOM.
928             aVisitor.mEvent->mTarget = aVisitor.mTargetInKnownToBeHandledScope;
929             return;
930           }
931 
932           // Part of step 5. Retargeting target has happened already higher
933           // up in this method.
934           // "Append to an event path with event, target, targetOverride,
935           //  relatedTarget, and false."
936           aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
937         }
938       } else {
939         nsCOMPtr<nsINode> relatedTargetAsNode =
940             FindChromeAccessOnlySubtreeOwner(
941                 aVisitor.mEvent->mOriginalRelatedTarget);
942         if (relatedTargetAsNode) {
943           // Step 11.3.
944           // "Let relatedTarget be the result of retargeting event's
945           // relatedTarget against parent if event's relatedTarget is non-null,
946           // and null otherwise.".
947           nsINode* retargetedRelatedTarget =
948               nsContentUtils::Retarget(relatedTargetAsNode, this);
949           nsCOMPtr<nsINode> targetInKnownToBeHandledScope =
950               FindChromeAccessOnlySubtreeOwner(
951                   aVisitor.mTargetInKnownToBeHandledScope);
952           // If aVisitor.mTargetInKnownToBeHandledScope wasn't nsINode,
953           // targetInKnownToBeHandledScope will be null. This may happen when
954           // dispatching event to Window object in a content page and
955           // propagating the event to a chrome Element.
956           if (targetInKnownToBeHandledScope &&
957               IsShadowIncludingInclusiveDescendantOf(
958                   targetInKnownToBeHandledScope->SubtreeRoot())) {
959             // Part of step 11.4.
960             // "If target's root is a shadow-including inclusive ancestor of
961             //  parent, then"
962             // "...Append to an event path with event, parent, null,
963             // relatedTarget, "   and slot-in-closed-tree."
964             aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
965           } else if (this == retargetedRelatedTarget) {
966             // Step 11.5
967             // "Otherwise, if parent and relatedTarget are identical, then set
968             //  parent to null."
969             aVisitor.IgnoreCurrentTargetBecauseOfShadowDOMRetargeting();
970             // Old code relies on mTarget to point to the first element which
971             // was not added to the event target chain because of mCanHandle
972             // being false, but in Shadow DOM case mTarget really should
973             // point to a node in Shadow DOM.
974             aVisitor.mEvent->mTarget = aVisitor.mTargetInKnownToBeHandledScope;
975             return;
976           } else if (targetInKnownToBeHandledScope) {
977             // Note, if targetInKnownToBeHandledScope is null,
978             // mTargetInKnownToBeHandledScope could be Window object in content
979             // page and we're in chrome document in the same process.
980 
981             // Step 11.6
982             aVisitor.mRetargetedRelatedTarget = retargetedRelatedTarget;
983           }
984         }
985       }
986     }
987 
988     if (aVisitor.mEvent->mClass == eTouchEventClass) {
989       // Retarget touch objects.
990       MOZ_ASSERT(!aVisitor.mRetargetedTouchTargets.isSome());
991       aVisitor.mRetargetedTouchTargets.emplace();
992       WidgetTouchEvent* touchEvent = aVisitor.mEvent->AsTouchEvent();
993       WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
994       for (uint32_t i = 0; i < touches.Length(); ++i) {
995         Touch* touch = touches[i];
996         EventTarget* originalTarget = touch->mOriginalTarget;
997         EventTarget* touchTarget = originalTarget;
998         nsCOMPtr<nsINode> targetAsNode =
999             nsINode::FromEventTargetOrNull(originalTarget);
1000         if (targetAsNode) {
1001           EventTarget* retargeted =
1002               nsContentUtils::Retarget(targetAsNode, this);
1003           if (retargeted) {
1004             touchTarget = retargeted;
1005           }
1006         }
1007         aVisitor.mRetargetedTouchTargets->AppendElement(touchTarget);
1008         touch->mTarget = touchTarget;
1009       }
1010       MOZ_ASSERT(aVisitor.mRetargetedTouchTargets->Length() ==
1011                  touches.Length());
1012     }
1013   }
1014 
1015   if (slot) {
1016     // Inform that we're about to exit the current scope.
1017     aVisitor.mRelatedTargetRetargetedInCurrentScope = false;
1018   }
1019 }
1020 
IsFocusable(int32_t * aTabIndex,bool aWithMouse)1021 bool nsIContent::IsFocusable(int32_t* aTabIndex, bool aWithMouse) {
1022   bool focusable = IsFocusableInternal(aTabIndex, aWithMouse);
1023   // Ensure that the return value and aTabIndex are consistent in the case
1024   // we're in userfocusignored context.
1025   if (focusable || (aTabIndex && *aTabIndex != -1)) {
1026     return focusable;
1027   }
1028   return false;
1029 }
1030 
IsFocusableInternal(int32_t * aTabIndex,bool aWithMouse)1031 bool nsIContent::IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse) {
1032   if (aTabIndex) {
1033     *aTabIndex = -1;  // Default, not tabbable
1034   }
1035   return false;
1036 }
1037 
IsLink(nsIURI ** aURI) const1038 bool FragmentOrElement::IsLink(nsIURI** aURI) const {
1039   *aURI = nullptr;
1040   return false;
1041 }
1042 
SetAssignedSlot(HTMLSlotElement * aSlot)1043 void nsIContent::SetAssignedSlot(HTMLSlotElement* aSlot) {
1044   MOZ_ASSERT(aSlot || GetExistingExtendedContentSlots());
1045   ExtendedContentSlots()->mAssignedSlot = aSlot;
1046 }
1047 
1048 #ifdef MOZ_DOM_LIST
Dump()1049 void nsIContent::Dump() { List(); }
1050 #endif
1051 
GetTextContentInternal(nsAString & aTextContent,OOMReporter & aError)1052 void FragmentOrElement::GetTextContentInternal(nsAString& aTextContent,
1053                                                OOMReporter& aError) {
1054   if (!nsContentUtils::GetNodeTextContent(this, true, aTextContent, fallible)) {
1055     aError.ReportOOM();
1056   }
1057 }
1058 
SetTextContentInternal(const nsAString & aTextContent,nsIPrincipal * aSubjectPrincipal,ErrorResult & aError)1059 void FragmentOrElement::SetTextContentInternal(const nsAString& aTextContent,
1060                                                nsIPrincipal* aSubjectPrincipal,
1061                                                ErrorResult& aError) {
1062   aError = nsContentUtils::SetNodeTextContent(this, aTextContent, false);
1063 }
1064 
DestroyContent()1065 void FragmentOrElement::DestroyContent() {
1066   // Drop any servo data. We do this before the RemovedFromDocument call below
1067   // so that it doesn't need to try to keep the style state sane when shuffling
1068   // around the flattened tree.
1069   //
1070   // TODO(emilio): I suspect this can be asserted against instead, with a bit of
1071   // effort to avoid calling Document::Destroy with a shell...
1072   if (IsElement()) {
1073     AsElement()->ClearServoData();
1074   }
1075 
1076 #ifdef DEBUG
1077   uint32_t oldChildCount = GetChildCount();
1078 #endif
1079 
1080   for (nsIContent* child = GetFirstChild(); child;
1081        child = child->GetNextSibling()) {
1082     child->DestroyContent();
1083     MOZ_ASSERT(child->GetParent() == this,
1084                "Mutating the tree during XBL destructors is evil");
1085   }
1086 
1087   MOZ_ASSERT(oldChildCount == GetChildCount(),
1088              "Mutating the tree during XBL destructors is evil");
1089 
1090   if (ShadowRoot* shadowRoot = GetShadowRoot()) {
1091     shadowRoot->DestroyContent();
1092   }
1093 }
1094 
SaveSubtreeState()1095 void FragmentOrElement::SaveSubtreeState() {
1096   for (nsIContent* child = GetFirstChild(); child;
1097        child = child->GetNextSibling()) {
1098     child->SaveSubtreeState();
1099   }
1100 
1101   // FIXME(bug 1469277): Pretty sure this wants to dig into shadow trees as
1102   // well.
1103 }
1104 
1105 //----------------------------------------------------------------------
1106 
1107 // Generic DOMNode implementations
1108 
FireNodeInserted(Document * aDoc,nsINode * aParent,nsTArray<nsCOMPtr<nsIContent>> & aNodes)1109 void FragmentOrElement::FireNodeInserted(
1110     Document* aDoc, nsINode* aParent, nsTArray<nsCOMPtr<nsIContent>>& aNodes) {
1111   uint32_t count = aNodes.Length();
1112   for (uint32_t i = 0; i < count; ++i) {
1113     nsIContent* childContent = aNodes[i];
1114 
1115     if (nsContentUtils::HasMutationListeners(
1116             childContent, NS_EVENT_BITS_MUTATION_NODEINSERTED, aParent)) {
1117       InternalMutationEvent mutation(true, eLegacyNodeInserted);
1118       mutation.mRelatedNode = aParent;
1119 
1120       mozAutoSubtreeModified subtree(aDoc, aParent);
1121       (new AsyncEventDispatcher(childContent, mutation))->RunDOMEventWhenSafe();
1122     }
1123   }
1124 }
1125 
1126 //----------------------------------------------------------------------
1127 
1128 // nsISupports implementation
1129 
1130 #define SUBTREE_UNBINDINGS_PER_RUNNABLE 500
1131 
1132 class ContentUnbinder : public Runnable {
1133  public:
ContentUnbinder()1134   ContentUnbinder() : Runnable("ContentUnbinder") { mLast = this; }
1135 
~ContentUnbinder()1136   ~ContentUnbinder() { Run(); }
1137 
UnbindSubtree(nsIContent * aNode)1138   void UnbindSubtree(nsIContent* aNode) {
1139     if (aNode->NodeType() != nsINode::ELEMENT_NODE &&
1140         aNode->NodeType() != nsINode::DOCUMENT_FRAGMENT_NODE) {
1141       return;
1142     }
1143     FragmentOrElement* container = static_cast<FragmentOrElement*>(aNode);
1144     if (container->HasChildren()) {
1145       // Invalidate cached array of child nodes
1146       container->InvalidateChildNodes();
1147 
1148       while (container->HasChildren()) {
1149         // Hold a strong ref to the node when we remove it, because we may be
1150         // the last reference to it.  We need to call DisconnectChild()
1151         // before calling UnbindFromTree, since this last can notify various
1152         // observers and they should really see consistent
1153         // tree state.
1154         // If this code changes, change the corresponding code in
1155         // FragmentOrElement's and Document's unlink impls.
1156         nsCOMPtr<nsIContent> child = container->GetLastChild();
1157         container->DisconnectChild(child);
1158         UnbindSubtree(child);
1159         child->UnbindFromTree();
1160       }
1161     }
1162   }
1163 
Run()1164   NS_IMETHOD Run() override {
1165     nsAutoScriptBlocker scriptBlocker;
1166     uint32_t len = mSubtreeRoots.Length();
1167     if (len) {
1168       for (uint32_t i = 0; i < len; ++i) {
1169         UnbindSubtree(mSubtreeRoots[i]);
1170       }
1171       mSubtreeRoots.Clear();
1172     }
1173     nsCycleCollector_dispatchDeferredDeletion();
1174     if (this == sContentUnbinder) {
1175       sContentUnbinder = nullptr;
1176       if (mNext) {
1177         RefPtr<ContentUnbinder> next;
1178         next.swap(mNext);
1179         sContentUnbinder = next;
1180         next->mLast = mLast;
1181         mLast = nullptr;
1182         NS_DispatchToCurrentThreadQueue(next.forget(),
1183                                         EventQueuePriority::Idle);
1184       }
1185     }
1186     return NS_OK;
1187   }
1188 
UnbindAll()1189   static void UnbindAll() {
1190     RefPtr<ContentUnbinder> ub = sContentUnbinder;
1191     sContentUnbinder = nullptr;
1192     while (ub) {
1193       ub->Run();
1194       ub = ub->mNext;
1195     }
1196   }
1197 
Append(nsIContent * aSubtreeRoot)1198   static void Append(nsIContent* aSubtreeRoot) {
1199     if (!sContentUnbinder) {
1200       sContentUnbinder = new ContentUnbinder();
1201       nsCOMPtr<nsIRunnable> e = sContentUnbinder;
1202       NS_DispatchToCurrentThreadQueue(e.forget(), EventQueuePriority::Idle);
1203     }
1204 
1205     if (sContentUnbinder->mLast->mSubtreeRoots.Length() >=
1206         SUBTREE_UNBINDINGS_PER_RUNNABLE) {
1207       sContentUnbinder->mLast->mNext = new ContentUnbinder();
1208       sContentUnbinder->mLast = sContentUnbinder->mLast->mNext;
1209     }
1210     sContentUnbinder->mLast->mSubtreeRoots.AppendElement(aSubtreeRoot);
1211   }
1212 
1213  private:
1214   AutoTArray<nsCOMPtr<nsIContent>, SUBTREE_UNBINDINGS_PER_RUNNABLE>
1215       mSubtreeRoots;
1216   RefPtr<ContentUnbinder> mNext;
1217   ContentUnbinder* mLast;
1218   static ContentUnbinder* sContentUnbinder;
1219 };
1220 
1221 ContentUnbinder* ContentUnbinder::sContentUnbinder = nullptr;
1222 
ClearContentUnbinder()1223 void FragmentOrElement::ClearContentUnbinder() { ContentUnbinder::UnbindAll(); }
1224 
1225 NS_IMPL_CYCLE_COLLECTION_CLASS(FragmentOrElement)
1226 
1227 // We purposefully don't UNLINK_BEGIN_INHERITED here.
1228 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement)
1229   nsIContent::Unlink(tmp);
1230 
1231   // The XBL binding is removed by RemoveFromBindingManagerRunnable
1232   // which is dispatched in UnbindFromTree.
1233 
1234   if (tmp->HasProperties()) {
1235     if (tmp->IsElement()) {
1236       Element* elem = tmp->AsElement();
1237       elem->UnlinkIntersectionObservers();
1238     }
1239 
1240     if (tmp->IsHTMLElement() || tmp->IsSVGElement()) {
1241       nsStaticAtom* const* props =
1242           Element::HTMLSVGPropertiesToTraverseAndUnlink();
1243       for (uint32_t i = 0; props[i]; ++i) {
1244         tmp->RemoveProperty(props[i]);
1245       }
1246     }
1247 
1248     if (tmp->MayHaveAnimations()) {
1249       nsAtom** effectProps = EffectSet::GetEffectSetPropertyAtoms();
1250       for (uint32_t i = 0; effectProps[i]; ++i) {
1251         tmp->RemoveProperty(effectProps[i]);
1252       }
1253     }
1254   }
1255 
1256   // Unlink child content (and unbind our subtree).
1257   if (tmp->UnoptimizableCCNode() || !nsCCUncollectableMarker::sGeneration) {
1258     // Don't allow script to run while we're unbinding everything.
1259     nsAutoScriptBlocker scriptBlocker;
1260     while (tmp->HasChildren()) {
1261       // Hold a strong ref to the node when we remove it, because we may be
1262       // the last reference to it.
1263       // If this code changes, change the corresponding code in Document's
1264       // unlink impl and ContentUnbinder::UnbindSubtree.
1265       nsCOMPtr<nsIContent> child = tmp->GetLastChild();
1266       tmp->DisconnectChild(child);
1267       child->UnbindFromTree();
1268     }
1269   } else if (!tmp->GetParent() && tmp->HasChildren()) {
1270     ContentUnbinder::Append(tmp);
1271   } /* else {
1272     The subtree root will end up to a ContentUnbinder, and that will
1273     unbind the child nodes.
1274   } */
1275 
1276   if (ShadowRoot* shadowRoot = tmp->GetShadowRoot()) {
1277     shadowRoot->Unbind();
1278     tmp->ExtendedDOMSlots()->mShadowRoot = nullptr;
1279   }
1280 
1281 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1282 
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(FragmentOrElement)1283 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(FragmentOrElement)
1284 
1285 void FragmentOrElement::MarkNodeChildren(nsINode* aNode) {
1286   JSObject* o = GetJSObjectChild(aNode);
1287   if (o) {
1288     JS::ExposeObjectToActiveJS(o);
1289   }
1290 
1291   EventListenerManager* elm = aNode->GetExistingListenerManager();
1292   if (elm) {
1293     elm->MarkForCC();
1294   }
1295 }
1296 
FindOptimizableSubtreeRoot(nsINode * aNode)1297 nsINode* FindOptimizableSubtreeRoot(nsINode* aNode) {
1298   nsINode* p;
1299   while ((p = aNode->GetParentNode())) {
1300     if (aNode->UnoptimizableCCNode()) {
1301       return nullptr;
1302     }
1303     aNode = p;
1304   }
1305 
1306   if (aNode->UnoptimizableCCNode()) {
1307     return nullptr;
1308   }
1309   return aNode;
1310 }
1311 
1312 StaticAutoPtr<nsTHashSet<nsINode*>> gCCBlackMarkedNodes;
1313 
ClearBlackMarkedNodes()1314 static void ClearBlackMarkedNodes() {
1315   if (!gCCBlackMarkedNodes) {
1316     return;
1317   }
1318   for (nsINode* n : *gCCBlackMarkedNodes) {
1319     n->SetCCMarkedRoot(false);
1320     n->SetInCCBlackTree(false);
1321   }
1322   gCCBlackMarkedNodes = nullptr;
1323 }
1324 
1325 // static
RemoveBlackMarkedNode(nsINode * aNode)1326 void FragmentOrElement::RemoveBlackMarkedNode(nsINode* aNode) {
1327   if (!gCCBlackMarkedNodes) {
1328     return;
1329   }
1330   gCCBlackMarkedNodes->Remove(aNode);
1331 }
1332 
IsCertainlyAliveNode(nsINode * aNode,Document * aDoc)1333 static bool IsCertainlyAliveNode(nsINode* aNode, Document* aDoc) {
1334   MOZ_ASSERT(aNode->GetComposedDoc() == aDoc);
1335 
1336   // Marked to be in-CC-generation or if the document is an svg image that's
1337   // being kept alive by the image cache. (Note that an svg image's internal
1338   // SVG document will receive an OnPageHide() call when it gets purged from
1339   // the image cache; hence, we use IsVisible() as a hint that the document is
1340   // actively being kept alive by the cache.)
1341   return nsCCUncollectableMarker::InGeneration(aDoc->GetMarkedCCGeneration()) ||
1342          (nsCCUncollectableMarker::sGeneration && aDoc->IsBeingUsedAsImage() &&
1343           aDoc->IsVisible());
1344 }
1345 
1346 // static
CanSkipInCC(nsINode * aNode)1347 bool FragmentOrElement::CanSkipInCC(nsINode* aNode) {
1348   // Don't try to optimize anything during shutdown.
1349   if (nsCCUncollectableMarker::sGeneration == 0) {
1350     return false;
1351   }
1352 
1353   Document* currentDoc = aNode->GetComposedDoc();
1354   if (currentDoc && IsCertainlyAliveNode(aNode, currentDoc)) {
1355     return !NeedsScriptTraverse(aNode);
1356   }
1357 
1358   // Bail out early if aNode is somewhere in anonymous content,
1359   // or otherwise unusual.
1360   if (aNode->UnoptimizableCCNode()) {
1361     return false;
1362   }
1363 
1364   nsINode* root = currentDoc ? static_cast<nsINode*>(currentDoc)
1365                              : FindOptimizableSubtreeRoot(aNode);
1366   if (!root) {
1367     return false;
1368   }
1369 
1370   // Subtree has been traversed already.
1371   if (root->CCMarkedRoot()) {
1372     return root->InCCBlackTree() && !NeedsScriptTraverse(aNode);
1373   }
1374 
1375   if (!gCCBlackMarkedNodes) {
1376     gCCBlackMarkedNodes = new nsTHashSet<nsINode*>(1020);
1377   }
1378 
1379   // nodesToUnpurple contains nodes which will be removed
1380   // from the purple buffer if the DOM tree is known-live.
1381   AutoTArray<nsIContent*, 1020> nodesToUnpurple;
1382   // grayNodes need script traverse, so they aren't removed from
1383   // the purple buffer, but are marked to be in known-live subtree so that
1384   // traverse is faster.
1385   AutoTArray<nsINode*, 1020> grayNodes;
1386 
1387   bool foundLiveWrapper = root->HasKnownLiveWrapper();
1388   if (root != currentDoc) {
1389     currentDoc = nullptr;
1390     if (NeedsScriptTraverse(root)) {
1391       grayNodes.AppendElement(root);
1392     } else if (static_cast<nsIContent*>(root)->IsPurple()) {
1393       nodesToUnpurple.AppendElement(static_cast<nsIContent*>(root));
1394     }
1395   }
1396 
1397   // Traverse the subtree and check if we could know without CC
1398   // that it is known-live.
1399   // Note, this traverse is non-virtual and inline, so it should be a lot faster
1400   // than CC's generic traverse.
1401   for (nsIContent* node = root->GetFirstChild(); node;
1402        node = node->GetNextNode(root)) {
1403     foundLiveWrapper = foundLiveWrapper || node->HasKnownLiveWrapper();
1404     if (foundLiveWrapper && currentDoc) {
1405       // If we can mark the whole document known-live, no need to optimize
1406       // so much, since when the next purple node in the document will be
1407       // handled, it is fast to check that currentDoc is in CCGeneration.
1408       break;
1409     }
1410     if (NeedsScriptTraverse(node)) {
1411       // Gray nodes need real CC traverse.
1412       grayNodes.AppendElement(node);
1413     } else if (node->IsPurple()) {
1414       nodesToUnpurple.AppendElement(node);
1415     }
1416   }
1417 
1418   root->SetCCMarkedRoot(true);
1419   root->SetInCCBlackTree(foundLiveWrapper);
1420   gCCBlackMarkedNodes->Insert(root);
1421 
1422   if (!foundLiveWrapper) {
1423     return false;
1424   }
1425 
1426   if (currentDoc) {
1427     // Special case documents. If we know the document is known-live,
1428     // we can mark the document to be in CCGeneration.
1429     currentDoc->MarkUncollectableForCCGeneration(
1430         nsCCUncollectableMarker::sGeneration);
1431   } else {
1432     for (uint32_t i = 0; i < grayNodes.Length(); ++i) {
1433       nsINode* node = grayNodes[i];
1434       node->SetInCCBlackTree(true);
1435       gCCBlackMarkedNodes->Insert(node);
1436     }
1437   }
1438 
1439   // Subtree is known-live, we can remove non-gray purple nodes from
1440   // purple buffer.
1441   for (uint32_t i = 0; i < nodesToUnpurple.Length(); ++i) {
1442     nsIContent* purple = nodesToUnpurple[i];
1443     // Can't remove currently handled purple node.
1444     if (purple != aNode) {
1445       purple->RemovePurple();
1446     }
1447   }
1448   return !NeedsScriptTraverse(aNode);
1449 }
1450 
1451 AutoTArray<nsINode*, 1020>* gPurpleRoots = nullptr;
1452 AutoTArray<nsIContent*, 1020>* gNodesToUnbind = nullptr;
1453 
ClearCycleCollectorCleanupData()1454 void ClearCycleCollectorCleanupData() {
1455   if (gPurpleRoots) {
1456     uint32_t len = gPurpleRoots->Length();
1457     for (uint32_t i = 0; i < len; ++i) {
1458       nsINode* n = gPurpleRoots->ElementAt(i);
1459       n->SetIsPurpleRoot(false);
1460     }
1461     delete gPurpleRoots;
1462     gPurpleRoots = nullptr;
1463   }
1464   if (gNodesToUnbind) {
1465     uint32_t len = gNodesToUnbind->Length();
1466     for (uint32_t i = 0; i < len; ++i) {
1467       nsIContent* c = gNodesToUnbind->ElementAt(i);
1468       c->SetIsPurpleRoot(false);
1469       ContentUnbinder::Append(c);
1470     }
1471     delete gNodesToUnbind;
1472     gNodesToUnbind = nullptr;
1473   }
1474 }
1475 
ShouldClearPurple(nsIContent * aContent)1476 static bool ShouldClearPurple(nsIContent* aContent) {
1477   MOZ_ASSERT(aContent);
1478   if (aContent->IsPurple()) {
1479     return true;
1480   }
1481 
1482   JSObject* o = GetJSObjectChild(aContent);
1483   if (o && JS::ObjectIsMarkedGray(o)) {
1484     return true;
1485   }
1486 
1487   if (aContent->HasListenerManager()) {
1488     return true;
1489   }
1490 
1491   return aContent->HasProperties();
1492 }
1493 
1494 // If aNode is not optimizable, but is an element
1495 // with a frame in a document which has currently active presshell,
1496 // we can act as if it was optimizable. When the primary frame dies, aNode
1497 // will end up to the purple buffer because of the refcount change.
NodeHasActiveFrame(Document * aCurrentDoc,nsINode * aNode)1498 bool NodeHasActiveFrame(Document* aCurrentDoc, nsINode* aNode) {
1499   return aCurrentDoc->GetPresShell() && aNode->IsElement() &&
1500          aNode->AsElement()->GetPrimaryFrame();
1501 }
1502 
1503 // CanSkip checks if aNode is known-live, and if it is, returns true. If aNode
1504 // is in a known-live DOM tree, CanSkip may also remove other objects from
1505 // purple buffer and unmark event listeners and user data.  If the root of the
1506 // DOM tree is a document, less optimizations are done since checking the
1507 // liveness of the current document is usually fast and we don't want slow down
1508 // such common cases.
CanSkip(nsINode * aNode,bool aRemovingAllowed)1509 bool FragmentOrElement::CanSkip(nsINode* aNode, bool aRemovingAllowed) {
1510   // Don't try to optimize anything during shutdown.
1511   if (nsCCUncollectableMarker::sGeneration == 0) {
1512     return false;
1513   }
1514 
1515   bool unoptimizable = aNode->UnoptimizableCCNode();
1516   Document* currentDoc = aNode->GetComposedDoc();
1517   if (currentDoc && IsCertainlyAliveNode(aNode, currentDoc) &&
1518       (!unoptimizable || NodeHasActiveFrame(currentDoc, aNode))) {
1519     MarkNodeChildren(aNode);
1520     return true;
1521   }
1522 
1523   if (unoptimizable) {
1524     return false;
1525   }
1526 
1527   nsINode* root = currentDoc ? static_cast<nsINode*>(currentDoc)
1528                              : FindOptimizableSubtreeRoot(aNode);
1529   if (!root) {
1530     return false;
1531   }
1532 
1533   // Subtree has been traversed already, and aNode has
1534   // been handled in a way that doesn't require revisiting it.
1535   if (root->IsPurpleRoot()) {
1536     return false;
1537   }
1538 
1539   // nodesToClear contains nodes which are either purple or
1540   // gray.
1541   AutoTArray<nsIContent*, 1020> nodesToClear;
1542 
1543   bool foundLiveWrapper = root->HasKnownLiveWrapper();
1544   bool domOnlyCycle = false;
1545   if (root != currentDoc) {
1546     currentDoc = nullptr;
1547     if (!foundLiveWrapper) {
1548       domOnlyCycle = static_cast<nsIContent*>(root)->OwnedOnlyByTheDOMTree();
1549     }
1550     if (ShouldClearPurple(static_cast<nsIContent*>(root))) {
1551       nodesToClear.AppendElement(static_cast<nsIContent*>(root));
1552     }
1553   }
1554 
1555   // Traverse the subtree and check if we could know without CC
1556   // that it is known-live.
1557   // Note, this traverse is non-virtual and inline, so it should be a lot faster
1558   // than CC's generic traverse.
1559   for (nsIContent* node = root->GetFirstChild(); node;
1560        node = node->GetNextNode(root)) {
1561     foundLiveWrapper = foundLiveWrapper || node->HasKnownLiveWrapper();
1562     if (foundLiveWrapper) {
1563       domOnlyCycle = false;
1564       if (currentDoc) {
1565         // If we can mark the whole document live, no need to optimize
1566         // so much, since when the next purple node in the document will be
1567         // handled, it is fast to check that the currentDoc is in CCGeneration.
1568         break;
1569       }
1570       // No need to put stuff to the nodesToClear array, if we can clear it
1571       // already here.
1572       if (node->IsPurple() && (node != aNode || aRemovingAllowed)) {
1573         node->RemovePurple();
1574       }
1575       MarkNodeChildren(node);
1576     } else {
1577       domOnlyCycle = domOnlyCycle && node->OwnedOnlyByTheDOMTree();
1578       if (ShouldClearPurple(node)) {
1579         // Collect interesting nodes which we can clear if we find that
1580         // they are kept alive in a known-live tree or are in a DOM-only cycle.
1581         nodesToClear.AppendElement(node);
1582       }
1583     }
1584   }
1585 
1586   if (!currentDoc || !foundLiveWrapper) {
1587     root->SetIsPurpleRoot(true);
1588     if (domOnlyCycle) {
1589       if (!gNodesToUnbind) {
1590         gNodesToUnbind = new AutoTArray<nsIContent*, 1020>();
1591       }
1592       gNodesToUnbind->AppendElement(static_cast<nsIContent*>(root));
1593       for (uint32_t i = 0; i < nodesToClear.Length(); ++i) {
1594         nsIContent* n = nodesToClear[i];
1595         if ((n != aNode || aRemovingAllowed) && n->IsPurple()) {
1596           n->RemovePurple();
1597         }
1598       }
1599       return true;
1600     } else {
1601       if (!gPurpleRoots) {
1602         gPurpleRoots = new AutoTArray<nsINode*, 1020>();
1603       }
1604       gPurpleRoots->AppendElement(root);
1605     }
1606   }
1607 
1608   if (!foundLiveWrapper) {
1609     return false;
1610   }
1611 
1612   if (currentDoc) {
1613     // Special case documents. If we know the document is known-live,
1614     // we can mark the document to be in CCGeneration.
1615     currentDoc->MarkUncollectableForCCGeneration(
1616         nsCCUncollectableMarker::sGeneration);
1617     MarkNodeChildren(currentDoc);
1618   }
1619 
1620   // Subtree is known-live, so we can remove purple nodes from
1621   // purple buffer and mark stuff that to be certainly alive.
1622   for (uint32_t i = 0; i < nodesToClear.Length(); ++i) {
1623     nsIContent* n = nodesToClear[i];
1624     MarkNodeChildren(n);
1625     // Can't remove currently handled purple node,
1626     // unless aRemovingAllowed is true.
1627     if ((n != aNode || aRemovingAllowed) && n->IsPurple()) {
1628       n->RemovePurple();
1629     }
1630   }
1631   return true;
1632 }
1633 
CanSkipThis(nsINode * aNode)1634 bool FragmentOrElement::CanSkipThis(nsINode* aNode) {
1635   if (nsCCUncollectableMarker::sGeneration == 0) {
1636     return false;
1637   }
1638   if (aNode->HasKnownLiveWrapper()) {
1639     return true;
1640   }
1641   Document* c = aNode->GetComposedDoc();
1642   return ((c && IsCertainlyAliveNode(aNode, c)) || aNode->InCCBlackTree()) &&
1643          !NeedsScriptTraverse(aNode);
1644 }
1645 
InitCCCallbacks()1646 void FragmentOrElement::InitCCCallbacks() {
1647   nsCycleCollector_setForgetSkippableCallback(ClearCycleCollectorCleanupData);
1648   nsCycleCollector_setBeforeUnlinkCallback(ClearBlackMarkedNodes);
1649 }
1650 
1651 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(FragmentOrElement)
1652   return FragmentOrElement::CanSkip(tmp, aRemovingAllowed);
1653 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1654 
1655 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(FragmentOrElement)
1656   return FragmentOrElement::CanSkipInCC(tmp);
1657 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1658 
1659 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(FragmentOrElement)
1660   return FragmentOrElement::CanSkipThis(tmp);
1661 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1662 
1663 // We purposefully don't TRAVERSE_BEGIN_INHERITED here.  All the bits
1664 // we should traverse should be added here or in nsINode::Traverse.
1665 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement)
1666   if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
1667     char name[512];
1668     uint32_t nsid = tmp->GetNameSpaceID();
1669     nsAtomCString localName(tmp->NodeInfo()->NameAtom());
1670     nsAutoCString uri;
1671     if (tmp->OwnerDoc()->GetDocumentURI()) {
1672       uri = tmp->OwnerDoc()->GetDocumentURI()->GetSpecOrDefault();
1673     }
1674 
1675     nsAutoString id;
1676     nsAtom* idAtom = tmp->GetID();
1677     if (idAtom) {
1678       id.AppendLiteral(" id='");
1679       id.Append(nsDependentAtomString(idAtom));
1680       id.Append('\'');
1681     }
1682 
1683     nsAutoString classes;
1684     const nsAttrValue* classAttrValue =
1685         tmp->IsElement() ? tmp->AsElement()->GetClasses() : nullptr;
1686     if (classAttrValue) {
1687       classes.AppendLiteral(" class='");
1688       nsAutoString classString;
1689       classAttrValue->ToString(classString);
1690       classString.ReplaceChar(char16_t('\n'), char16_t(' '));
1691       classes.Append(classString);
1692       classes.Append('\'');
1693     }
1694 
1695     nsAutoCString orphan;
1696     if (!tmp->IsInComposedDoc()) {
1697       orphan.AppendLiteral(" (orphan)");
1698     }
1699 
1700     const char* nsuri = nsNameSpaceManager::GetNameSpaceDisplayName(nsid);
1701     SprintfLiteral(name, "FragmentOrElement %s %s%s%s%s %s", nsuri,
1702                    localName.get(), NS_ConvertUTF16toUTF8(id).get(),
1703                    NS_ConvertUTF16toUTF8(classes).get(), orphan.get(),
1704                    uri.get());
1705     cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
1706   } else {
1707     NS_IMPL_CYCLE_COLLECTION_DESCRIBE(FragmentOrElement, tmp->mRefCnt.get())
1708   }
1709 
1710   if (!nsIContent::Traverse(tmp, cb)) {
1711     return NS_SUCCESS_INTERRUPTED_TRAVERSE;
1712   }
1713 
1714   // Check that whenever we have effect properties, MayHaveAnimations is set.
1715 #ifdef DEBUG
1716   nsAtom** effectProps = EffectSet::GetEffectSetPropertyAtoms();
1717   for (uint32_t i = 0; effectProps[i]; ++i) {
1718     MOZ_ASSERT_IF(tmp->GetProperty(effectProps[i]), tmp->MayHaveAnimations());
1719   }
1720 #endif
1721 
1722   if (tmp->HasProperties()) {
1723     if (tmp->IsElement()) {
1724       Element* elem = tmp->AsElement();
1725       IntersectionObserverList* observers =
1726           static_cast<IntersectionObserverList*>(
1727               elem->GetProperty(nsGkAtoms::intersectionobserverlist));
1728       if (observers) {
1729         for (DOMIntersectionObserver* observer : observers->Keys()) {
1730           cb.NoteXPCOMChild(observer);
1731         }
1732       }
1733     }
1734     if (tmp->IsHTMLElement() || tmp->IsSVGElement()) {
1735       nsStaticAtom* const* props =
1736           Element::HTMLSVGPropertiesToTraverseAndUnlink();
1737       for (uint32_t i = 0; props[i]; ++i) {
1738         nsISupports* property =
1739             static_cast<nsISupports*>(tmp->GetProperty(props[i]));
1740         cb.NoteXPCOMChild(property);
1741       }
1742     }
1743     if (tmp->MayHaveAnimations()) {
1744       nsAtom** effectProps = EffectSet::GetEffectSetPropertyAtoms();
1745       for (uint32_t i = 0; effectProps[i]; ++i) {
1746         EffectSet* effectSet =
1747             static_cast<EffectSet*>(tmp->GetProperty(effectProps[i]));
1748         if (effectSet) {
1749           effectSet->Traverse(cb);
1750         }
1751       }
1752     }
1753   }
1754 
1755   // Traverse attribute names.
1756   if (tmp->IsElement()) {
1757     Element* element = tmp->AsElement();
1758     uint32_t i;
1759     uint32_t attrs = element->GetAttrCount();
1760     for (i = 0; i < attrs; i++) {
1761       const nsAttrName* name = element->GetUnsafeAttrNameAt(i);
1762       if (!name->IsAtom()) {
1763         NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mAttrs[i]->NodeInfo()");
1764         cb.NoteNativeChild(name->NodeInfo(),
1765                            NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo));
1766       }
1767     }
1768   }
1769 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1770 
NS_INTERFACE_MAP_BEGIN(FragmentOrElement)1771 NS_INTERFACE_MAP_BEGIN(FragmentOrElement)
1772   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(FragmentOrElement)
1773 NS_INTERFACE_MAP_END_INHERITING(nsIContent)
1774 
1775 //----------------------------------------------------------------------
1776 
1777 const nsTextFragment* FragmentOrElement::GetText() { return nullptr; }
1778 
TextLength() const1779 uint32_t FragmentOrElement::TextLength() const {
1780   // We can remove this assertion if it turns out to be useful to be able
1781   // to depend on this returning 0
1782   MOZ_ASSERT_UNREACHABLE("called FragmentOrElement::TextLength");
1783 
1784   return 0;
1785 }
1786 
TextIsOnlyWhitespace()1787 bool FragmentOrElement::TextIsOnlyWhitespace() { return false; }
1788 
ThreadSafeTextIsOnlyWhitespace() const1789 bool FragmentOrElement::ThreadSafeTextIsOnlyWhitespace() const { return false; }
1790 
IsVoidTag(nsAtom * aTag)1791 static inline bool IsVoidTag(nsAtom* aTag) {
1792   static const nsAtom* voidElements[] = {
1793       nsGkAtoms::area,    nsGkAtoms::base,  nsGkAtoms::basefont,
1794       nsGkAtoms::bgsound, nsGkAtoms::br,    nsGkAtoms::col,
1795       nsGkAtoms::embed,   nsGkAtoms::frame, nsGkAtoms::hr,
1796       nsGkAtoms::img,     nsGkAtoms::input, nsGkAtoms::keygen,
1797       nsGkAtoms::link,    nsGkAtoms::meta,  nsGkAtoms::param,
1798       nsGkAtoms::source,  nsGkAtoms::track, nsGkAtoms::wbr};
1799 
1800   static mozilla::BitBloomFilter<12, nsAtom> sFilter;
1801   static bool sInitialized = false;
1802   if (!sInitialized) {
1803     sInitialized = true;
1804     for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
1805       sFilter.add(voidElements[i]);
1806     }
1807   }
1808 
1809   if (sFilter.mightContain(aTag)) {
1810     for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
1811       if (aTag == voidElements[i]) {
1812         return true;
1813       }
1814     }
1815   }
1816   return false;
1817 }
1818 
1819 /* static */
IsHTMLVoid(nsAtom * aLocalName)1820 bool FragmentOrElement::IsHTMLVoid(nsAtom* aLocalName) {
1821   return aLocalName && IsVoidTag(aLocalName);
1822 }
1823 
GetMarkup(bool aIncludeSelf,nsAString & aMarkup)1824 void FragmentOrElement::GetMarkup(bool aIncludeSelf, nsAString& aMarkup) {
1825   aMarkup.Truncate();
1826 
1827   Document* doc = OwnerDoc();
1828   if (IsInHTMLDocument()) {
1829     nsContentUtils::SerializeNodeToMarkup(this, !aIncludeSelf, aMarkup);
1830     return;
1831   }
1832 
1833   nsAutoString contentType;
1834   doc->GetContentType(contentType);
1835   bool tryToCacheEncoder = !aIncludeSelf;
1836 
1837   nsCOMPtr<nsIDocumentEncoder> docEncoder = doc->GetCachedEncoder();
1838   if (!docEncoder) {
1839     docEncoder = do_createDocumentEncoder(
1840         PromiseFlatCString(NS_ConvertUTF16toUTF8(contentType)).get());
1841   }
1842   if (!docEncoder) {
1843     // This could be some type for which we create a synthetic document.  Try
1844     // again as XML
1845     contentType.AssignLiteral("application/xml");
1846     docEncoder = do_createDocumentEncoder("application/xml");
1847     // Don't try to cache the encoder since it would point to a different
1848     // contentType once it has been reinitialized.
1849     tryToCacheEncoder = false;
1850   }
1851 
1852   NS_ENSURE_TRUE_VOID(docEncoder);
1853 
1854   uint32_t flags = nsIDocumentEncoder::OutputEncodeBasicEntities |
1855                    // Output DOM-standard newlines
1856                    nsIDocumentEncoder::OutputLFLineBreak |
1857                    // Don't do linebreaking that's not present in
1858                    // the source
1859                    nsIDocumentEncoder::OutputRaw |
1860                    // Only check for mozdirty when necessary (bug 599983)
1861                    nsIDocumentEncoder::OutputIgnoreMozDirty;
1862 
1863   if (IsEditable()) {
1864     nsCOMPtr<Element> elem = do_QueryInterface(this);
1865     TextEditor* textEditor = elem ? elem->GetTextEditorInternal() : nullptr;
1866     if (textEditor && textEditor->OutputsMozDirty()) {
1867       flags &= ~nsIDocumentEncoder::OutputIgnoreMozDirty;
1868     }
1869   }
1870 
1871   DebugOnly<nsresult> rv = docEncoder->NativeInit(doc, contentType, flags);
1872   MOZ_ASSERT(NS_SUCCEEDED(rv));
1873 
1874   if (aIncludeSelf) {
1875     docEncoder->SetNode(this);
1876   } else {
1877     docEncoder->SetContainerNode(this);
1878   }
1879   rv = docEncoder->EncodeToString(aMarkup);
1880   MOZ_ASSERT(NS_SUCCEEDED(rv));
1881   if (tryToCacheEncoder) {
1882     doc->SetCachedEncoder(docEncoder.forget());
1883   }
1884 }
1885 
ContainsMarkup(const nsAString & aStr)1886 static bool ContainsMarkup(const nsAString& aStr) {
1887   // Note: we can't use FindCharInSet because null is one of the characters we
1888   // want to search for.
1889   const char16_t* start = aStr.BeginReading();
1890   const char16_t* end = aStr.EndReading();
1891 
1892   while (start != end) {
1893     char16_t c = *start;
1894     if (c == char16_t('<') || c == char16_t('&') || c == char16_t('\r') ||
1895         c == char16_t('\0')) {
1896       return true;
1897     }
1898     ++start;
1899   }
1900 
1901   return false;
1902 }
1903 
SetInnerHTMLInternal(const nsAString & aInnerHTML,ErrorResult & aError)1904 void FragmentOrElement::SetInnerHTMLInternal(const nsAString& aInnerHTML,
1905                                              ErrorResult& aError) {
1906   FragmentOrElement* target = this;
1907   // Handle template case.
1908   if (target->IsTemplateElement()) {
1909     DocumentFragment* frag =
1910         static_cast<HTMLTemplateElement*>(target)->Content();
1911     MOZ_ASSERT(frag);
1912     target = frag;
1913   }
1914 
1915   // Fast-path for strings with no markup. Limit this to short strings, to
1916   // avoid ContainsMarkup taking too long. The choice for 100 is based on
1917   // gut feeling.
1918   //
1919   // Don't do this for elements with a weird parser insertion mode, for
1920   // instance setting innerHTML = "" on a <html> element should add the
1921   // optional <head> and <body> elements.
1922   if (!target->HasWeirdParserInsertionMode() && aInnerHTML.Length() < 100 &&
1923       !ContainsMarkup(aInnerHTML)) {
1924     aError = nsContentUtils::SetNodeTextContent(target, aInnerHTML, false);
1925     return;
1926   }
1927 
1928   Document* doc = target->OwnerDoc();
1929 
1930   // Batch possible DOMSubtreeModified events.
1931   mozAutoSubtreeModified subtree(doc, nullptr);
1932 
1933   target->FireNodeRemovedForChildren();
1934 
1935   // Needed when innerHTML is used in combination with contenteditable
1936   mozAutoDocUpdate updateBatch(doc, true);
1937 
1938   // Remove childnodes.
1939   nsAutoMutationBatch mb(target, true, false);
1940   while (target->HasChildren()) {
1941     target->RemoveChildNode(target->GetFirstChild(), true);
1942   }
1943   mb.RemovalDone();
1944 
1945   nsAutoScriptLoaderDisabler sld(doc);
1946 
1947   FragmentOrElement* parseContext = this;
1948   if (ShadowRoot* shadowRoot = ShadowRoot::FromNode(this)) {
1949     // Fix up the context to be the host of the ShadowRoot.  See
1950     // https://w3c.github.io/DOM-Parsing/#dom-innerhtml-innerhtml setter step 1.
1951     parseContext = shadowRoot->GetHost();
1952   }
1953 
1954   if (doc->IsHTMLDocument()) {
1955     nsAtom* contextLocalName = parseContext->NodeInfo()->NameAtom();
1956     int32_t contextNameSpaceID = parseContext->GetNameSpaceID();
1957 
1958     int32_t oldChildCount = target->GetChildCount();
1959     aError = nsContentUtils::ParseFragmentHTML(
1960         aInnerHTML, target, contextLocalName, contextNameSpaceID,
1961         doc->GetCompatibilityMode() == eCompatibility_NavQuirks, true);
1962     mb.NodesAdded();
1963     // HTML5 parser has notified, but not fired mutation events.
1964     nsContentUtils::FireMutationEventsForDirectParsing(doc, target,
1965                                                        oldChildCount);
1966   } else {
1967     RefPtr<DocumentFragment> df = nsContentUtils::CreateContextualFragment(
1968         parseContext, aInnerHTML, true, aError);
1969     if (!aError.Failed()) {
1970       // Suppress assertion about node removal mutation events that can't have
1971       // listeners anyway, because no one has had the chance to register
1972       // mutation listeners on the fragment that comes from the parser.
1973       nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
1974 
1975       target->AppendChild(*df, aError);
1976       mb.NodesAdded();
1977     }
1978   }
1979 }
1980 
FireNodeRemovedForChildren()1981 void FragmentOrElement::FireNodeRemovedForChildren() {
1982   Document* doc = OwnerDoc();
1983   // Optimize the common case
1984   if (!nsContentUtils::HasMutationListeners(
1985           doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
1986     return;
1987   }
1988 
1989   nsCOMPtr<nsINode> child;
1990   for (child = GetFirstChild(); child && child->GetParentNode() == this;
1991        child = child->GetNextSibling()) {
1992     nsContentUtils::MaybeFireNodeRemoved(child, this);
1993   }
1994 }
1995 
AddSizeOfExcludingThis(nsWindowSizes & aSizes,size_t * aNodeSize) const1996 void FragmentOrElement::AddSizeOfExcludingThis(nsWindowSizes& aSizes,
1997                                                size_t* aNodeSize) const {
1998   nsIContent::AddSizeOfExcludingThis(aSizes, aNodeSize);
1999 
2000   nsDOMSlots* slots = GetExistingDOMSlots();
2001   if (slots) {
2002     *aNodeSize += slots->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf);
2003   }
2004 }
2005