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; this provides an implementation
9  * of DOM Core's nsIDOMElement, implements nsIContent, provides
10  * utility methods for subclasses, and so forth.
11  */
12 
13 #include "mozilla/ArrayUtils.h"
14 #include "mozilla/Likely.h"
15 #include "mozilla/MemoryReporting.h"
16 #include "mozilla/StaticPtr.h"
17 
18 #include "mozilla/dom/FragmentOrElement.h"
19 
20 #include "mozilla/AsyncEventDispatcher.h"
21 #include "mozilla/DeclarationBlockInlines.h"
22 #include "mozilla/EffectSet.h"
23 #include "mozilla/EventDispatcher.h"
24 #include "mozilla/EventListenerManager.h"
25 #include "mozilla/EventStates.h"
26 #include "mozilla/dom/Attr.h"
27 #include "nsDOMAttributeMap.h"
28 #include "nsIAtom.h"
29 #include "mozilla/dom/NodeInfo.h"
30 #include "mozilla/dom/Event.h"
31 #include "nsIDocumentInlines.h"
32 #include "nsIDocumentEncoder.h"
33 #include "nsIDOMNodeList.h"
34 #include "nsIContentIterator.h"
35 #include "nsFocusManager.h"
36 #include "nsILinkHandler.h"
37 #include "nsIScriptGlobalObject.h"
38 #include "nsIURL.h"
39 #include "nsNetUtil.h"
40 #include "nsIFrame.h"
41 #include "nsIAnonymousContentCreator.h"
42 #include "nsIPresShell.h"
43 #include "nsPresContext.h"
44 #include "nsStyleConsts.h"
45 #include "nsString.h"
46 #include "nsUnicharUtils.h"
47 #include "nsIDOMEvent.h"
48 #include "nsDOMCID.h"
49 #include "nsIServiceManager.h"
50 #include "nsIDOMCSSStyleDeclaration.h"
51 #include "nsDOMCSSAttrDeclaration.h"
52 #include "nsNameSpaceManager.h"
53 #include "nsContentList.h"
54 #include "nsDOMTokenList.h"
55 #include "nsXBLPrototypeBinding.h"
56 #include "nsError.h"
57 #include "nsDOMString.h"
58 #include "nsIScriptSecurityManager.h"
59 #include "nsIDOMMutationEvent.h"
60 #include "mozilla/InternalMutationEvent.h"
61 #include "mozilla/MouseEvents.h"
62 #include "nsNodeUtils.h"
63 #include "nsDocument.h"
64 #include "nsAttrValueOrString.h"
65 #ifdef MOZ_XUL
66 #include "nsXULElement.h"
67 #endif /* MOZ_XUL */
68 #include "nsFrameSelection.h"
69 #ifdef DEBUG
70 #include "nsRange.h"
71 #endif
72 
73 #include "nsBindingManager.h"
74 #include "nsXBLBinding.h"
75 #include "nsPIDOMWindow.h"
76 #include "nsPIBoxObject.h"
77 #include "nsSVGUtils.h"
78 #include "nsLayoutUtils.h"
79 #include "nsGkAtoms.h"
80 #include "nsContentUtils.h"
81 #include "nsTextFragment.h"
82 #include "nsContentCID.h"
83 
84 #include "nsIDOMEventListener.h"
85 #include "nsIWebNavigation.h"
86 #include "nsIBaseWindow.h"
87 #include "nsIWidget.h"
88 
89 #include "js/GCAPI.h"
90 
91 #include "nsNodeInfoManager.h"
92 #include "nsICategoryManager.h"
93 #include "nsGenericHTMLElement.h"
94 #include "nsIEditor.h"
95 #include "nsIEditorIMESupport.h"
96 #include "nsContentCreatorFunctions.h"
97 #include "nsIControllers.h"
98 #include "nsView.h"
99 #include "nsViewManager.h"
100 #include "nsIScrollableFrame.h"
101 #include "ChildIterator.h"
102 #include "mozilla/css/StyleRule.h" /* For nsCSSSelectorList */
103 #include "nsRuleProcessorData.h"
104 #include "nsTextNode.h"
105 #include "mozilla/dom/NodeListBinding.h"
106 
107 #ifdef MOZ_XUL
108 #include "nsIXULDocument.h"
109 #endif /* MOZ_XUL */
110 
111 #include "nsCCUncollectableMarker.h"
112 
113 #include "mozAutoDocUpdate.h"
114 
115 #include "mozilla/Sprintf.h"
116 #include "nsDOMMutationObserver.h"
117 #include "nsWrapperCacheInlines.h"
118 #include "nsCycleCollector.h"
119 #include "xpcpublic.h"
120 #include "nsIScriptError.h"
121 #include "mozilla/Telemetry.h"
122 
123 #include "mozilla/CORSMode.h"
124 
125 #include "mozilla/dom/ShadowRoot.h"
126 #include "mozilla/dom/HTMLTemplateElement.h"
127 
128 #include "nsStyledElement.h"
129 #include "nsIContentInlines.h"
130 #include "nsChildContentList.h"
131 
132 using namespace mozilla;
133 using namespace mozilla::dom;
134 
135 int32_t nsIContent::sTabFocusModel = eTabFocus_any;
136 bool nsIContent::sTabFocusModelAppliesToXUL = false;
137 uint64_t nsMutationGuard::sGeneration = 0;
138 
139 nsIContent*
FindFirstNonChromeOnlyAccessContent() const140 nsIContent::FindFirstNonChromeOnlyAccessContent() const
141 {
142   // This handles also nested native anonymous content.
143   for (const nsIContent *content = this; content;
144        content = content->GetBindingParent()) {
145     if (!content->ChromeOnlyAccess()) {
146       // Oops, this function signature allows casting const to
147       // non-const.  (Then again, so does GetChildAt(0)->GetParent().)
148       return const_cast<nsIContent*>(content);
149     }
150   }
151   return nullptr;
152 }
153 
154 nsINode*
GetFlattenedTreeParentNodeInternal() const155 nsIContent::GetFlattenedTreeParentNodeInternal() const
156 {
157   nsINode* parentNode = GetParentNode();
158   if (!parentNode || !parentNode->IsContent()) {
159     MOZ_ASSERT(!parentNode || parentNode == OwnerDoc());
160     return parentNode;
161   }
162   nsIContent* parent = parentNode->AsContent();
163 
164   if (parent && nsContentUtils::HasDistributedChildren(parent) &&
165       nsContentUtils::IsInSameAnonymousTree(parent, this)) {
166     // This node is distributed to insertion points, thus we
167     // need to consult the destination insertion points list to
168     // figure out where this node was inserted in the flattened tree.
169     // It may be the case that |parent| distributes its children
170     // but the child does not match any insertion points, thus
171     // the flattened tree parent is nullptr.
172     nsTArray<nsIContent*>* destInsertionPoints = GetExistingDestInsertionPoints();
173     parent = destInsertionPoints && !destInsertionPoints->IsEmpty() ?
174       destInsertionPoints->LastElement()->GetParent() : nullptr;
175   } else if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
176     nsIContent* insertionParent = GetXBLInsertionParent();
177     if (insertionParent) {
178       parent = insertionParent;
179     }
180   }
181 
182   // Shadow roots never shows up in the flattened tree. Return the host
183   // instead.
184   if (parent && parent->IsInShadowTree()) {
185     ShadowRoot* parentShadowRoot = ShadowRoot::FromNode(parent);
186     if (parentShadowRoot) {
187       return parentShadowRoot->GetHost();
188     }
189   }
190 
191   return parent;
192 }
193 
194 nsIContent::IMEState
GetDesiredIMEState()195 nsIContent::GetDesiredIMEState()
196 {
197   if (!IsEditableInternal()) {
198     // Check for the special case where we're dealing with elements which don't
199     // have the editable flag set, but are readwrite (such as text controls).
200     if (!IsElement() ||
201         !AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE)) {
202       return IMEState(IMEState::DISABLED);
203     }
204   }
205   // NOTE: The content for independent editors (e.g., input[type=text],
206   // textarea) must override this method, so, we don't need to worry about
207   // that here.
208   nsIContent *editableAncestor = GetEditingHost();
209 
210   // This is in another editable content, use the result of it.
211   if (editableAncestor && editableAncestor != this) {
212     return editableAncestor->GetDesiredIMEState();
213   }
214   nsIDocument* doc = GetComposedDoc();
215   if (!doc) {
216     return IMEState(IMEState::DISABLED);
217   }
218   nsIPresShell* ps = doc->GetShell();
219   if (!ps) {
220     return IMEState(IMEState::DISABLED);
221   }
222   nsPresContext* pc = ps->GetPresContext();
223   if (!pc) {
224     return IMEState(IMEState::DISABLED);
225   }
226   nsIEditor* editor = nsContentUtils::GetHTMLEditor(pc);
227   nsCOMPtr<nsIEditorIMESupport> imeEditor = do_QueryInterface(editor);
228   if (!imeEditor) {
229     return IMEState(IMEState::DISABLED);
230   }
231   IMEState state;
232   imeEditor->GetPreferredIMEState(&state);
233   return state;
234 }
235 
236 bool
HasIndependentSelection()237 nsIContent::HasIndependentSelection()
238 {
239   nsIFrame* frame = GetPrimaryFrame();
240   return (frame && frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION);
241 }
242 
243 dom::Element*
GetEditingHost()244 nsIContent::GetEditingHost()
245 {
246   // If this isn't editable, return nullptr.
247   if (!IsEditableInternal()) {
248     return nullptr;
249   }
250 
251   nsIDocument* doc = GetComposedDoc();
252   if (!doc) {
253     return nullptr;
254   }
255 
256   // If this is in designMode, we should return <body>
257   if (doc->HasFlag(NODE_IS_EDITABLE) && !IsInShadowTree()) {
258     return doc->GetBodyElement();
259   }
260 
261   nsIContent* content = this;
262   for (dom::Element* parent = GetParentElement();
263        parent && parent->HasFlag(NODE_IS_EDITABLE);
264        parent = content->GetParentElement()) {
265     content = parent;
266   }
267   return content->AsElement();
268 }
269 
270 nsresult
LookupNamespaceURIInternal(const nsAString & aNamespacePrefix,nsAString & aNamespaceURI) const271 nsIContent::LookupNamespaceURIInternal(const nsAString& aNamespacePrefix,
272                                        nsAString& aNamespaceURI) const
273 {
274   if (aNamespacePrefix.EqualsLiteral("xml")) {
275     // Special-case for xml prefix
276     aNamespaceURI.AssignLiteral("http://www.w3.org/XML/1998/namespace");
277     return NS_OK;
278   }
279 
280   if (aNamespacePrefix.EqualsLiteral("xmlns")) {
281     // Special-case for xmlns prefix
282     aNamespaceURI.AssignLiteral("http://www.w3.org/2000/xmlns/");
283     return NS_OK;
284   }
285 
286   nsCOMPtr<nsIAtom> name;
287   if (!aNamespacePrefix.IsEmpty()) {
288     name = NS_Atomize(aNamespacePrefix);
289     NS_ENSURE_TRUE(name, NS_ERROR_OUT_OF_MEMORY);
290   }
291   else {
292     name = nsGkAtoms::xmlns;
293   }
294   // Trace up the content parent chain looking for the namespace
295   // declaration that declares aNamespacePrefix.
296   const nsIContent* content = this;
297   do {
298     if (content->GetAttr(kNameSpaceID_XMLNS, name, aNamespaceURI))
299       return NS_OK;
300   } while ((content = content->GetParent()));
301   return NS_ERROR_FAILURE;
302 }
303 
304 already_AddRefed<nsIURI>
GetBaseURI(bool aTryUseXHRDocBaseURI) const305 nsIContent::GetBaseURI(bool aTryUseXHRDocBaseURI) const
306 {
307   nsIDocument* doc = OwnerDoc();
308   // Start with document base
309   nsCOMPtr<nsIURI> base = doc->GetBaseURI(aTryUseXHRDocBaseURI);
310 
311   // Collect array of xml:base attribute values up the parent chain. This
312   // is slightly slower for the case when there are xml:base attributes, but
313   // faster for the far more common case of there not being any such
314   // attributes.
315   // Also check for SVG elements which require special handling
316   AutoTArray<nsString, 5> baseAttrs;
317   nsString attr;
318   const nsIContent *elem = this;
319   do {
320     // First check for SVG specialness (why is this SVG specific?)
321     if (elem->IsSVGElement()) {
322       nsIContent* bindingParent = elem->GetBindingParent();
323       if (bindingParent) {
324         nsXBLBinding* binding = bindingParent->GetXBLBinding();
325         if (binding) {
326           // XXX sXBL/XBL2 issue
327           // If this is an anonymous XBL element use the binding
328           // document for the base URI.
329           // XXX Will fail with xml:base
330           base = binding->PrototypeBinding()->DocURI();
331           break;
332         }
333       }
334     }
335 
336     nsIURI* explicitBaseURI = elem->GetExplicitBaseURI();
337     if (explicitBaseURI) {
338       base = explicitBaseURI;
339       break;
340     }
341 
342     // Otherwise check for xml:base attribute
343     elem->GetAttr(kNameSpaceID_XML, nsGkAtoms::base, attr);
344     if (!attr.IsEmpty()) {
345       baseAttrs.AppendElement(attr);
346     }
347     elem = elem->GetParent();
348   } while(elem);
349 
350   // Now resolve against all xml:base attrs
351   for (uint32_t i = baseAttrs.Length() - 1; i != uint32_t(-1); --i) {
352     nsCOMPtr<nsIURI> newBase;
353     nsresult rv = NS_NewURI(getter_AddRefs(newBase), baseAttrs[i],
354                             doc->GetDocumentCharacterSet().get(), base);
355     // Do a security check, almost the same as nsDocument::SetBaseURL()
356     // Only need to do this on the final uri
357     if (NS_SUCCEEDED(rv) && i == 0) {
358       rv = nsContentUtils::GetSecurityManager()->
359         CheckLoadURIWithPrincipal(NodePrincipal(), newBase,
360                                   nsIScriptSecurityManager::STANDARD);
361     }
362     if (NS_SUCCEEDED(rv)) {
363       base.swap(newBase);
364     }
365   }
366 
367   return base.forget();
368 }
369 
370 //----------------------------------------------------------------------
371 
372 static inline JSObject*
GetJSObjectChild(nsWrapperCache * aCache)373 GetJSObjectChild(nsWrapperCache* aCache)
374 {
375   return aCache->PreservingWrapper() ? aCache->GetWrapperPreserveColor() : nullptr;
376 }
377 
378 static bool
NeedsScriptTraverse(nsINode * aNode)379 NeedsScriptTraverse(nsINode* aNode)
380 {
381   return aNode->PreservingWrapper() && aNode->GetWrapperPreserveColor() &&
382          !aNode->IsBlackAndDoesNotNeedTracing(aNode);
383 }
384 
385 //----------------------------------------------------------------------
386 
387 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsChildContentList)
388 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsChildContentList)
389 
390 // If nsChildContentList is changed so that any additional fields are
391 // traversed by the cycle collector, then CAN_SKIP must be updated to
392 // check that the additional fields are null.
393 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsChildContentList)
394 
395 // nsChildContentList only ever has a single child, its wrapper, so if
396 // the wrapper is black, the list can't be part of a garbage cycle.
397 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsChildContentList)
398   return tmp->IsBlack();
399 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
400 
401 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsChildContentList)
402   return tmp->IsBlackAndDoesNotNeedTracing(tmp);
403 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
404 
405 // CanSkipThis returns false to avoid problems with incomplete unlinking.
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsChildContentList)406 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsChildContentList)
407 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
408 
409 NS_INTERFACE_TABLE_HEAD(nsChildContentList)
410   NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
411   NS_INTERFACE_TABLE(nsChildContentList, nsINodeList, nsIDOMNodeList)
412   NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsChildContentList)
413 NS_INTERFACE_MAP_END
414 
415 JSObject*
416 nsChildContentList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
417 {
418   return NodeListBinding::Wrap(cx, this, aGivenProto);
419 }
420 
421 NS_IMETHODIMP
GetLength(uint32_t * aLength)422 nsChildContentList::GetLength(uint32_t* aLength)
423 {
424   *aLength = mNode ? mNode->GetChildCount() : 0;
425 
426   return NS_OK;
427 }
428 
429 NS_IMETHODIMP
Item(uint32_t aIndex,nsIDOMNode ** aReturn)430 nsChildContentList::Item(uint32_t aIndex, nsIDOMNode** aReturn)
431 {
432   nsINode* node = Item(aIndex);
433   if (!node) {
434     *aReturn = nullptr;
435 
436     return NS_OK;
437   }
438 
439   return CallQueryInterface(node, aReturn);
440 }
441 
442 nsIContent*
Item(uint32_t aIndex)443 nsChildContentList::Item(uint32_t aIndex)
444 {
445   if (mNode) {
446     return mNode->GetChildAt(aIndex);
447   }
448 
449   return nullptr;
450 }
451 
452 int32_t
IndexOf(nsIContent * aContent)453 nsChildContentList::IndexOf(nsIContent* aContent)
454 {
455   if (mNode) {
456     return mNode->IndexOf(aContent);
457   }
458 
459   return -1;
460 }
461 
462 //----------------------------------------------------------------------
463 
464 nsIHTMLCollection*
Children()465 FragmentOrElement::Children()
466 {
467   FragmentOrElement::nsDOMSlots *slots = DOMSlots();
468 
469   if (!slots->mChildrenList) {
470     slots->mChildrenList = new nsContentList(this, kNameSpaceID_Wildcard,
471                                              nsGkAtoms::_asterisk, nsGkAtoms::_asterisk,
472                                              false);
473   }
474 
475   return slots->mChildrenList;
476 }
477 
478 
479 //----------------------------------------------------------------------
480 
481 
NS_IMPL_ISUPPORTS(nsNodeWeakReference,nsIWeakReference)482 NS_IMPL_ISUPPORTS(nsNodeWeakReference,
483                   nsIWeakReference)
484 
485 nsNodeWeakReference::~nsNodeWeakReference()
486 {
487   if (mNode) {
488     NS_ASSERTION(mNode->Slots()->mWeakReference == this,
489                  "Weak reference has wrong value");
490     mNode->Slots()->mWeakReference = nullptr;
491   }
492 }
493 
494 NS_IMETHODIMP
QueryReferent(const nsIID & aIID,void ** aInstancePtr)495 nsNodeWeakReference::QueryReferent(const nsIID& aIID, void** aInstancePtr)
496 {
497   return mNode ? mNode->QueryInterface(aIID, aInstancePtr) :
498                  NS_ERROR_NULL_POINTER;
499 }
500 
501 size_t
SizeOfOnlyThis(mozilla::MallocSizeOf aMallocSizeOf) const502 nsNodeWeakReference::SizeOfOnlyThis(mozilla::MallocSizeOf aMallocSizeOf) const
503 {
504   return aMallocSizeOf(this);
505 }
506 
507 
NS_IMPL_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff,mNode)508 NS_IMPL_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff, mNode)
509 
510 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff)
511   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
512 NS_INTERFACE_MAP_END_AGGREGATED(mNode)
513 
514 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNodeSupportsWeakRefTearoff)
515 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNodeSupportsWeakRefTearoff)
516 
517 NS_IMETHODIMP
518 nsNodeSupportsWeakRefTearoff::GetWeakReference(nsIWeakReference** aInstancePtr)
519 {
520   nsINode::nsSlots* slots = mNode->Slots();
521   if (!slots->mWeakReference) {
522     slots->mWeakReference = new nsNodeWeakReference(mNode);
523   }
524 
525   NS_ADDREF(*aInstancePtr = slots->mWeakReference);
526 
527   return NS_OK;
528 }
529 
530 //----------------------------------------------------------------------
nsDOMSlots()531 FragmentOrElement::nsDOMSlots::nsDOMSlots()
532   : nsINode::nsSlots(),
533     mDataset(nullptr),
534     mBindingParent(nullptr)
535 {
536 }
537 
~nsDOMSlots()538 FragmentOrElement::nsDOMSlots::~nsDOMSlots()
539 {
540   if (mAttributeMap) {
541     mAttributeMap->DropReference();
542   }
543 }
544 
545 void
Traverse(nsCycleCollectionTraversalCallback & cb,bool aIsXUL)546 FragmentOrElement::nsDOMSlots::Traverse(nsCycleCollectionTraversalCallback &cb, bool aIsXUL)
547 {
548   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mStyle");
549   cb.NoteXPCOMChild(mStyle.get());
550 
551   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mSMILOverrideStyle");
552   cb.NoteXPCOMChild(mSMILOverrideStyle.get());
553 
554   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mAttributeMap");
555   cb.NoteXPCOMChild(mAttributeMap.get());
556 
557   if (aIsXUL) {
558     NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mControllers");
559     cb.NoteXPCOMChild(mControllers);
560   }
561 
562   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLBinding");
563   cb.NoteNativeChild(mXBLBinding, NS_CYCLE_COLLECTION_PARTICIPANT(nsXBLBinding));
564 
565   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mXBLInsertionParent");
566   cb.NoteXPCOMChild(mXBLInsertionParent.get());
567 
568   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mShadowRoot");
569   cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mShadowRoot));
570 
571   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mContainingShadow");
572   cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent*, mContainingShadow));
573 
574   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mChildrenList");
575   cb.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList*, mChildrenList));
576 
577   NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mSlots->mClassList");
578   cb.NoteXPCOMChild(mClassList.get());
579 
580   if (mCustomElementData) {
581     for (uint32_t i = 0; i < mCustomElementData->mCallbackQueue.Length(); i++) {
582       mCustomElementData->mCallbackQueue[i]->Traverse(cb);
583     }
584   }
585 }
586 
587 void
Unlink(bool aIsXUL)588 FragmentOrElement::nsDOMSlots::Unlink(bool aIsXUL)
589 {
590   mStyle = nullptr;
591   mSMILOverrideStyle = nullptr;
592   if (mAttributeMap) {
593     mAttributeMap->DropReference();
594     mAttributeMap = nullptr;
595   }
596   if (aIsXUL)
597     NS_IF_RELEASE(mControllers);
598 
599   MOZ_ASSERT(!mXBLBinding);
600 
601   mXBLInsertionParent = nullptr;
602   mShadowRoot = nullptr;
603   mContainingShadow = nullptr;
604   mChildrenList = nullptr;
605   mCustomElementData = nullptr;
606   mClassList = nullptr;
607 }
608 
609 size_t
SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const610 FragmentOrElement::nsDOMSlots::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
611 {
612   size_t n = aMallocSizeOf(this);
613 
614   if (mAttributeMap) {
615     n += mAttributeMap->SizeOfIncludingThis(aMallocSizeOf);
616   }
617 
618   // Measurement of the following members may be added later if DMD finds it is
619   // worthwhile:
620   // - Superclass members (nsINode::nsSlots)
621   // - mStyle
622   // - mDataSet
623   // - mSMILOverrideStyle
624   // - mSMILOverrideStyleDeclaration
625   // - mChildrenList
626   // - mClassList
627 
628   // The following members are not measured:
629   // - mBindingParent / mControllers: because they're   non-owning
630   return n;
631 }
632 
FragmentOrElement(already_AddRefed<mozilla::dom::NodeInfo> & aNodeInfo)633 FragmentOrElement::FragmentOrElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
634   : nsIContent(aNodeInfo)
635 {
636 }
637 
FragmentOrElement(already_AddRefed<mozilla::dom::NodeInfo> && aNodeInfo)638 FragmentOrElement::FragmentOrElement(already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo)
639   : nsIContent(aNodeInfo)
640 {
641 }
642 
~FragmentOrElement()643 FragmentOrElement::~FragmentOrElement()
644 {
645   NS_PRECONDITION(!IsInUncomposedDoc(),
646                   "Please remove this from the document properly");
647   if (GetParent()) {
648     NS_RELEASE(mParent);
649   }
650 }
651 
652 already_AddRefed<nsINodeList>
GetChildren(uint32_t aFilter)653 FragmentOrElement::GetChildren(uint32_t aFilter)
654 {
655   RefPtr<nsSimpleContentList> list = new nsSimpleContentList(this);
656   AllChildrenIterator iter(this, aFilter);
657   while (nsIContent* kid = iter.GetNextChild()) {
658     list->AppendElement(kid);
659   }
660 
661   return list.forget();
662 }
663 
664 static nsIContent*
FindChromeAccessOnlySubtreeOwner(nsIContent * aContent)665 FindChromeAccessOnlySubtreeOwner(nsIContent* aContent)
666 {
667   if (aContent->ChromeOnlyAccess()) {
668     bool chromeAccessOnly = false;
669     while (aContent && !chromeAccessOnly) {
670       chromeAccessOnly = aContent->IsRootOfChromeAccessOnlySubtree();
671       aContent = aContent->GetParent();
672     }
673   }
674   return aContent;
675 }
676 
677 nsresult
PreHandleEvent(EventChainPreVisitor & aVisitor)678 nsIContent::PreHandleEvent(EventChainPreVisitor& aVisitor)
679 {
680   //FIXME! Document how this event retargeting works, Bug 329124.
681   aVisitor.mCanHandle = true;
682   aVisitor.mMayHaveListenerManager = HasListenerManager();
683 
684   // Don't propagate mouseover and mouseout events when mouse is moving
685   // inside chrome access only content.
686   bool isAnonForEvents = IsRootOfChromeAccessOnlySubtree();
687   if ((aVisitor.mEvent->mMessage == eMouseOver ||
688        aVisitor.mEvent->mMessage == eMouseOut ||
689        aVisitor.mEvent->mMessage == ePointerOver ||
690        aVisitor.mEvent->mMessage == ePointerOut) &&
691       // Check if we should stop event propagation when event has just been
692       // dispatched or when we're about to propagate from
693       // chrome access only subtree or if we are about to propagate out of
694       // a shadow root to a shadow root host.
695       ((this == aVisitor.mEvent->mOriginalTarget &&
696         !ChromeOnlyAccess()) || isAnonForEvents || GetShadowRoot())) {
697      nsCOMPtr<nsIContent> relatedTarget =
698        do_QueryInterface(aVisitor.mEvent->AsMouseEvent()->relatedTarget);
699     if (relatedTarget &&
700         relatedTarget->OwnerDoc() == OwnerDoc()) {
701 
702       // In the web components case, we may need to stop propagation of events
703       // at shadow root host.
704       if (GetShadowRoot()) {
705         nsIContent* adjustedTarget =
706           Event::GetShadowRelatedTarget(this, relatedTarget);
707         if (this == adjustedTarget) {
708           aVisitor.mParentTarget = nullptr;
709           aVisitor.mCanHandle = false;
710           return NS_OK;
711         }
712       }
713 
714       // If current target is anonymous for events or we know that related
715       // target is descendant of an element which is anonymous for events,
716       // we may want to stop event propagation.
717       // If this is the original target, aVisitor.mRelatedTargetIsInAnon
718       // must be updated.
719       if (isAnonForEvents || aVisitor.mRelatedTargetIsInAnon ||
720           (aVisitor.mEvent->mOriginalTarget == this &&
721            (aVisitor.mRelatedTargetIsInAnon =
722             relatedTarget->ChromeOnlyAccess()))) {
723         nsIContent* anonOwner = FindChromeAccessOnlySubtreeOwner(this);
724         if (anonOwner) {
725           nsIContent* anonOwnerRelated =
726             FindChromeAccessOnlySubtreeOwner(relatedTarget);
727           if (anonOwnerRelated) {
728             // Note, anonOwnerRelated may still be inside some other
729             // native anonymous subtree. The case where anonOwner is still
730             // inside native anonymous subtree will be handled when event
731             // propagates up in the DOM tree.
732             while (anonOwner != anonOwnerRelated &&
733                    anonOwnerRelated->ChromeOnlyAccess()) {
734               anonOwnerRelated = FindChromeAccessOnlySubtreeOwner(anonOwnerRelated);
735             }
736             if (anonOwner == anonOwnerRelated) {
737 #ifdef DEBUG_smaug
738               nsCOMPtr<nsIContent> originalTarget =
739                 do_QueryInterface(aVisitor.mEvent->mOriginalTarget);
740               nsAutoString ot, ct, rt;
741               if (originalTarget) {
742                 originalTarget->NodeInfo()->NameAtom()->ToString(ot);
743               }
744               NodeInfo()->NameAtom()->ToString(ct);
745               relatedTarget->NodeInfo()->NameAtom()->ToString(rt);
746               printf("Stopping %s propagation:"
747                      "\n\toriginalTarget=%s \n\tcurrentTarget=%s %s"
748                      "\n\trelatedTarget=%s %s \n%s",
749                      (aVisitor.mEvent->mMessage == eMouseOver)
750                        ? "mouseover" : "mouseout",
751                      NS_ConvertUTF16toUTF8(ot).get(),
752                      NS_ConvertUTF16toUTF8(ct).get(),
753                      isAnonForEvents
754                        ? "(is native anonymous)"
755                        : (ChromeOnlyAccess()
756                            ? "(is in native anonymous subtree)" : ""),
757                      NS_ConvertUTF16toUTF8(rt).get(),
758                      relatedTarget->ChromeOnlyAccess()
759                        ? "(is in native anonymous subtree)" : "",
760                      (originalTarget &&
761                       relatedTarget->FindFirstNonChromeOnlyAccessContent() ==
762                         originalTarget->FindFirstNonChromeOnlyAccessContent())
763                        ? "" : "Wrong event propagation!?!\n");
764 #endif
765               aVisitor.mParentTarget = nullptr;
766               // Event should not propagate to non-anon content.
767               aVisitor.mCanHandle = isAnonForEvents;
768               return NS_OK;
769             }
770           }
771         }
772       }
773     }
774   }
775 
776   nsIContent* parent = GetParent();
777 
778   // Web components have a special event chain that need to account
779   // for destination insertion points where nodes have been distributed.
780   nsTArray<nsIContent*>* destPoints = GetExistingDestInsertionPoints();
781   if (destPoints && !destPoints->IsEmpty()) {
782     // Push destination insertion points to aVisitor.mDestInsertionPoints
783     // excluding shadow insertion points.
784     bool didPushNonShadowInsertionPoint = false;
785     for (uint32_t i = 0; i < destPoints->Length(); i++) {
786       nsIContent* point = destPoints->ElementAt(i);
787       if (!ShadowRoot::IsShadowInsertionPoint(point)) {
788         aVisitor.mDestInsertionPoints.AppendElement(point);
789         didPushNonShadowInsertionPoint = true;
790       }
791     }
792 
793     // Next node in the event path is the final destination
794     // (non-shadow) insertion point that was pushed.
795     if (didPushNonShadowInsertionPoint) {
796       parent = aVisitor.mDestInsertionPoints.LastElement();
797       aVisitor.mDestInsertionPoints.SetLength(
798         aVisitor.mDestInsertionPoints.Length() - 1);
799     }
800   }
801 
802   ShadowRoot* thisShadowRoot = ShadowRoot::FromNode(this);
803   if (thisShadowRoot) {
804     if (!aVisitor.mEvent->mFlags.mComposed) {
805       // If we do stop propagation, we still want to propagate
806       // the event to chrome (nsPIDOMWindow::GetParentTarget()).
807       // The load event is special in that we don't ever propagate it
808       // to chrome.
809       nsCOMPtr<nsPIDOMWindowOuter> win = OwnerDoc()->GetWindow();
810       EventTarget* parentTarget = win && aVisitor.mEvent->mMessage != eLoad
811         ? win->GetParentTarget() : nullptr;
812 
813       aVisitor.mParentTarget = parentTarget;
814       return NS_OK;
815     }
816 
817     if (!aVisitor.mDestInsertionPoints.IsEmpty()) {
818       parent = aVisitor.mDestInsertionPoints.LastElement();
819       aVisitor.mDestInsertionPoints.SetLength(
820         aVisitor.mDestInsertionPoints.Length() - 1);
821     } else {
822       // The pool host for the youngest shadow root is shadow DOM host,
823       // for older shadow roots, it is the shadow insertion point
824       // where the shadow root is projected, nullptr if none exists.
825       parent = thisShadowRoot->GetPoolHost();
826     }
827   }
828 
829   // Event may need to be retargeted if this is the root of a native
830   // anonymous content subtree or event is dispatched somewhere inside XBL.
831   if (isAnonForEvents) {
832 #ifdef DEBUG
833     // If a DOM event is explicitly dispatched using node.dispatchEvent(), then
834     // all the events are allowed even in the native anonymous content..
835     nsCOMPtr<nsIContent> t =
836       do_QueryInterface(aVisitor.mEvent->mOriginalTarget);
837     NS_ASSERTION(!t || !t->ChromeOnlyAccess() ||
838                  aVisitor.mEvent->mClass != eMutationEventClass ||
839                  aVisitor.mDOMEvent,
840                  "Mutation event dispatched in native anonymous content!?!");
841 #endif
842     aVisitor.mEventTargetAtParent = parent;
843   } else if (parent && aVisitor.mOriginalTargetIsInAnon) {
844     nsCOMPtr<nsIContent> content(do_QueryInterface(aVisitor.mEvent->mTarget));
845     if (content && content->GetBindingParent() == parent) {
846       aVisitor.mEventTargetAtParent = parent;
847     }
848   }
849 
850   // check for an anonymous parent
851   // XXX XBL2/sXBL issue
852   if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
853     nsIContent* insertionParent = GetXBLInsertionParent();
854     NS_ASSERTION(!(aVisitor.mEventTargetAtParent && insertionParent &&
855                    aVisitor.mEventTargetAtParent != insertionParent),
856                  "Retargeting and having insertion parent!");
857     if (insertionParent) {
858       parent = insertionParent;
859     }
860   }
861 
862   if (!aVisitor.mEvent->mFlags.mComposedInNativeAnonymousContent &&
863       IsRootOfNativeAnonymousSubtree() && OwnerDoc() &&
864       OwnerDoc()->GetWindow()) {
865     aVisitor.mParentTarget = OwnerDoc()->GetWindow()->GetParentTarget();
866   } else if (parent) {
867     aVisitor.mParentTarget = parent;
868   } else {
869     aVisitor.mParentTarget = GetComposedDoc();
870   }
871   return NS_OK;
872 }
873 
874 bool
GetAttr(int32_t aNameSpaceID,nsIAtom * aName,nsAString & aResult) const875 nsIContent::GetAttr(int32_t aNameSpaceID, nsIAtom* aName,
876                     nsAString& aResult) const
877 {
878   if (IsElement()) {
879     return AsElement()->GetAttr(aNameSpaceID, aName, aResult);
880   }
881   aResult.Truncate();
882   return false;
883 }
884 
885 bool
HasAttr(int32_t aNameSpaceID,nsIAtom * aName) const886 nsIContent::HasAttr(int32_t aNameSpaceID, nsIAtom* aName) const
887 {
888   return IsElement() && AsElement()->HasAttr(aNameSpaceID, aName);
889 }
890 
891 bool
AttrValueIs(int32_t aNameSpaceID,nsIAtom * aName,const nsAString & aValue,nsCaseTreatment aCaseSensitive) const892 nsIContent::AttrValueIs(int32_t aNameSpaceID,
893                         nsIAtom* aName,
894                         const nsAString& aValue,
895                         nsCaseTreatment aCaseSensitive) const
896 {
897   return IsElement() &&
898     AsElement()->AttrValueIs(aNameSpaceID, aName, aValue, aCaseSensitive);
899 }
900 
901 bool
AttrValueIs(int32_t aNameSpaceID,nsIAtom * aName,nsIAtom * aValue,nsCaseTreatment aCaseSensitive) const902 nsIContent::AttrValueIs(int32_t aNameSpaceID,
903                         nsIAtom* aName,
904                         nsIAtom* aValue,
905                         nsCaseTreatment aCaseSensitive) const
906 {
907   return IsElement() &&
908     AsElement()->AttrValueIs(aNameSpaceID, aName, aValue, aCaseSensitive);
909 }
910 
911 bool
IsFocusable(int32_t * aTabIndex,bool aWithMouse)912 nsIContent::IsFocusable(int32_t* aTabIndex, bool aWithMouse)
913 {
914   bool focusable = IsFocusableInternal(aTabIndex, aWithMouse);
915   // Ensure that the return value and aTabIndex are consistent in the case
916   // we're in userfocusignored context.
917   if (focusable || (aTabIndex && *aTabIndex != -1)) {
918     if (nsContentUtils::IsUserFocusIgnored(this)) {
919       if (aTabIndex) {
920         *aTabIndex = -1;
921       }
922       return false;
923     }
924     return focusable;
925   }
926   return false;
927 }
928 
929 bool
IsFocusableInternal(int32_t * aTabIndex,bool aWithMouse)930 nsIContent::IsFocusableInternal(int32_t* aTabIndex, bool aWithMouse)
931 {
932   if (aTabIndex) {
933     *aTabIndex = -1; // Default, not tabbable
934   }
935   return false;
936 }
937 
938 NS_IMETHODIMP
WalkContentStyleRules(nsRuleWalker * aRuleWalker)939 FragmentOrElement::WalkContentStyleRules(nsRuleWalker* aRuleWalker)
940 {
941   return NS_OK;
942 }
943 
944 bool
IsLink(nsIURI ** aURI) const945 FragmentOrElement::IsLink(nsIURI** aURI) const
946 {
947   *aURI = nullptr;
948   return false;
949 }
950 
951 nsIContent*
GetBindingParent() const952 FragmentOrElement::GetBindingParent() const
953 {
954   nsDOMSlots *slots = GetExistingDOMSlots();
955 
956   if (slots) {
957     return slots->mBindingParent;
958   }
959   return nullptr;
960 }
961 
962 nsXBLBinding*
GetXBLBinding() const963 FragmentOrElement::GetXBLBinding() const
964 {
965   if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
966     nsDOMSlots *slots = GetExistingDOMSlots();
967     if (slots) {
968       return slots->mXBLBinding;
969     }
970   }
971 
972   return nullptr;
973 }
974 
975 void
SetXBLBinding(nsXBLBinding * aBinding,nsBindingManager * aOldBindingManager)976 FragmentOrElement::SetXBLBinding(nsXBLBinding* aBinding,
977                                  nsBindingManager* aOldBindingManager)
978 {
979   nsBindingManager* bindingManager;
980   if (aOldBindingManager) {
981     MOZ_ASSERT(!aBinding, "aOldBindingManager should only be provided "
982                           "when removing a binding.");
983     bindingManager = aOldBindingManager;
984   } else {
985     bindingManager = OwnerDoc()->BindingManager();
986   }
987 
988   // After this point, aBinding will be the most-derived binding for aContent.
989   // If we already have a binding for aContent, make sure to
990   // remove it from the attached stack.  Otherwise we might end up firing its
991   // constructor twice (if aBinding inherits from it) or firing its constructor
992   // after aContent has been deleted (if aBinding is null and the content node
993   // dies before we process mAttachedStack).
994   RefPtr<nsXBLBinding> oldBinding = GetXBLBinding();
995   if (oldBinding) {
996     bindingManager->RemoveFromAttachedQueue(oldBinding);
997   }
998 
999   if (aBinding) {
1000     SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
1001     nsDOMSlots *slots = DOMSlots();
1002     slots->mXBLBinding = aBinding;
1003     bindingManager->AddBoundContent(this);
1004   } else {
1005     nsDOMSlots *slots = GetExistingDOMSlots();
1006     if (slots) {
1007       slots->mXBLBinding = nullptr;
1008     }
1009     bindingManager->RemoveBoundContent(this);
1010     if (oldBinding) {
1011       oldBinding->SetBoundElement(nullptr);
1012     }
1013   }
1014 }
1015 
1016 nsIContent*
GetXBLInsertionParent() const1017 FragmentOrElement::GetXBLInsertionParent() const
1018 {
1019   if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR)) {
1020     nsDOMSlots *slots = GetExistingDOMSlots();
1021     if (slots) {
1022       return slots->mXBLInsertionParent;
1023     }
1024   }
1025 
1026   return nullptr;
1027 }
1028 
1029 ShadowRoot*
GetContainingShadow() const1030 FragmentOrElement::GetContainingShadow() const
1031 {
1032   nsDOMSlots *slots = GetExistingDOMSlots();
1033   if (slots) {
1034     return slots->mContainingShadow;
1035   }
1036   return nullptr;
1037 }
1038 
1039 void
SetShadowRoot(ShadowRoot * aShadowRoot)1040 FragmentOrElement::SetShadowRoot(ShadowRoot* aShadowRoot)
1041 {
1042   nsDOMSlots *slots = DOMSlots();
1043   slots->mShadowRoot = aShadowRoot;
1044 }
1045 
1046 nsTArray<nsIContent*>&
DestInsertionPoints()1047 FragmentOrElement::DestInsertionPoints()
1048 {
1049   nsDOMSlots *slots = DOMSlots();
1050   return slots->mDestInsertionPoints;
1051 }
1052 
1053 nsTArray<nsIContent*>*
GetExistingDestInsertionPoints() const1054 FragmentOrElement::GetExistingDestInsertionPoints() const
1055 {
1056   nsDOMSlots *slots = GetExistingDOMSlots();
1057   if (slots) {
1058     return &slots->mDestInsertionPoints;
1059   }
1060   return nullptr;
1061 }
1062 
1063 void
SetXBLInsertionParent(nsIContent * aContent)1064 FragmentOrElement::SetXBLInsertionParent(nsIContent* aContent)
1065 {
1066   if (aContent) {
1067     nsDOMSlots *slots = DOMSlots();
1068     SetFlags(NODE_MAY_BE_IN_BINDING_MNGR);
1069     slots->mXBLInsertionParent = aContent;
1070   } else {
1071     nsDOMSlots *slots = GetExistingDOMSlots();
1072     if (slots) {
1073       slots->mXBLInsertionParent = nullptr;
1074     }
1075   }
1076 }
1077 
1078 CustomElementData*
GetCustomElementData() const1079 FragmentOrElement::GetCustomElementData() const
1080 {
1081   nsDOMSlots *slots = GetExistingDOMSlots();
1082   if (slots) {
1083     return slots->mCustomElementData;
1084   }
1085   return nullptr;
1086 }
1087 
1088 void
SetCustomElementData(CustomElementData * aData)1089 FragmentOrElement::SetCustomElementData(CustomElementData* aData)
1090 {
1091   nsDOMSlots *slots = DOMSlots();
1092   MOZ_ASSERT(!slots->mCustomElementData, "Custom element data may not be changed once set.");
1093   slots->mCustomElementData = aData;
1094 }
1095 
1096 nsresult
InsertChildAt(nsIContent * aKid,uint32_t aIndex,bool aNotify)1097 FragmentOrElement::InsertChildAt(nsIContent* aKid,
1098                                 uint32_t aIndex,
1099                                 bool aNotify)
1100 {
1101   NS_PRECONDITION(aKid, "null ptr");
1102 
1103   return doInsertChildAt(aKid, aIndex, aNotify, mAttrsAndChildren);
1104 }
1105 
1106 void
RemoveChildAt(uint32_t aIndex,bool aNotify)1107 FragmentOrElement::RemoveChildAt(uint32_t aIndex, bool aNotify)
1108 {
1109   nsCOMPtr<nsIContent> oldKid = mAttrsAndChildren.GetSafeChildAt(aIndex);
1110   NS_ASSERTION(oldKid == GetChildAt(aIndex), "Unexpected child in RemoveChildAt");
1111 
1112   if (oldKid) {
1113     doRemoveChildAt(aIndex, aNotify, oldKid, mAttrsAndChildren);
1114   }
1115 }
1116 
1117 void
GetTextContentInternal(nsAString & aTextContent,ErrorResult & aError)1118 FragmentOrElement::GetTextContentInternal(nsAString& aTextContent,
1119                                           ErrorResult& aError)
1120 {
1121   if (!nsContentUtils::GetNodeTextContent(this, true, aTextContent, fallible)) {
1122     aError.Throw(NS_ERROR_OUT_OF_MEMORY);
1123   }
1124 }
1125 
1126 void
SetTextContentInternal(const nsAString & aTextContent,ErrorResult & aError)1127 FragmentOrElement::SetTextContentInternal(const nsAString& aTextContent,
1128                                           ErrorResult& aError)
1129 {
1130   aError = nsContentUtils::SetNodeTextContent(this, aTextContent, false);
1131 }
1132 
1133 void
DestroyContent()1134 FragmentOrElement::DestroyContent()
1135 {
1136   nsIDocument *document = OwnerDoc();
1137   document->BindingManager()->RemovedFromDocument(this, document,
1138                                                   nsBindingManager::eRunDtor);
1139   document->ClearBoxObjectFor(this);
1140 
1141   uint32_t i, count = mAttrsAndChildren.ChildCount();
1142   for (i = 0; i < count; ++i) {
1143     // The child can remove itself from the parent in BindToTree.
1144     mAttrsAndChildren.ChildAt(i)->DestroyContent();
1145   }
1146   ShadowRoot* shadowRoot = GetShadowRoot();
1147   if (shadowRoot) {
1148     shadowRoot->DestroyContent();
1149   }
1150 }
1151 
1152 void
SaveSubtreeState()1153 FragmentOrElement::SaveSubtreeState()
1154 {
1155   uint32_t i, count = mAttrsAndChildren.ChildCount();
1156   for (i = 0; i < count; ++i) {
1157     mAttrsAndChildren.ChildAt(i)->SaveSubtreeState();
1158   }
1159 }
1160 
1161 //----------------------------------------------------------------------
1162 
1163 // Generic DOMNode implementations
1164 
1165 void
FireNodeInserted(nsIDocument * aDoc,nsINode * aParent,nsTArray<nsCOMPtr<nsIContent>> & aNodes)1166 FragmentOrElement::FireNodeInserted(nsIDocument* aDoc,
1167                                    nsINode* aParent,
1168                                    nsTArray<nsCOMPtr<nsIContent> >& aNodes)
1169 {
1170   uint32_t count = aNodes.Length();
1171   for (uint32_t i = 0; i < count; ++i) {
1172     nsIContent* childContent = aNodes[i];
1173 
1174     if (nsContentUtils::HasMutationListeners(childContent,
1175           NS_EVENT_BITS_MUTATION_NODEINSERTED, aParent)) {
1176       InternalMutationEvent mutation(true, eLegacyNodeInserted);
1177       mutation.mRelatedNode = do_QueryInterface(aParent);
1178 
1179       mozAutoSubtreeModified subtree(aDoc, aParent);
1180       (new AsyncEventDispatcher(childContent, mutation))->RunDOMEventWhenSafe();
1181     }
1182   }
1183 }
1184 
1185 //----------------------------------------------------------------------
1186 
1187 // nsISupports implementation
1188 
1189 #define SUBTREE_UNBINDINGS_PER_RUNNABLE 500
1190 
1191 class ContentUnbinder : public Runnable
1192 {
1193 public:
ContentUnbinder()1194   ContentUnbinder()
1195   {
1196     mLast = this;
1197   }
1198 
~ContentUnbinder()1199   ~ContentUnbinder()
1200   {
1201     Run();
1202   }
1203 
UnbindSubtree(nsIContent * aNode)1204   void UnbindSubtree(nsIContent* aNode)
1205   {
1206     if (aNode->NodeType() != nsIDOMNode::ELEMENT_NODE &&
1207         aNode->NodeType() != nsIDOMNode::DOCUMENT_FRAGMENT_NODE) {
1208       return;
1209     }
1210     FragmentOrElement* container = static_cast<FragmentOrElement*>(aNode);
1211     uint32_t childCount = container->mAttrsAndChildren.ChildCount();
1212     if (childCount) {
1213       while (childCount-- > 0) {
1214         // Hold a strong ref to the node when we remove it, because we may be
1215         // the last reference to it.  We need to call TakeChildAt() and
1216         // update mFirstChild before calling UnbindFromTree, since this last
1217         // can notify various observers and they should really see consistent
1218         // tree state.
1219         nsCOMPtr<nsIContent> child =
1220           container->mAttrsAndChildren.TakeChildAt(childCount);
1221         if (childCount == 0) {
1222           container->mFirstChild = nullptr;
1223         }
1224         UnbindSubtree(child);
1225         child->UnbindFromTree();
1226       }
1227     }
1228   }
1229 
Run()1230   NS_IMETHOD Run() override
1231   {
1232     nsAutoScriptBlocker scriptBlocker;
1233     uint32_t len = mSubtreeRoots.Length();
1234     if (len) {
1235       for (uint32_t i = 0; i < len; ++i) {
1236         UnbindSubtree(mSubtreeRoots[i]);
1237       }
1238       mSubtreeRoots.Clear();
1239     }
1240     nsCycleCollector_dispatchDeferredDeletion();
1241     if (this == sContentUnbinder) {
1242       sContentUnbinder = nullptr;
1243       if (mNext) {
1244         RefPtr<ContentUnbinder> next;
1245         next.swap(mNext);
1246         sContentUnbinder = next;
1247         next->mLast = mLast;
1248         mLast = nullptr;
1249         NS_DispatchToMainThread(next);
1250       }
1251     }
1252     return NS_OK;
1253   }
1254 
UnbindAll()1255   static void UnbindAll()
1256   {
1257     RefPtr<ContentUnbinder> ub = sContentUnbinder;
1258     sContentUnbinder = nullptr;
1259     while (ub) {
1260       ub->Run();
1261       ub = ub->mNext;
1262     }
1263   }
1264 
Append(nsIContent * aSubtreeRoot)1265   static void Append(nsIContent* aSubtreeRoot)
1266   {
1267     if (!sContentUnbinder) {
1268       sContentUnbinder = new ContentUnbinder();
1269       nsCOMPtr<nsIRunnable> e = sContentUnbinder;
1270       NS_DispatchToMainThread(e);
1271     }
1272 
1273     if (sContentUnbinder->mLast->mSubtreeRoots.Length() >=
1274         SUBTREE_UNBINDINGS_PER_RUNNABLE) {
1275       sContentUnbinder->mLast->mNext = new ContentUnbinder();
1276       sContentUnbinder->mLast = sContentUnbinder->mLast->mNext;
1277     }
1278     sContentUnbinder->mLast->mSubtreeRoots.AppendElement(aSubtreeRoot);
1279   }
1280 
1281 private:
1282   AutoTArray<nsCOMPtr<nsIContent>,
1283                SUBTREE_UNBINDINGS_PER_RUNNABLE> mSubtreeRoots;
1284   RefPtr<ContentUnbinder>                     mNext;
1285   ContentUnbinder*                              mLast;
1286   static ContentUnbinder*                       sContentUnbinder;
1287 };
1288 
1289 ContentUnbinder* ContentUnbinder::sContentUnbinder = nullptr;
1290 
1291 void
ClearContentUnbinder()1292 FragmentOrElement::ClearContentUnbinder()
1293 {
1294   ContentUnbinder::UnbindAll();
1295 }
1296 
1297 NS_IMPL_CYCLE_COLLECTION_CLASS(FragmentOrElement)
1298 
1299 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement)
1300   nsINode::Unlink(tmp);
1301 
1302   // The XBL binding is removed by RemoveFromBindingManagerRunnable
1303   // which is dispatched in UnbindFromTree.
1304 
1305   if (tmp->HasProperties()) {
1306     if (tmp->IsHTMLElement() || tmp->IsSVGElement()) {
1307       nsIAtom*** props = Element::HTMLSVGPropertiesToTraverseAndUnlink();
1308       for (uint32_t i = 0; props[i]; ++i) {
1309         tmp->DeleteProperty(*props[i]);
1310       }
1311       if (tmp->MayHaveAnimations()) {
1312         nsIAtom** effectProps = EffectSet::GetEffectSetPropertyAtoms();
1313         for (uint32_t i = 0; effectProps[i]; ++i) {
1314           tmp->DeleteProperty(effectProps[i]);
1315         }
1316       }
1317     }
1318   }
1319 
1320   // Unlink child content (and unbind our subtree).
1321   if (tmp->UnoptimizableCCNode() || !nsCCUncollectableMarker::sGeneration) {
1322     uint32_t childCount = tmp->mAttrsAndChildren.ChildCount();
1323     if (childCount) {
1324       // Don't allow script to run while we're unbinding everything.
1325       nsAutoScriptBlocker scriptBlocker;
1326       while (childCount-- > 0) {
1327         // Hold a strong ref to the node when we remove it, because we may be
1328         // the last reference to it.  We need to call TakeChildAt() and
1329         // update mFirstChild before calling UnbindFromTree, since this last
1330         // can notify various observers and they should really see consistent
1331         // tree state.
1332         nsCOMPtr<nsIContent> child = tmp->mAttrsAndChildren.TakeChildAt(childCount);
1333         if (childCount == 0) {
1334           tmp->mFirstChild = nullptr;
1335         }
1336         child->UnbindFromTree();
1337       }
1338     }
1339   } else if (!tmp->GetParent() && tmp->mAttrsAndChildren.ChildCount()) {
1340     ContentUnbinder::Append(tmp);
1341   } /* else {
1342     The subtree root will end up to a ContentUnbinder, and that will
1343     unbind the child nodes.
1344   } */
1345 
1346   // Clear flag here because unlinking slots will clear the
1347   // containing shadow root pointer.
1348   tmp->UnsetFlags(NODE_IS_IN_SHADOW_TREE);
1349 
1350   nsIDocument* doc = tmp->OwnerDoc();
1351   doc->BindingManager()->RemovedFromDocument(tmp, doc,
1352                                              nsBindingManager::eDoNotRunDtor);
1353 
1354   // Unlink any DOM slots of interest.
1355   {
1356     nsDOMSlots *slots = tmp->GetExistingDOMSlots();
1357     if (slots) {
1358       slots->Unlink(tmp->IsXULElement());
1359     }
1360   }
1361 
1362 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1363 
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(FragmentOrElement)1364 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(FragmentOrElement)
1365 
1366 void
1367 FragmentOrElement::MarkUserData(void* aObject, nsIAtom* aKey, void* aChild,
1368                                void* aData)
1369 {
1370   uint32_t* gen = static_cast<uint32_t*>(aData);
1371   xpc_MarkInCCGeneration(static_cast<nsISupports*>(aChild), *gen);
1372 }
1373 
1374 void
MarkNodeChildren(nsINode * aNode)1375 FragmentOrElement::MarkNodeChildren(nsINode* aNode)
1376 {
1377   JSObject* o = GetJSObjectChild(aNode);
1378   if (o) {
1379     JS::ExposeObjectToActiveJS(o);
1380   }
1381 
1382   EventListenerManager* elm = aNode->GetExistingListenerManager();
1383   if (elm) {
1384     elm->MarkForCC();
1385   }
1386 
1387   if (aNode->HasProperties()) {
1388     nsIDocument* ownerDoc = aNode->OwnerDoc();
1389     ownerDoc->PropertyTable(DOM_USER_DATA)->
1390       Enumerate(aNode, FragmentOrElement::MarkUserData,
1391                 &nsCCUncollectableMarker::sGeneration);
1392   }
1393 }
1394 
1395 nsINode*
FindOptimizableSubtreeRoot(nsINode * aNode)1396 FindOptimizableSubtreeRoot(nsINode* aNode)
1397 {
1398   nsINode* p;
1399   while ((p = aNode->GetParentNode())) {
1400     if (aNode->UnoptimizableCCNode()) {
1401       return nullptr;
1402     }
1403     aNode = p;
1404   }
1405 
1406   if (aNode->UnoptimizableCCNode()) {
1407     return nullptr;
1408   }
1409   return aNode;
1410 }
1411 
1412 StaticAutoPtr<nsTHashtable<nsPtrHashKey<nsINode>>> gCCBlackMarkedNodes;
1413 
1414 static void
ClearBlackMarkedNodes()1415 ClearBlackMarkedNodes()
1416 {
1417   if (!gCCBlackMarkedNodes) {
1418     return;
1419   }
1420   for (auto iter = gCCBlackMarkedNodes->ConstIter(); !iter.Done();
1421        iter.Next()) {
1422     nsINode* n = iter.Get()->GetKey();
1423     n->SetCCMarkedRoot(false);
1424     n->SetInCCBlackTree(false);
1425   }
1426   gCCBlackMarkedNodes = nullptr;
1427 }
1428 
1429 // static
1430 void
RemoveBlackMarkedNode(nsINode * aNode)1431 FragmentOrElement::RemoveBlackMarkedNode(nsINode* aNode)
1432 {
1433   if (!gCCBlackMarkedNodes) {
1434     return;
1435   }
1436   gCCBlackMarkedNodes->RemoveEntry(aNode);
1437 }
1438 
1439 static bool
IsCertainlyAliveNode(nsINode * aNode,nsIDocument * aDoc)1440 IsCertainlyAliveNode(nsINode* aNode, nsIDocument* aDoc)
1441 {
1442   MOZ_ASSERT(aNode->GetUncomposedDoc() == aDoc);
1443 
1444   // Marked to be in-CC-generation or if the document is an svg image that's
1445   // being kept alive by the image cache. (Note that an svg image's internal
1446   // SVG document will receive an OnPageHide() call when it gets purged from
1447   // the image cache; hence, we use IsVisible() as a hint that the document is
1448   // actively being kept alive by the cache.)
1449   return nsCCUncollectableMarker::InGeneration(aDoc->GetMarkedCCGeneration()) ||
1450          (nsCCUncollectableMarker::sGeneration &&
1451           aDoc->IsBeingUsedAsImage() &&
1452           aDoc->IsVisible());
1453 }
1454 
1455 // static
1456 bool
CanSkipInCC(nsINode * aNode)1457 FragmentOrElement::CanSkipInCC(nsINode* aNode)
1458 {
1459   // Don't try to optimize anything during shutdown.
1460   if (nsCCUncollectableMarker::sGeneration == 0) {
1461     return false;
1462   }
1463 
1464   //XXXsmaug Need to figure out in which cases Shadow DOM can be optimized out
1465   //         from the CC graph.
1466   nsIDocument* currentDoc = aNode->GetUncomposedDoc();
1467   if (currentDoc && IsCertainlyAliveNode(aNode, currentDoc)) {
1468     return !NeedsScriptTraverse(aNode);
1469   }
1470 
1471   // Bail out early if aNode is somewhere in anonymous content,
1472   // or otherwise unusual.
1473   if (aNode->UnoptimizableCCNode()) {
1474     return false;
1475   }
1476 
1477   nsINode* root =
1478     currentDoc ? static_cast<nsINode*>(currentDoc) :
1479                  FindOptimizableSubtreeRoot(aNode);
1480   if (!root) {
1481     return false;
1482   }
1483 
1484   // Subtree has been traversed already.
1485   if (root->CCMarkedRoot()) {
1486     return root->InCCBlackTree() && !NeedsScriptTraverse(aNode);
1487   }
1488 
1489   if (!gCCBlackMarkedNodes) {
1490     gCCBlackMarkedNodes = new nsTHashtable<nsPtrHashKey<nsINode> >(1020);
1491   }
1492 
1493   // nodesToUnpurple contains nodes which will be removed
1494   // from the purple buffer if the DOM tree is black.
1495   AutoTArray<nsIContent*, 1020> nodesToUnpurple;
1496   // grayNodes need script traverse, so they aren't removed from
1497   // the purple buffer, but are marked to be in black subtree so that
1498   // traverse is faster.
1499   AutoTArray<nsINode*, 1020> grayNodes;
1500 
1501   bool foundBlack = root->IsBlack();
1502   if (root != currentDoc) {
1503     currentDoc = nullptr;
1504     if (NeedsScriptTraverse(root)) {
1505       grayNodes.AppendElement(root);
1506     } else if (static_cast<nsIContent*>(root)->IsPurple()) {
1507       nodesToUnpurple.AppendElement(static_cast<nsIContent*>(root));
1508     }
1509   }
1510 
1511   // Traverse the subtree and check if we could know without CC
1512   // that it is black.
1513   // Note, this traverse is non-virtual and inline, so it should be a lot faster
1514   // than CC's generic traverse.
1515   for (nsIContent* node = root->GetFirstChild(); node;
1516        node = node->GetNextNode(root)) {
1517     foundBlack = foundBlack || node->IsBlack();
1518     if (foundBlack && currentDoc) {
1519       // If we can mark the whole document black, no need to optimize
1520       // so much, since when the next purple node in the document will be
1521       // handled, it is fast to check that currentDoc is in CCGeneration.
1522       break;
1523     }
1524     if (NeedsScriptTraverse(node)) {
1525       // Gray nodes need real CC traverse.
1526       grayNodes.AppendElement(node);
1527     } else if (node->IsPurple()) {
1528       nodesToUnpurple.AppendElement(node);
1529     }
1530   }
1531 
1532   root->SetCCMarkedRoot(true);
1533   root->SetInCCBlackTree(foundBlack);
1534   gCCBlackMarkedNodes->PutEntry(root);
1535 
1536   if (!foundBlack) {
1537     return false;
1538   }
1539 
1540   if (currentDoc) {
1541     // Special case documents. If we know the document is black,
1542     // we can mark the document to be in CCGeneration.
1543     currentDoc->
1544       MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
1545   } else {
1546     for (uint32_t i = 0; i < grayNodes.Length(); ++i) {
1547       nsINode* node = grayNodes[i];
1548       node->SetInCCBlackTree(true);
1549       gCCBlackMarkedNodes->PutEntry(node);
1550     }
1551   }
1552 
1553   // Subtree is black, we can remove non-gray purple nodes from
1554   // purple buffer.
1555   for (uint32_t i = 0; i < nodesToUnpurple.Length(); ++i) {
1556     nsIContent* purple = nodesToUnpurple[i];
1557     // Can't remove currently handled purple node.
1558     if (purple != aNode) {
1559       purple->RemovePurple();
1560     }
1561   }
1562   return !NeedsScriptTraverse(aNode);
1563 }
1564 
1565 AutoTArray<nsINode*, 1020>* gPurpleRoots = nullptr;
1566 AutoTArray<nsIContent*, 1020>* gNodesToUnbind = nullptr;
1567 
ClearCycleCollectorCleanupData()1568 void ClearCycleCollectorCleanupData()
1569 {
1570   if (gPurpleRoots) {
1571     uint32_t len = gPurpleRoots->Length();
1572     for (uint32_t i = 0; i < len; ++i) {
1573       nsINode* n = gPurpleRoots->ElementAt(i);
1574       n->SetIsPurpleRoot(false);
1575     }
1576     delete gPurpleRoots;
1577     gPurpleRoots = nullptr;
1578   }
1579   if (gNodesToUnbind) {
1580     uint32_t len = gNodesToUnbind->Length();
1581     for (uint32_t i = 0; i < len; ++i) {
1582       nsIContent* c = gNodesToUnbind->ElementAt(i);
1583       c->SetIsPurpleRoot(false);
1584       ContentUnbinder::Append(c);
1585     }
1586     delete gNodesToUnbind;
1587     gNodesToUnbind = nullptr;
1588   }
1589 }
1590 
1591 static bool
ShouldClearPurple(nsIContent * aContent)1592 ShouldClearPurple(nsIContent* aContent)
1593 {
1594   MOZ_ASSERT(aContent);
1595   if (aContent->IsPurple()) {
1596     return true;
1597   }
1598 
1599   JSObject* o = GetJSObjectChild(aContent);
1600   if (o && JS::ObjectIsMarkedGray(o)) {
1601     return true;
1602   }
1603 
1604   if (aContent->HasListenerManager()) {
1605     return true;
1606   }
1607 
1608   return aContent->HasProperties();
1609 }
1610 
1611 // If aNode is not optimizable, but is an element
1612 // with a frame in a document which has currently active presshell,
1613 // we can act as if it was optimizable. When the primary frame dies, aNode
1614 // will end up to the purple buffer because of the refcount change.
1615 bool
NodeHasActiveFrame(nsIDocument * aCurrentDoc,nsINode * aNode)1616 NodeHasActiveFrame(nsIDocument* aCurrentDoc, nsINode* aNode)
1617 {
1618   return aCurrentDoc->GetShell() && aNode->IsElement() &&
1619          aNode->AsElement()->GetPrimaryFrame();
1620 }
1621 
1622 bool
OwnedByBindingManager(nsIDocument * aCurrentDoc,nsINode * aNode)1623 OwnedByBindingManager(nsIDocument* aCurrentDoc, nsINode* aNode)
1624 {
1625   return aNode->IsElement() && aNode->AsElement()->GetXBLBinding();
1626 }
1627 
1628 // CanSkip checks if aNode is black, and if it is, returns
1629 // true. If aNode is in a black DOM tree, CanSkip may also remove other objects
1630 // from purple buffer and unmark event listeners and user data.
1631 // If the root of the DOM tree is a document, less optimizations are done
1632 // since checking the blackness of the current document is usually fast and we
1633 // don't want slow down such common cases.
1634 bool
CanSkip(nsINode * aNode,bool aRemovingAllowed)1635 FragmentOrElement::CanSkip(nsINode* aNode, bool aRemovingAllowed)
1636 {
1637   // Don't try to optimize anything during shutdown.
1638   if (nsCCUncollectableMarker::sGeneration == 0) {
1639     return false;
1640   }
1641 
1642   bool unoptimizable = aNode->UnoptimizableCCNode();
1643   nsIDocument* currentDoc = aNode->GetUncomposedDoc();
1644   if (currentDoc && IsCertainlyAliveNode(aNode, currentDoc) &&
1645       (!unoptimizable || NodeHasActiveFrame(currentDoc, aNode) ||
1646        OwnedByBindingManager(currentDoc, aNode))) {
1647     MarkNodeChildren(aNode);
1648     return true;
1649   }
1650 
1651   if (unoptimizable) {
1652     return false;
1653   }
1654 
1655   nsINode* root = currentDoc ? static_cast<nsINode*>(currentDoc) :
1656                                FindOptimizableSubtreeRoot(aNode);
1657   if (!root) {
1658     return false;
1659   }
1660 
1661   // Subtree has been traversed already, and aNode has
1662   // been handled in a way that doesn't require revisiting it.
1663   if (root->IsPurpleRoot()) {
1664     return false;
1665   }
1666 
1667   // nodesToClear contains nodes which are either purple or
1668   // gray.
1669   AutoTArray<nsIContent*, 1020> nodesToClear;
1670 
1671   bool foundBlack = root->IsBlack();
1672   bool domOnlyCycle = false;
1673   if (root != currentDoc) {
1674     currentDoc = nullptr;
1675     if (!foundBlack) {
1676       domOnlyCycle = static_cast<nsIContent*>(root)->OwnedOnlyByTheDOMTree();
1677     }
1678     if (ShouldClearPurple(static_cast<nsIContent*>(root))) {
1679       nodesToClear.AppendElement(static_cast<nsIContent*>(root));
1680     }
1681   }
1682 
1683   // Traverse the subtree and check if we could know without CC
1684   // that it is black.
1685   // Note, this traverse is non-virtual and inline, so it should be a lot faster
1686   // than CC's generic traverse.
1687   for (nsIContent* node = root->GetFirstChild(); node;
1688        node = node->GetNextNode(root)) {
1689     foundBlack = foundBlack || node->IsBlack();
1690     if (foundBlack) {
1691       domOnlyCycle = false;
1692       if (currentDoc) {
1693         // If we can mark the whole document black, no need to optimize
1694         // so much, since when the next purple node in the document will be
1695         // handled, it is fast to check that the currentDoc is in CCGeneration.
1696         break;
1697       }
1698       // No need to put stuff to the nodesToClear array, if we can clear it
1699       // already here.
1700       if (node->IsPurple() && (node != aNode || aRemovingAllowed)) {
1701         node->RemovePurple();
1702       }
1703       MarkNodeChildren(node);
1704     } else {
1705       domOnlyCycle = domOnlyCycle && node->OwnedOnlyByTheDOMTree();
1706       if (ShouldClearPurple(node)) {
1707         // Collect interesting nodes which we can clear if we find that
1708         // they are kept alive in a black tree or are in a DOM-only cycle.
1709         nodesToClear.AppendElement(node);
1710       }
1711     }
1712   }
1713 
1714   if (!currentDoc || !foundBlack) {
1715     root->SetIsPurpleRoot(true);
1716     if (domOnlyCycle) {
1717       if (!gNodesToUnbind) {
1718         gNodesToUnbind = new AutoTArray<nsIContent*, 1020>();
1719       }
1720       gNodesToUnbind->AppendElement(static_cast<nsIContent*>(root));
1721       for (uint32_t i = 0; i < nodesToClear.Length(); ++i) {
1722         nsIContent* n = nodesToClear[i];
1723         if ((n != aNode || aRemovingAllowed) && n->IsPurple()) {
1724           n->RemovePurple();
1725         }
1726       }
1727       return true;
1728     } else {
1729       if (!gPurpleRoots) {
1730         gPurpleRoots = new AutoTArray<nsINode*, 1020>();
1731       }
1732       gPurpleRoots->AppendElement(root);
1733     }
1734   }
1735 
1736   if (!foundBlack) {
1737     return false;
1738   }
1739 
1740   if (currentDoc) {
1741     // Special case documents. If we know the document is black,
1742     // we can mark the document to be in CCGeneration.
1743     currentDoc->
1744       MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration);
1745     MarkNodeChildren(currentDoc);
1746   }
1747 
1748   // Subtree is black, so we can remove purple nodes from
1749   // purple buffer and mark stuff that to be certainly alive.
1750   for (uint32_t i = 0; i < nodesToClear.Length(); ++i) {
1751     nsIContent* n = nodesToClear[i];
1752     MarkNodeChildren(n);
1753     // Can't remove currently handled purple node,
1754     // unless aRemovingAllowed is true.
1755     if ((n != aNode || aRemovingAllowed) && n->IsPurple()) {
1756       n->RemovePurple();
1757     }
1758   }
1759   return true;
1760 }
1761 
1762 bool
CanSkipThis(nsINode * aNode)1763 FragmentOrElement::CanSkipThis(nsINode* aNode)
1764 {
1765   if (nsCCUncollectableMarker::sGeneration == 0) {
1766     return false;
1767   }
1768   if (aNode->IsBlack()) {
1769     return true;
1770   }
1771   nsIDocument* c = aNode->GetUncomposedDoc();
1772   return
1773     ((c && IsCertainlyAliveNode(aNode, c)) || aNode->InCCBlackTree()) &&
1774     !NeedsScriptTraverse(aNode);
1775 }
1776 
1777 void
InitCCCallbacks()1778 FragmentOrElement::InitCCCallbacks()
1779 {
1780   nsCycleCollector_setForgetSkippableCallback(ClearCycleCollectorCleanupData);
1781   nsCycleCollector_setBeforeUnlinkCallback(ClearBlackMarkedNodes);
1782 }
1783 
1784 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(FragmentOrElement)
1785   return FragmentOrElement::CanSkip(tmp, aRemovingAllowed);
1786 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1787 
1788 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(FragmentOrElement)
1789   return FragmentOrElement::CanSkipInCC(tmp);
1790 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1791 
1792 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(FragmentOrElement)
1793   return FragmentOrElement::CanSkipThis(tmp);
1794 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1795 
1796 static const char* kNSURIs[] = {
1797   " ([none])",
1798   " (xmlns)",
1799   " (xml)",
1800   " (xhtml)",
1801   " (XLink)",
1802   " (XSLT)",
1803   " (XBL)",
1804   " (MathML)",
1805   " (RDF)",
1806   " (XUL)",
1807   " (SVG)",
1808   " (XML Events)"
1809 };
1810 
1811 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement)
1812   if (MOZ_UNLIKELY(cb.WantDebugInfo())) {
1813     char name[512];
1814     uint32_t nsid = tmp->GetNameSpaceID();
1815     nsAtomCString localName(tmp->NodeInfo()->NameAtom());
1816     nsAutoCString uri;
1817     if (tmp->OwnerDoc()->GetDocumentURI()) {
1818       uri = tmp->OwnerDoc()->GetDocumentURI()->GetSpecOrDefault();
1819     }
1820 
1821     nsAutoString id;
1822     nsIAtom* idAtom = tmp->GetID();
1823     if (idAtom) {
1824       id.AppendLiteral(" id='");
1825       id.Append(nsDependentAtomString(idAtom));
1826       id.Append('\'');
1827     }
1828 
1829     nsAutoString classes;
1830     const nsAttrValue* classAttrValue = tmp->GetClasses();
1831     if (classAttrValue) {
1832       classes.AppendLiteral(" class='");
1833       nsAutoString classString;
1834       classAttrValue->ToString(classString);
1835       classString.ReplaceChar(char16_t('\n'), char16_t(' '));
1836       classes.Append(classString);
1837       classes.Append('\'');
1838     }
1839 
1840     nsAutoCString orphan;
1841     if (!tmp->IsInUncomposedDoc() &&
1842         // Ignore xbl:content, which is never in the document and hence always
1843         // appears to be orphaned.
1844         !tmp->NodeInfo()->Equals(nsGkAtoms::content, kNameSpaceID_XBL)) {
1845       orphan.AppendLiteral(" (orphan)");
1846     }
1847 
1848     const char* nsuri = nsid < ArrayLength(kNSURIs) ? kNSURIs[nsid] : "";
1849     SprintfLiteral(name, "FragmentOrElement%s %s%s%s%s %s",
1850                    nsuri,
1851                    localName.get(),
1852                    NS_ConvertUTF16toUTF8(id).get(),
1853                    NS_ConvertUTF16toUTF8(classes).get(),
1854                    orphan.get(),
1855                    uri.get());
1856     cb.DescribeRefCountedNode(tmp->mRefCnt.get(), name);
1857   }
1858   else {
1859     NS_IMPL_CYCLE_COLLECTION_DESCRIBE(FragmentOrElement, tmp->mRefCnt.get())
1860   }
1861 
1862   // Always need to traverse script objects, so do that before we check
1863   // if we're uncollectable.
1864   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
1865 
1866   if (!nsINode::Traverse(tmp, cb)) {
1867     return NS_SUCCESS_INTERRUPTED_TRAVERSE;
1868   }
1869 
1870   tmp->OwnerDoc()->BindingManager()->Traverse(tmp, cb);
1871 
1872   // Check that whenever we have effect properties, MayHaveAnimations is set.
1873 #ifdef DEBUG
1874   nsIAtom** effectProps = EffectSet::GetEffectSetPropertyAtoms();
1875   for (uint32_t i = 0; effectProps[i]; ++i) {
1876     MOZ_ASSERT_IF(tmp->GetProperty(effectProps[i]), tmp->MayHaveAnimations());
1877   }
1878 #endif
1879 
1880   if (tmp->HasProperties()) {
1881     if (tmp->IsHTMLElement() || tmp->IsSVGElement()) {
1882       nsIAtom*** props = Element::HTMLSVGPropertiesToTraverseAndUnlink();
1883       for (uint32_t i = 0; props[i]; ++i) {
1884         nsISupports* property =
1885           static_cast<nsISupports*>(tmp->GetProperty(*props[i]));
1886         cb.NoteXPCOMChild(property);
1887       }
1888       if (tmp->MayHaveAnimations()) {
1889         nsIAtom** effectProps = EffectSet::GetEffectSetPropertyAtoms();
1890         for (uint32_t i = 0; effectProps[i]; ++i) {
1891           EffectSet* effectSet =
1892             static_cast<EffectSet*>(tmp->GetProperty(effectProps[i]));
1893           if (effectSet) {
1894             effectSet->Traverse(cb);
1895           }
1896         }
1897       }
1898     }
1899   }
1900 
1901   // Traverse attribute names and child content.
1902   {
1903     uint32_t i;
1904     uint32_t attrs = tmp->mAttrsAndChildren.AttrCount();
1905     for (i = 0; i < attrs; i++) {
1906       const nsAttrName* name = tmp->mAttrsAndChildren.AttrNameAt(i);
1907       if (!name->IsAtom()) {
1908         NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
1909                                            "mAttrsAndChildren[i]->NodeInfo()");
1910         cb.NoteNativeChild(name->NodeInfo(),
1911                            NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo));
1912       }
1913     }
1914 
1915     uint32_t kids = tmp->mAttrsAndChildren.ChildCount();
1916     for (i = 0; i < kids; i++) {
1917       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mAttrsAndChildren[i]");
1918       cb.NoteXPCOMChild(tmp->mAttrsAndChildren.GetSafeChildAt(i));
1919     }
1920   }
1921 
1922   // Traverse any DOM slots of interest.
1923   {
1924     nsDOMSlots *slots = tmp->GetExistingDOMSlots();
1925     if (slots) {
1926       slots->Traverse(cb, tmp->IsXULElement());
1927     }
1928   }
1929 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1930 
1931 
NS_INTERFACE_MAP_BEGIN(FragmentOrElement)1932 NS_INTERFACE_MAP_BEGIN(FragmentOrElement)
1933   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1934   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(FragmentOrElement)
1935   NS_INTERFACE_MAP_ENTRY(nsIContent)
1936   NS_INTERFACE_MAP_ENTRY(nsINode)
1937   NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
1938   NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
1939   NS_INTERFACE_MAP_ENTRY_TEAROFF(nsISupportsWeakReference,
1940                                  new nsNodeSupportsWeakRefTearoff(this))
1941   // DOM bindings depend on the identity pointer being the
1942   // same as nsINode (which nsIContent inherits).
1943   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContent)
1944 NS_INTERFACE_MAP_END
1945 
1946 NS_IMPL_CYCLE_COLLECTING_ADDREF(FragmentOrElement)
1947 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(FragmentOrElement,
1948                                                    nsNodeUtils::LastRelease(this))
1949 
1950 //----------------------------------------------------------------------
1951 
1952 nsresult
1953 FragmentOrElement::CopyInnerTo(FragmentOrElement* aDst)
1954 {
1955   uint32_t i, count = mAttrsAndChildren.AttrCount();
1956   for (i = 0; i < count; ++i) {
1957     const nsAttrName* name = mAttrsAndChildren.AttrNameAt(i);
1958     const nsAttrValue* value = mAttrsAndChildren.AttrAt(i);
1959     nsAutoString valStr;
1960     value->ToString(valStr);
1961     nsresult rv = aDst->SetAttr(name->NamespaceID(), name->LocalName(),
1962                                 name->GetPrefix(), valStr, false);
1963     NS_ENSURE_SUCCESS(rv, rv);
1964   }
1965 
1966   return NS_OK;
1967 }
1968 
1969 const nsTextFragment*
GetText()1970 FragmentOrElement::GetText()
1971 {
1972   return nullptr;
1973 }
1974 
1975 uint32_t
TextLength() const1976 FragmentOrElement::TextLength() const
1977 {
1978   // We can remove this assertion if it turns out to be useful to be able
1979   // to depend on this returning 0
1980   NS_NOTREACHED("called FragmentOrElement::TextLength");
1981 
1982   return 0;
1983 }
1984 
1985 nsresult
SetText(const char16_t * aBuffer,uint32_t aLength,bool aNotify)1986 FragmentOrElement::SetText(const char16_t* aBuffer, uint32_t aLength,
1987                           bool aNotify)
1988 {
1989   NS_ERROR("called FragmentOrElement::SetText");
1990 
1991   return NS_ERROR_FAILURE;
1992 }
1993 
1994 nsresult
AppendText(const char16_t * aBuffer,uint32_t aLength,bool aNotify)1995 FragmentOrElement::AppendText(const char16_t* aBuffer, uint32_t aLength,
1996                              bool aNotify)
1997 {
1998   NS_ERROR("called FragmentOrElement::AppendText");
1999 
2000   return NS_ERROR_FAILURE;
2001 }
2002 
2003 bool
TextIsOnlyWhitespace()2004 FragmentOrElement::TextIsOnlyWhitespace()
2005 {
2006   return false;
2007 }
2008 
2009 bool
HasTextForTranslation()2010 FragmentOrElement::HasTextForTranslation()
2011 {
2012   return false;
2013 }
2014 
2015 void
AppendTextTo(nsAString & aResult)2016 FragmentOrElement::AppendTextTo(nsAString& aResult)
2017 {
2018   // We can remove this assertion if it turns out to be useful to be able
2019   // to depend on this appending nothing.
2020   NS_NOTREACHED("called FragmentOrElement::TextLength");
2021 }
2022 
2023 bool
AppendTextTo(nsAString & aResult,const mozilla::fallible_t &)2024 FragmentOrElement::AppendTextTo(nsAString& aResult, const mozilla::fallible_t&)
2025 {
2026   // We can remove this assertion if it turns out to be useful to be able
2027   // to depend on this appending nothing.
2028   NS_NOTREACHED("called FragmentOrElement::TextLength");
2029 
2030   return false;
2031 }
2032 
2033 uint32_t
GetChildCount() const2034 FragmentOrElement::GetChildCount() const
2035 {
2036   return mAttrsAndChildren.ChildCount();
2037 }
2038 
2039 nsIContent *
GetChildAt(uint32_t aIndex) const2040 FragmentOrElement::GetChildAt(uint32_t aIndex) const
2041 {
2042   return mAttrsAndChildren.GetSafeChildAt(aIndex);
2043 }
2044 
2045 nsIContent * const *
GetChildArray(uint32_t * aChildCount) const2046 FragmentOrElement::GetChildArray(uint32_t* aChildCount) const
2047 {
2048   return mAttrsAndChildren.GetChildArray(aChildCount);
2049 }
2050 
2051 int32_t
IndexOf(const nsINode * aPossibleChild) const2052 FragmentOrElement::IndexOf(const nsINode* aPossibleChild) const
2053 {
2054   return mAttrsAndChildren.IndexOfChild(aPossibleChild);
2055 }
2056 
2057 static inline bool
IsVoidTag(nsIAtom * aTag)2058 IsVoidTag(nsIAtom* aTag)
2059 {
2060   static const nsIAtom* voidElements[] = {
2061     nsGkAtoms::area, nsGkAtoms::base, nsGkAtoms::basefont,
2062     nsGkAtoms::bgsound, nsGkAtoms::br, nsGkAtoms::col,
2063     nsGkAtoms::embed, nsGkAtoms::frame,
2064     nsGkAtoms::hr, nsGkAtoms::img, nsGkAtoms::input,
2065     nsGkAtoms::keygen, nsGkAtoms::link, nsGkAtoms::meta,
2066     nsGkAtoms::param, nsGkAtoms::source, nsGkAtoms::track,
2067     nsGkAtoms::wbr
2068   };
2069 
2070   static mozilla::BloomFilter<12, nsIAtom> sFilter;
2071   static bool sInitialized = false;
2072   if (!sInitialized) {
2073     sInitialized = true;
2074     for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
2075       sFilter.add(voidElements[i]);
2076     }
2077   }
2078 
2079   if (sFilter.mightContain(aTag)) {
2080     for (uint32_t i = 0; i < ArrayLength(voidElements); ++i) {
2081       if (aTag == voidElements[i]) {
2082         return true;
2083       }
2084     }
2085   }
2086   return false;
2087 }
2088 
2089 /* static */
2090 bool
IsHTMLVoid(nsIAtom * aLocalName)2091 FragmentOrElement::IsHTMLVoid(nsIAtom* aLocalName)
2092 {
2093   return aLocalName && IsVoidTag(aLocalName);
2094 }
2095 
2096 void
GetMarkup(bool aIncludeSelf,nsAString & aMarkup)2097 FragmentOrElement::GetMarkup(bool aIncludeSelf, nsAString& aMarkup)
2098 {
2099   aMarkup.Truncate();
2100 
2101   nsIDocument* doc = OwnerDoc();
2102   if (IsInHTMLDocument()) {
2103     nsContentUtils::SerializeNodeToMarkup(this, !aIncludeSelf, aMarkup);
2104     return;
2105   }
2106 
2107   nsAutoString contentType;
2108   doc->GetContentType(contentType);
2109   bool tryToCacheEncoder = !aIncludeSelf;
2110 
2111   nsCOMPtr<nsIDocumentEncoder> docEncoder = doc->GetCachedEncoder();
2112   if (!docEncoder) {
2113     docEncoder =
2114       do_CreateInstance(PromiseFlatCString(
2115         nsDependentCString(NS_DOC_ENCODER_CONTRACTID_BASE) +
2116         NS_ConvertUTF16toUTF8(contentType)
2117       ).get());
2118   }
2119   if (!docEncoder) {
2120     // This could be some type for which we create a synthetic document.  Try
2121     // again as XML
2122     contentType.AssignLiteral("application/xml");
2123     docEncoder = do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "application/xml");
2124     // Don't try to cache the encoder since it would point to a different
2125     // contentType once it has been reinitialized.
2126     tryToCacheEncoder = false;
2127   }
2128 
2129   NS_ENSURE_TRUE_VOID(docEncoder);
2130 
2131   uint32_t flags = nsIDocumentEncoder::OutputEncodeBasicEntities |
2132                    // Output DOM-standard newlines
2133                    nsIDocumentEncoder::OutputLFLineBreak |
2134                    // Don't do linebreaking that's not present in
2135                    // the source
2136                    nsIDocumentEncoder::OutputRaw |
2137                    // Only check for mozdirty when necessary (bug 599983)
2138                    nsIDocumentEncoder::OutputIgnoreMozDirty;
2139 
2140   if (IsEditable()) {
2141     nsCOMPtr<Element> elem = do_QueryInterface(this);
2142     nsIEditor* editor = elem ? elem->GetEditorInternal() : nullptr;
2143     if (editor && editor->OutputsMozDirty()) {
2144       flags &= ~nsIDocumentEncoder::OutputIgnoreMozDirty;
2145     }
2146   }
2147 
2148   DebugOnly<nsresult> rv = docEncoder->NativeInit(doc, contentType, flags);
2149   MOZ_ASSERT(NS_SUCCEEDED(rv));
2150 
2151   if (aIncludeSelf) {
2152     docEncoder->SetNativeNode(this);
2153   } else {
2154     docEncoder->SetNativeContainerNode(this);
2155   }
2156   rv = docEncoder->EncodeToString(aMarkup);
2157   MOZ_ASSERT(NS_SUCCEEDED(rv));
2158   if (tryToCacheEncoder) {
2159     doc->SetCachedEncoder(docEncoder.forget());
2160   }
2161 }
2162 
2163 static bool
ContainsMarkup(const nsAString & aStr)2164 ContainsMarkup(const nsAString& aStr)
2165 {
2166   // Note: we can't use FindCharInSet because null is one of the characters we
2167   // want to search for.
2168   const char16_t* start = aStr.BeginReading();
2169   const char16_t* end = aStr.EndReading();
2170 
2171   while (start != end) {
2172     char16_t c = *start;
2173     if (c == char16_t('<') ||
2174         c == char16_t('&') ||
2175         c == char16_t('\r') ||
2176         c == char16_t('\0')) {
2177       return true;
2178     }
2179     ++start;
2180   }
2181 
2182   return false;
2183 }
2184 
2185 void
SetInnerHTMLInternal(const nsAString & aInnerHTML,ErrorResult & aError)2186 FragmentOrElement::SetInnerHTMLInternal(const nsAString& aInnerHTML, ErrorResult& aError)
2187 {
2188   FragmentOrElement* target = this;
2189   // Handle template case.
2190   if (nsNodeUtils::IsTemplateElement(target)) {
2191     DocumentFragment* frag =
2192       static_cast<HTMLTemplateElement*>(target)->Content();
2193     MOZ_ASSERT(frag);
2194     target = frag;
2195   }
2196 
2197   // Fast-path for strings with no markup. Limit this to short strings, to
2198   // avoid ContainsMarkup taking too long. The choice for 100 is based on
2199   // gut feeling.
2200   //
2201   // Don't do this for elements with a weird parser insertion mode, for
2202   // instance setting innerHTML = "" on a <html> element should add the
2203   // optional <head> and <body> elements.
2204   if (!target->HasWeirdParserInsertionMode() &&
2205       aInnerHTML.Length() < 100 && !ContainsMarkup(aInnerHTML)) {
2206     aError = nsContentUtils::SetNodeTextContent(target, aInnerHTML, false);
2207     return;
2208   }
2209 
2210   nsIDocument* doc = target->OwnerDoc();
2211 
2212   // Batch possible DOMSubtreeModified events.
2213   mozAutoSubtreeModified subtree(doc, nullptr);
2214 
2215   target->FireNodeRemovedForChildren();
2216 
2217   // Needed when innerHTML is used in combination with contenteditable
2218   mozAutoDocUpdate updateBatch(doc, UPDATE_CONTENT_MODEL, true);
2219 
2220   // Remove childnodes.
2221   uint32_t childCount = target->GetChildCount();
2222   nsAutoMutationBatch mb(target, true, false);
2223   for (uint32_t i = 0; i < childCount; ++i) {
2224     target->RemoveChildAt(0, true);
2225   }
2226   mb.RemovalDone();
2227 
2228   nsAutoScriptLoaderDisabler sld(doc);
2229 
2230   nsIAtom* contextLocalName = NodeInfo()->NameAtom();
2231   int32_t contextNameSpaceID = GetNameSpaceID();
2232 
2233   ShadowRoot* shadowRoot = ShadowRoot::FromNode(this);
2234   if (shadowRoot) {
2235     // Fix up the context to be the host of the ShadowRoot.
2236     contextLocalName = shadowRoot->GetHost()->NodeInfo()->NameAtom();
2237     contextNameSpaceID = shadowRoot->GetHost()->GetNameSpaceID();
2238   }
2239 
2240   if (doc->IsHTMLDocument()) {
2241     int32_t oldChildCount = target->GetChildCount();
2242     aError = nsContentUtils::ParseFragmentHTML(aInnerHTML,
2243                                                target,
2244                                                contextLocalName,
2245                                                contextNameSpaceID,
2246                                                doc->GetCompatibilityMode() ==
2247                                                  eCompatibility_NavQuirks,
2248                                                true);
2249     mb.NodesAdded();
2250     // HTML5 parser has notified, but not fired mutation events.
2251     nsContentUtils::FireMutationEventsForDirectParsing(doc, target,
2252                                                        oldChildCount);
2253   } else {
2254     RefPtr<DocumentFragment> df =
2255       nsContentUtils::CreateContextualFragment(target, aInnerHTML, true, aError);
2256     if (!aError.Failed()) {
2257       // Suppress assertion about node removal mutation events that can't have
2258       // listeners anyway, because no one has had the chance to register mutation
2259       // listeners on the fragment that comes from the parser.
2260       nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker;
2261 
2262       static_cast<nsINode*>(target)->AppendChild(*df, aError);
2263       mb.NodesAdded();
2264     }
2265   }
2266 }
2267 
2268 nsINode::nsSlots*
CreateSlots()2269 FragmentOrElement::CreateSlots()
2270 {
2271   return new nsDOMSlots();
2272 }
2273 
2274 void
FireNodeRemovedForChildren()2275 FragmentOrElement::FireNodeRemovedForChildren()
2276 {
2277   nsIDocument* doc = OwnerDoc();
2278   // Optimize the common case
2279   if (!nsContentUtils::
2280         HasMutationListeners(doc, NS_EVENT_BITS_MUTATION_NODEREMOVED)) {
2281     return;
2282   }
2283 
2284   nsCOMPtr<nsIDocument> owningDoc = doc;
2285 
2286   nsCOMPtr<nsINode> child;
2287   for (child = GetFirstChild();
2288        child && child->GetParentNode() == this;
2289        child = child->GetNextSibling()) {
2290     nsContentUtils::MaybeFireNodeRemoved(child, this, doc);
2291   }
2292 }
2293 
2294 size_t
SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const2295 FragmentOrElement::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
2296 {
2297   size_t n = 0;
2298   n += nsIContent::SizeOfExcludingThis(aMallocSizeOf);
2299   n += mAttrsAndChildren.SizeOfExcludingThis(aMallocSizeOf);
2300 
2301   nsDOMSlots* slots = GetExistingDOMSlots();
2302   if (slots) {
2303     n += slots->SizeOfIncludingThis(aMallocSizeOf);
2304   }
2305 
2306   return n;
2307 }
2308 
2309 void
SetIsElementInStyleScopeFlagOnSubtree(bool aInStyleScope)2310 FragmentOrElement::SetIsElementInStyleScopeFlagOnSubtree(bool aInStyleScope)
2311 {
2312   if (aInStyleScope && IsElementInStyleScope()) {
2313     return;
2314   }
2315 
2316   if (IsElement()) {
2317     SetIsElementInStyleScope(aInStyleScope);
2318     SetIsElementInStyleScopeFlagOnShadowTree(aInStyleScope);
2319   }
2320 
2321   nsIContent* n = GetNextNode(this);
2322   while (n) {
2323     if (n->IsElementInStyleScope()) {
2324       n = n->GetNextNonChildNode(this);
2325     } else {
2326       if (n->IsElement()) {
2327         n->SetIsElementInStyleScope(aInStyleScope);
2328         n->AsElement()->SetIsElementInStyleScopeFlagOnShadowTree(aInStyleScope);
2329       }
2330       n = n->GetNextNode(this);
2331     }
2332   }
2333 }
2334 
2335 void
SetIsElementInStyleScopeFlagOnShadowTree(bool aInStyleScope)2336 FragmentOrElement::SetIsElementInStyleScopeFlagOnShadowTree(bool aInStyleScope)
2337 {
2338   NS_ASSERTION(IsElement(), "calling SetIsElementInStyleScopeFlagOnShadowTree "
2339                             "on a non-Element is useless");
2340   ShadowRoot* shadowRoot = GetShadowRoot();
2341   while (shadowRoot) {
2342     shadowRoot->SetIsElementInStyleScopeFlagOnSubtree(aInStyleScope);
2343     shadowRoot = shadowRoot->GetOlderShadowRoot();
2344   }
2345 }
2346 
2347 #ifdef DEBUG
2348 static void
AssertDirtyDescendantsBitPropagated(nsINode * aNode)2349 AssertDirtyDescendantsBitPropagated(nsINode* aNode)
2350 {
2351   MOZ_ASSERT(aNode->HasDirtyDescendantsForServo());
2352   nsINode* parent = aNode->GetFlattenedTreeParentNode();
2353   if (!parent->IsContent()) {
2354     MOZ_ASSERT(parent == aNode->OwnerDoc());
2355     MOZ_ASSERT(parent->HasDirtyDescendantsForServo());
2356   } else {
2357     AssertDirtyDescendantsBitPropagated(parent);
2358   }
2359 }
2360 #else
AssertDirtyDescendantsBitPropagated(nsINode * aNode)2361 static void AssertDirtyDescendantsBitPropagated(nsINode* aNode) {}
2362 #endif
2363 
2364 void
MarkAncestorsAsHavingDirtyDescendantsForServo()2365 nsIContent::MarkAncestorsAsHavingDirtyDescendantsForServo()
2366 {
2367   MOZ_ASSERT(IsInComposedDoc());
2368 
2369   // Get the parent in the flattened tree.
2370   nsINode* parent = GetFlattenedTreeParentNode();
2371 
2372   // Loop until we hit a base case.
2373   while (true) {
2374 
2375     // Base case: the document.
2376     if (!parent->IsContent()) {
2377       MOZ_ASSERT(parent == OwnerDoc());
2378       parent->SetHasDirtyDescendantsForServo();
2379       return;
2380     }
2381 
2382     // Base case: the parent is already marked, and therefore
2383     // so are all its ancestors.
2384     if (parent->HasDirtyDescendantsForServo()) {
2385       AssertDirtyDescendantsBitPropagated(parent);
2386       return;
2387     }
2388 
2389     // Mark the parent and iterate.
2390     parent->SetHasDirtyDescendantsForServo();
2391     parent = parent->GetFlattenedTreeParentNode();
2392   }
2393 }
2394