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