1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=79: */
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/. */
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.
13 #include "mozilla/ArrayUtils.h"
14 #include "mozilla/Likely.h"
15 #include "mozilla/MemoryReporting.h"
16 #include "mozilla/StaticPtr.h"
18 #include "mozilla/dom/FragmentOrElement.h"
20 #include "mozilla/AsyncEventDispatcher.h"
21 #include "mozilla/EventDispatcher.h"
22 #include "mozilla/EventListenerManager.h"
23 #include "mozilla/EventStates.h"
24 #include "mozilla/dom/Attr.h"
25 #include "nsDOMAttributeMap.h"
27 #include "mozilla/dom/NodeInfo.h"
28 #include "mozilla/dom/Event.h"
29 #include "nsIDocumentInlines.h"
30 #include "nsIDocumentEncoder.h"
31 #include "nsIDOMNodeList.h"
32 #include "nsIContentIterator.h"
33 #include "nsFocusManager.h"
34 #include "nsILinkHandler.h"
35 #include "nsIScriptGlobalObject.h"
37 #include "nsNetUtil.h"
39 #include "nsIAnonymousContentCreator.h"
40 #include "nsIPresShell.h"
41 #include "nsPresContext.h"
42 #include "nsStyleConsts.h"
44 #include "nsUnicharUtils.h"
45 #include "nsIDOMEvent.h"
47 #include "nsIServiceManager.h"
48 #include "nsIDOMCSSStyleDeclaration.h"
49 #include "nsDOMCSSAttrDeclaration.h"
50 #include "nsNameSpaceManager.h"
51 #include "nsContentList.h"
52 #include "nsDOMTokenList.h"
53 #include "nsXBLPrototypeBinding.h"
55 #include "nsDOMString.h"
56 #include "nsIScriptSecurityManager.h"
57 #include "nsIDOMMutationEvent.h"
58 #include "mozilla/InternalMutationEvent.h"
59 #include "mozilla/MouseEvents.h"
60 #include "nsNodeUtils.h"
61 #include "nsDocument.h"
62 #include "nsAttrValueOrString.h"
64 #include "nsXULElement.h"
66 #include "nsFrameSelection.h"
71 #include "nsBindingManager.h"
72 #include "nsXBLBinding.h"
73 #include "nsPIDOMWindow.h"
74 #include "nsPIBoxObject.h"
75 #include "nsSVGUtils.h"
76 #include "nsLayoutUtils.h"
77 #include "nsGkAtoms.h"
78 #include "nsContentUtils.h"
79 #include "nsTextFragment.h"
80 #include "nsContentCID.h"
82 #include "nsIDOMEventListener.h"
83 #include "nsIWebNavigation.h"
84 #include "nsIBaseWindow.h"
85 #include "nsIWidget.h"
89 #include "nsNodeInfoManager.h"
90 #include "nsICategoryManager.h"
91 #include "nsGenericHTMLElement.h"
92 #include "nsIEditor.h"
93 #include "nsIEditorIMESupport.h"
94 #include "nsContentCreatorFunctions.h"
95 #include "nsIControllers.h"
97 #include "nsViewManager.h"
98 #include "nsIScrollableFrame.h"
99 #include "ChildIterator.h"
100 #include "mozilla/css/StyleRule.h" /* For nsCSSSelectorList */
101 #include "nsRuleProcessorData.h"
102 #include "nsTextNode.h"
103 #include "mozilla/dom/NodeListBinding.h"
104 #include "mozilla/dom/UndoManager.h"
107 #include "nsIXULDocument.h"
110 #include "nsCCUncollectableMarker.h"
112 #include "mozAutoDocUpdate.h"
115 #include "nsDOMMutationObserver.h"
116 #include "nsWrapperCacheInlines.h"
117 #include "nsCycleCollector.h"
118 #include "xpcpublic.h"
119 #include "nsIScriptError.h"
120 #include "mozilla/Telemetry.h"
122 #include "mozilla/CORSMode.h"
124 #include "mozilla/dom/ShadowRoot.h"
125 #include "mozilla/dom/HTMLTemplateElement.h"
127 #include "nsStyledElement.h"
128 #include "nsIContentInlines.h"
130 using namespace mozilla
;
131 using namespace mozilla::dom
;
133 int32_t nsIContent::sTabFocusModel
= eTabFocus_any
;
134 bool nsIContent::sTabFocusModelAppliesToXUL
= false;
135 uint64_t nsMutationGuard::sGeneration
= 0;
138 nsIContent::FindFirstNonChromeOnlyAccessContent() const
140 // This handles also nested native anonymous content.
141 for (const nsIContent
*content
= this; content
;
142 content
= content
->GetBindingParent()) {
143 if (!content
->ChromeOnlyAccess()) {
144 // Oops, this function signature allows casting const to
145 // non-const. (Then again, so does GetChildAt(0)->GetParent().)
146 return const_cast<nsIContent
*>(content
);
153 nsIContent::GetFlattenedTreeParent() const
155 nsIContent
* parent
= GetParent();
157 if (parent
&& nsContentUtils::HasDistributedChildren(parent
) &&
158 nsContentUtils::IsInSameAnonymousTree(parent
, this)) {
159 // This node is distributed to insertion points, thus we
160 // need to consult the destination insertion points list to
161 // figure out where this node was inserted in the flattened tree.
162 // It may be the case that |parent| distributes its children
163 // but the child does not match any insertion points, thus
164 // the flattened tree parent is nullptr.
165 nsTArray
<nsIContent
*>* destInsertionPoints
= GetExistingDestInsertionPoints();
166 parent
= destInsertionPoints
&& !destInsertionPoints
->IsEmpty() ?
167 destInsertionPoints
->LastElement()->GetParent() : nullptr;
168 } else if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR
)) {
169 nsIContent
* insertionParent
= GetXBLInsertionParent();
170 if (insertionParent
) {
171 parent
= insertionParent
;
175 // Shadow roots never shows up in the flattened tree. Return the host
177 if (parent
&& parent
->IsInShadowTree()) {
178 ShadowRoot
* parentShadowRoot
= ShadowRoot::FromNode(parent
);
179 if (parentShadowRoot
) {
180 return parentShadowRoot
->GetHost();
188 nsIContent::GetDesiredIMEState()
190 if (!IsEditableInternal()) {
191 // Check for the special case where we're dealing with elements which don't
192 // have the editable flag set, but are readwrite (such as text controls).
194 !AsElement()->State().HasState(NS_EVENT_STATE_MOZ_READWRITE
)) {
195 return IMEState(IMEState::DISABLED
);
198 // NOTE: The content for independent editors (e.g., input[type=text],
199 // textarea) must override this method, so, we don't need to worry about
201 nsIContent
*editableAncestor
= GetEditingHost();
203 // This is in another editable content, use the result of it.
204 if (editableAncestor
&& editableAncestor
!= this) {
205 return editableAncestor
->GetDesiredIMEState();
207 nsIDocument
* doc
= GetComposedDoc();
209 return IMEState(IMEState::DISABLED
);
211 nsIPresShell
* ps
= doc
->GetShell();
213 return IMEState(IMEState::DISABLED
);
215 nsPresContext
* pc
= ps
->GetPresContext();
217 return IMEState(IMEState::DISABLED
);
219 nsIEditor
* editor
= nsContentUtils::GetHTMLEditor(pc
);
220 nsCOMPtr
<nsIEditorIMESupport
> imeEditor
= do_QueryInterface(editor
);
222 return IMEState(IMEState::DISABLED
);
225 imeEditor
->GetPreferredIMEState(&state
);
230 nsIContent::HasIndependentSelection()
232 nsIFrame
* frame
= GetPrimaryFrame();
233 return (frame
&& frame
->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION
);
237 nsIContent::GetEditingHost()
239 // If this isn't editable, return nullptr.
240 NS_ENSURE_TRUE(IsEditableInternal(), nullptr);
242 nsIDocument
* doc
= GetComposedDoc();
243 NS_ENSURE_TRUE(doc
, nullptr);
244 // If this is in designMode, we should return <body>
245 if (doc
->HasFlag(NODE_IS_EDITABLE
) && !IsInShadowTree()) {
246 return doc
->GetBodyElement();
249 nsIContent
* content
= this;
250 for (dom::Element
* parent
= GetParentElement();
251 parent
&& parent
->HasFlag(NODE_IS_EDITABLE
);
252 parent
= content
->GetParentElement()) {
255 return content
->AsElement();
259 nsIContent::LookupNamespaceURIInternal(const nsAString
& aNamespacePrefix
,
260 nsAString
& aNamespaceURI
) const
262 if (aNamespacePrefix
.EqualsLiteral("xml")) {
263 // Special-case for xml prefix
264 aNamespaceURI
.AssignLiteral("http://www.w3.org/XML/1998/namespace");
268 if (aNamespacePrefix
.EqualsLiteral("xmlns")) {
269 // Special-case for xmlns prefix
270 aNamespaceURI
.AssignLiteral("http://www.w3.org/2000/xmlns/");
274 nsCOMPtr
<nsIAtom
> name
;
275 if (!aNamespacePrefix
.IsEmpty()) {
276 name
= do_GetAtom(aNamespacePrefix
);
277 NS_ENSURE_TRUE(name
, NS_ERROR_OUT_OF_MEMORY
);
280 name
= nsGkAtoms::xmlns
;
282 // Trace up the content parent chain looking for the namespace
283 // declaration that declares aNamespacePrefix.
284 const nsIContent
* content
= this;
286 if (content
->GetAttr(kNameSpaceID_XMLNS
, name
, aNamespaceURI
))
288 } while ((content
= content
->GetParent()));
289 return NS_ERROR_FAILURE
;
292 already_AddRefed
<nsIURI
>
293 nsIContent::GetBaseURI(bool aTryUseXHRDocBaseURI
) const
295 nsIDocument
* doc
= OwnerDoc();
296 // Start with document base
297 nsCOMPtr
<nsIURI
> base
= doc
->GetBaseURI(aTryUseXHRDocBaseURI
);
299 // Collect array of xml:base attribute values up the parent chain. This
300 // is slightly slower for the case when there are xml:base attributes, but
301 // faster for the far more common case of there not being any such
303 // Also check for SVG elements which require special handling
304 nsAutoTArray
<nsString
, 5> baseAttrs
;
306 const nsIContent
*elem
= this;
308 // First check for SVG specialness (why is this SVG specific?)
310 nsIContent
* bindingParent
= elem
->GetBindingParent();
312 nsXBLBinding
* binding
= bindingParent
->GetXBLBinding();
314 // XXX sXBL/XBL2 issue
315 // If this is an anonymous XBL element use the binding
316 // document for the base URI.
317 // XXX Will fail with xml:base
318 base
= binding
->PrototypeBinding()->DocURI();
324 nsIURI
* explicitBaseURI
= elem
->GetExplicitBaseURI();
325 if (explicitBaseURI
) {
326 base
= explicitBaseURI
;
330 // Otherwise check for xml:base attribute
331 elem
->GetAttr(kNameSpaceID_XML
, nsGkAtoms::base
, attr
);
332 if (!attr
.IsEmpty()) {
333 baseAttrs
.AppendElement(attr
);
335 elem
= elem
->GetParent();
338 // Now resolve against all xml:base attrs
339 for (uint32_t i
= baseAttrs
.Length() - 1; i
!= uint32_t(-1); --i
) {
340 nsCOMPtr
<nsIURI
> newBase
;
341 nsresult rv
= NS_NewURI(getter_AddRefs(newBase
), baseAttrs
[i
],
342 doc
->GetDocumentCharacterSet().get(), base
);
343 // Do a security check, almost the same as nsDocument::SetBaseURL()
344 // Only need to do this on the final uri
345 if (NS_SUCCEEDED(rv
) && i
== 0) {
346 rv
= nsContentUtils::GetSecurityManager()->
347 CheckLoadURIWithPrincipal(NodePrincipal(), newBase
,
348 nsIScriptSecurityManager::STANDARD
);
350 if (NS_SUCCEEDED(rv
)) {
355 return base
.forget();
358 //----------------------------------------------------------------------
360 static inline JSObject
*
361 GetJSObjectChild(nsWrapperCache
* aCache
)
363 return aCache
->PreservingWrapper() ? aCache
->GetWrapperPreserveColor() : nullptr;
367 NeedsScriptTraverse(nsINode
* aNode
)
369 return aNode
->PreservingWrapper() && aNode
->GetWrapperPreserveColor() &&
370 !aNode
->IsBlackAndDoesNotNeedTracing(aNode
);
373 //----------------------------------------------------------------------
375 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsChildContentList
)
376 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsChildContentList
)
378 // If nsChildContentList is changed so that any additional fields are
379 // traversed by the cycle collector, then CAN_SKIP must be updated to
380 // check that the additional fields are null.
381 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsChildContentList
)
383 // nsChildContentList only ever has a single child, its wrapper, so if
384 // the wrapper is black, the list can't be part of a garbage cycle.
385 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsChildContentList
)
386 return tmp
->IsBlack();
387 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
389 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsChildContentList
)
390 return tmp
->IsBlackAndDoesNotNeedTracing(tmp
);
391 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
393 // CanSkipThis returns false to avoid problems with incomplete unlinking.
394 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsChildContentList
)
395 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
397 NS_INTERFACE_TABLE_HEAD(nsChildContentList
)
398 NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
399 NS_INTERFACE_TABLE(nsChildContentList
, nsINodeList
, nsIDOMNodeList
)
400 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsChildContentList
)
404 nsChildContentList::WrapObject(JSContext
*cx
)
406 return NodeListBinding::Wrap(cx
, this);
410 nsChildContentList::GetLength(uint32_t* aLength
)
412 *aLength
= mNode
? mNode
->GetChildCount() : 0;
418 nsChildContentList::Item(uint32_t aIndex
, nsIDOMNode
** aReturn
)
420 nsINode
* node
= Item(aIndex
);
427 return CallQueryInterface(node
, aReturn
);
431 nsChildContentList::Item(uint32_t aIndex
)
434 return mNode
->GetChildAt(aIndex
);
441 nsChildContentList::IndexOf(nsIContent
* aContent
)
444 return mNode
->IndexOf(aContent
);
450 //----------------------------------------------------------------------
453 FragmentOrElement::Children()
455 FragmentOrElement::nsDOMSlots
*slots
= DOMSlots();
457 if (!slots
->mChildrenList
) {
458 slots
->mChildrenList
= new nsContentList(this, kNameSpaceID_Wildcard
,
459 nsGkAtoms::_asterix
, nsGkAtoms::_asterix
,
463 return slots
->mChildrenList
;
467 //----------------------------------------------------------------------
470 NS_IMPL_ISUPPORTS(nsNodeWeakReference
,
473 nsNodeWeakReference::~nsNodeWeakReference()
476 NS_ASSERTION(mNode
->Slots()->mWeakReference
== this,
477 "Weak reference has wrong value");
478 mNode
->Slots()->mWeakReference
= nullptr;
483 nsNodeWeakReference::QueryReferent(const nsIID
& aIID
, void** aInstancePtr
)
485 return mNode
? mNode
->QueryInterface(aIID
, aInstancePtr
) :
486 NS_ERROR_NULL_POINTER
;
490 nsNodeWeakReference::SizeOfOnlyThis(mozilla::MallocSizeOf aMallocSizeOf
) const
492 return aMallocSizeOf(this);
496 NS_IMPL_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff
, mNode
)
498 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsNodeSupportsWeakRefTearoff
)
499 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
500 NS_INTERFACE_MAP_END_AGGREGATED(mNode
)
502 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsNodeSupportsWeakRefTearoff
)
503 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsNodeSupportsWeakRefTearoff
)
506 nsNodeSupportsWeakRefTearoff::GetWeakReference(nsIWeakReference
** aInstancePtr
)
508 nsINode::nsSlots
* slots
= mNode
->Slots();
509 if (!slots
->mWeakReference
) {
510 slots
->mWeakReference
= new nsNodeWeakReference(mNode
);
511 NS_ENSURE_TRUE(slots
->mWeakReference
, NS_ERROR_OUT_OF_MEMORY
);
514 NS_ADDREF(*aInstancePtr
= slots
->mWeakReference
);
519 //----------------------------------------------------------------------
520 FragmentOrElement::nsDOMSlots::nsDOMSlots()
521 : nsINode::nsSlots(),
523 mUndoManager(nullptr),
524 mBindingParent(nullptr)
528 FragmentOrElement::nsDOMSlots::~nsDOMSlots()
531 mAttributeMap
->DropReference();
536 FragmentOrElement::nsDOMSlots::Traverse(nsCycleCollectionTraversalCallback
&cb
, bool aIsXUL
)
538 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mSlots->mStyle");
539 cb
.NoteXPCOMChild(mStyle
.get());
541 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mSlots->mSMILOverrideStyle");
542 cb
.NoteXPCOMChild(mSMILOverrideStyle
.get());
544 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mSlots->mAttributeMap");
545 cb
.NoteXPCOMChild(mAttributeMap
.get());
547 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mSlots->mUndoManager");
548 cb
.NoteXPCOMChild(mUndoManager
.get());
551 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mSlots->mControllers");
552 cb
.NoteXPCOMChild(mControllers
);
555 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mSlots->mXBLBinding");
556 cb
.NoteNativeChild(mXBLBinding
, NS_CYCLE_COLLECTION_PARTICIPANT(nsXBLBinding
));
558 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mSlots->mXBLInsertionParent");
559 cb
.NoteXPCOMChild(mXBLInsertionParent
.get());
561 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mSlots->mShadowRoot");
562 cb
.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent
*, mShadowRoot
));
564 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mSlots->mContainingShadow");
565 cb
.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIContent
*, mContainingShadow
));
567 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mSlots->mChildrenList");
568 cb
.NoteXPCOMChild(NS_ISUPPORTS_CAST(nsIDOMNodeList
*, mChildrenList
));
570 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mSlots->mClassList");
571 cb
.NoteXPCOMChild(mClassList
.get());
573 if (mCustomElementData
) {
574 for (uint32_t i
= 0; i
< mCustomElementData
->mCallbackQueue
.Length(); i
++) {
575 mCustomElementData
->mCallbackQueue
[i
]->Traverse(cb
);
581 FragmentOrElement::nsDOMSlots::Unlink(bool aIsXUL
)
584 mSMILOverrideStyle
= nullptr;
586 mAttributeMap
->DropReference();
587 mAttributeMap
= nullptr;
590 NS_IF_RELEASE(mControllers
);
591 mXBLBinding
= nullptr;
592 mXBLInsertionParent
= nullptr;
593 mShadowRoot
= nullptr;
594 mContainingShadow
= nullptr;
595 mChildrenList
= nullptr;
596 mUndoManager
= nullptr;
597 mCustomElementData
= nullptr;
598 mClassList
= nullptr;
602 FragmentOrElement::nsDOMSlots::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf
) const
604 size_t n
= aMallocSizeOf(this);
607 n
+= mAttributeMap
->SizeOfIncludingThis(aMallocSizeOf
);
610 // Measurement of the following members may be added later if DMD finds it is
612 // - Superclass members (nsINode::nsSlots)
615 // - mSMILOverrideStyle
616 // - mSMILOverrideStyleRule
620 // The following members are not measured:
621 // - mBindingParent / mControllers: because they're non-owning
625 FragmentOrElement::FragmentOrElement(already_AddRefed
<mozilla::dom::NodeInfo
>& aNodeInfo
)
626 : nsIContent(aNodeInfo
)
630 FragmentOrElement::FragmentOrElement(already_AddRefed
<mozilla::dom::NodeInfo
>&& aNodeInfo
)
631 : nsIContent(aNodeInfo
)
635 FragmentOrElement::~FragmentOrElement()
637 NS_PRECONDITION(!IsInDoc(),
638 "Please remove this from the document properly");
644 already_AddRefed
<nsINodeList
>
645 FragmentOrElement::GetChildren(uint32_t aFilter
)
647 nsRefPtr
<nsSimpleContentList
> list
= new nsSimpleContentList(this);
648 AllChildrenIterator
iter(this, aFilter
);
649 while (nsIContent
* kid
= iter
.GetNextChild()) {
650 list
->AppendElement(kid
);
653 return list
.forget();
657 FindChromeAccessOnlySubtreeOwner(nsIContent
* aContent
)
659 if (aContent
->ChromeOnlyAccess()) {
660 bool chromeAccessOnly
= false;
661 while (aContent
&& !chromeAccessOnly
) {
662 chromeAccessOnly
= aContent
->IsRootOfChromeAccessOnlySubtree();
663 aContent
= aContent
->GetParent();
670 nsIContent::PreHandleEvent(EventChainPreVisitor
& aVisitor
)
672 //FIXME! Document how this event retargeting works, Bug 329124.
673 aVisitor
.mCanHandle
= true;
674 aVisitor
.mMayHaveListenerManager
= HasListenerManager();
676 // Don't propagate mouseover and mouseout events when mouse is moving
677 // inside chrome access only content.
678 bool isAnonForEvents
= IsRootOfChromeAccessOnlySubtree();
679 if ((aVisitor
.mEvent
->message
== NS_MOUSE_ENTER_SYNTH
||
680 aVisitor
.mEvent
->message
== NS_MOUSE_EXIT_SYNTH
||
681 aVisitor
.mEvent
->message
== NS_POINTER_OVER
||
682 aVisitor
.mEvent
->message
== NS_POINTER_OUT
) &&
683 // Check if we should stop event propagation when event has just been
684 // dispatched or when we're about to propagate from
685 // chrome access only subtree or if we are about to propagate out of
686 // a shadow root to a shadow root host.
687 ((this == aVisitor
.mEvent
->originalTarget
&&
688 !ChromeOnlyAccess()) || isAnonForEvents
|| GetShadowRoot())) {
689 nsCOMPtr
<nsIContent
> relatedTarget
=
690 do_QueryInterface(aVisitor
.mEvent
->AsMouseEvent()->relatedTarget
);
692 relatedTarget
->OwnerDoc() == OwnerDoc()) {
694 // In the web components case, we may need to stop propagation of events
695 // at shadow root host.
696 if (GetShadowRoot()) {
697 nsIContent
* adjustedTarget
=
698 Event::GetShadowRelatedTarget(this, relatedTarget
);
699 if (this == adjustedTarget
) {
700 aVisitor
.mParentTarget
= nullptr;
701 aVisitor
.mCanHandle
= false;
706 // If current target is anonymous for events or we know that related
707 // target is descendant of an element which is anonymous for events,
708 // we may want to stop event propagation.
709 // If this is the original target, aVisitor.mRelatedTargetIsInAnon
711 if (isAnonForEvents
|| aVisitor
.mRelatedTargetIsInAnon
||
712 (aVisitor
.mEvent
->originalTarget
== this &&
713 (aVisitor
.mRelatedTargetIsInAnon
=
714 relatedTarget
->ChromeOnlyAccess()))) {
715 nsIContent
* anonOwner
= FindChromeAccessOnlySubtreeOwner(this);
717 nsIContent
* anonOwnerRelated
=
718 FindChromeAccessOnlySubtreeOwner(relatedTarget
);
719 if (anonOwnerRelated
) {
720 // Note, anonOwnerRelated may still be inside some other
721 // native anonymous subtree. The case where anonOwner is still
722 // inside native anonymous subtree will be handled when event
723 // propagates up in the DOM tree.
724 while (anonOwner
!= anonOwnerRelated
&&
725 anonOwnerRelated
->ChromeOnlyAccess()) {
726 anonOwnerRelated
= FindChromeAccessOnlySubtreeOwner(anonOwnerRelated
);
728 if (anonOwner
== anonOwnerRelated
) {
730 nsCOMPtr
<nsIContent
> originalTarget
=
731 do_QueryInterface(aVisitor
.mEvent
->originalTarget
);
732 nsAutoString ot
, ct
, rt
;
733 if (originalTarget
) {
734 originalTarget
->Tag()->ToString(ot
);
737 relatedTarget
->Tag()->ToString(rt
);
738 printf("Stopping %s propagation:"
739 "\n\toriginalTarget=%s \n\tcurrentTarget=%s %s"
740 "\n\trelatedTarget=%s %s \n%s",
741 (aVisitor
.mEvent
->message
== NS_MOUSE_ENTER_SYNTH
)
742 ? "mouseover" : "mouseout",
743 NS_ConvertUTF16toUTF8(ot
).get(),
744 NS_ConvertUTF16toUTF8(ct
).get(),
746 ? "(is native anonymous)"
747 : (ChromeOnlyAccess()
748 ? "(is in native anonymous subtree)" : ""),
749 NS_ConvertUTF16toUTF8(rt
).get(),
750 relatedTarget
->ChromeOnlyAccess()
751 ? "(is in native anonymous subtree)" : "",
753 relatedTarget
->FindFirstNonChromeOnlyAccessContent() ==
754 originalTarget
->FindFirstNonChromeOnlyAccessContent())
755 ? "" : "Wrong event propagation!?!\n");
757 aVisitor
.mParentTarget
= nullptr;
758 // Event should not propagate to non-anon content.
759 aVisitor
.mCanHandle
= isAnonForEvents
;
768 nsIContent
* parent
= GetParent();
770 // Web components have a special event chain that need to account
771 // for destination insertion points where nodes have been distributed.
772 nsTArray
<nsIContent
*>* destPoints
= GetExistingDestInsertionPoints();
773 if (destPoints
&& !destPoints
->IsEmpty()) {
774 // Push destination insertion points to aVisitor.mDestInsertionPoints
775 // excluding shadow insertion points.
776 bool didPushNonShadowInsertionPoint
= false;
777 for (uint32_t i
= 0; i
< destPoints
->Length(); i
++) {
778 nsIContent
* point
= destPoints
->ElementAt(i
);
779 if (!ShadowRoot::IsShadowInsertionPoint(point
)) {
780 aVisitor
.mDestInsertionPoints
.AppendElement(point
);
781 didPushNonShadowInsertionPoint
= true;
785 // Next node in the event path is the final destination
786 // (non-shadow) insertion point that was pushed.
787 if (didPushNonShadowInsertionPoint
) {
788 parent
= aVisitor
.mDestInsertionPoints
.LastElement();
789 aVisitor
.mDestInsertionPoints
.SetLength(
790 aVisitor
.mDestInsertionPoints
.Length() - 1);
794 ShadowRoot
* thisShadowRoot
= ShadowRoot::FromNode(this);
795 if (thisShadowRoot
) {
796 // The following events must always be stopped at the root node of the node tree:
806 bool stopEvent
= false;
807 switch (aVisitor
.mEvent
->message
) {
810 case NS_FORM_SELECTED
:
814 case NS_RESIZE_EVENT
:
815 case NS_SCROLL_EVENT
:
818 case NS_USER_DEFINED_EVENT
:
819 if (aVisitor
.mDOMEvent
) {
820 nsAutoString eventType
;
821 aVisitor
.mDOMEvent
->GetType(eventType
);
822 if (eventType
.EqualsLiteral("abort") ||
823 eventType
.EqualsLiteral("error") ||
824 eventType
.EqualsLiteral("select") ||
825 eventType
.EqualsLiteral("change") ||
826 eventType
.EqualsLiteral("load") ||
827 eventType
.EqualsLiteral("reset") ||
828 eventType
.EqualsLiteral("resize") ||
829 eventType
.EqualsLiteral("scroll") ||
830 eventType
.EqualsLiteral("selectstart")) {
838 // If we do stop propagation, we still want to propagate
839 // the event to chrome (nsPIDOMWindow::GetParentTarget()).
840 // The load event is special in that we don't ever propagate it
842 nsCOMPtr
<nsPIDOMWindow
> win
= OwnerDoc()->GetWindow();
843 EventTarget
* parentTarget
= win
&& aVisitor
.mEvent
->message
!= NS_LOAD
844 ? win
->GetParentTarget() : nullptr;
846 aVisitor
.mParentTarget
= parentTarget
;
850 if (!aVisitor
.mDestInsertionPoints
.IsEmpty()) {
851 parent
= aVisitor
.mDestInsertionPoints
.LastElement();
852 aVisitor
.mDestInsertionPoints
.SetLength(
853 aVisitor
.mDestInsertionPoints
.Length() - 1);
855 // The pool host for the youngest shadow root is shadow DOM host,
856 // for older shadow roots, it is the shadow insertion point
857 // where the shadow root is projected, nullptr if none exists.
858 parent
= thisShadowRoot
->GetPoolHost();
862 // Event may need to be retargeted if this is the root of a native
863 // anonymous content subtree or event is dispatched somewhere inside XBL.
864 if (isAnonForEvents
) {
866 // If a DOM event is explicitly dispatched using node.dispatchEvent(), then
867 // all the events are allowed even in the native anonymous content..
868 nsCOMPtr
<nsIContent
> t
= do_QueryInterface(aVisitor
.mEvent
->originalTarget
);
869 NS_ASSERTION(!t
|| !t
->ChromeOnlyAccess() ||
870 aVisitor
.mEvent
->mClass
!= eMutationEventClass
||
872 "Mutation event dispatched in native anonymous content!?!");
874 aVisitor
.mEventTargetAtParent
= parent
;
875 } else if (parent
&& aVisitor
.mOriginalTargetIsInAnon
) {
876 nsCOMPtr
<nsIContent
> content(do_QueryInterface(aVisitor
.mEvent
->target
));
877 if (content
&& content
->GetBindingParent() == parent
) {
878 aVisitor
.mEventTargetAtParent
= parent
;
882 // check for an anonymous parent
883 // XXX XBL2/sXBL issue
884 if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR
)) {
885 nsIContent
* insertionParent
= GetXBLInsertionParent();
886 NS_ASSERTION(!(aVisitor
.mEventTargetAtParent
&& insertionParent
&&
887 aVisitor
.mEventTargetAtParent
!= insertionParent
),
888 "Retargeting and having insertion parent!");
889 if (insertionParent
) {
890 parent
= insertionParent
;
895 aVisitor
.mParentTarget
= parent
;
897 aVisitor
.mParentTarget
= GetComposedDoc();
903 nsIContent::GetAttr(int32_t aNameSpaceID
, nsIAtom
* aName
,
904 nsAString
& aResult
) const
907 return AsElement()->GetAttr(aNameSpaceID
, aName
, aResult
);
914 nsIContent::HasAttr(int32_t aNameSpaceID
, nsIAtom
* aName
) const
916 return IsElement() && AsElement()->HasAttr(aNameSpaceID
, aName
);
920 nsIContent::AttrValueIs(int32_t aNameSpaceID
,
922 const nsAString
& aValue
,
923 nsCaseTreatment aCaseSensitive
) const
925 return IsElement() &&
926 AsElement()->AttrValueIs(aNameSpaceID
, aName
, aValue
, aCaseSensitive
);
930 nsIContent::AttrValueIs(int32_t aNameSpaceID
,
933 nsCaseTreatment aCaseSensitive
) const
935 return IsElement() &&
936 AsElement()->AttrValueIs(aNameSpaceID
, aName
, aValue
, aCaseSensitive
);
940 nsIContent::IsFocusable(int32_t* aTabIndex
, bool aWithMouse
)
942 bool focusable
= IsFocusableInternal(aTabIndex
, aWithMouse
);
943 // Ensure that the return value and aTabIndex are consistent in the case
944 // we're in userfocusignored context.
945 if (focusable
|| (aTabIndex
&& *aTabIndex
!= -1)) {
946 if (nsContentUtils::IsUserFocusIgnored(this)) {
958 nsIContent::IsFocusableInternal(int32_t* aTabIndex
, bool aWithMouse
)
961 *aTabIndex
= -1; // Default, not tabbable
967 FragmentOrElement::WalkContentStyleRules(nsRuleWalker
* aRuleWalker
)
973 FragmentOrElement::IsLink(nsIURI
** aURI
) const
980 FragmentOrElement::GetBindingParent() const
982 nsDOMSlots
*slots
= GetExistingDOMSlots();
985 return slots
->mBindingParent
;
991 FragmentOrElement::GetXBLBinding() const
993 if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR
)) {
994 nsDOMSlots
*slots
= GetExistingDOMSlots();
996 return slots
->mXBLBinding
;
1004 FragmentOrElement::SetXBLBinding(nsXBLBinding
* aBinding
,
1005 nsBindingManager
* aOldBindingManager
)
1007 nsBindingManager
* bindingManager
;
1008 if (aOldBindingManager
) {
1009 MOZ_ASSERT(!aBinding
, "aOldBindingManager should only be provided "
1010 "when removing a binding.");
1011 bindingManager
= aOldBindingManager
;
1013 bindingManager
= OwnerDoc()->BindingManager();
1016 // After this point, aBinding will be the most-derived binding for aContent.
1017 // If we already have a binding for aContent, make sure to
1018 // remove it from the attached stack. Otherwise we might end up firing its
1019 // constructor twice (if aBinding inherits from it) or firing its constructor
1020 // after aContent has been deleted (if aBinding is null and the content node
1021 // dies before we process mAttachedStack).
1022 nsRefPtr
<nsXBLBinding
> oldBinding
= GetXBLBinding();
1024 bindingManager
->RemoveFromAttachedQueue(oldBinding
);
1028 SetFlags(NODE_MAY_BE_IN_BINDING_MNGR
);
1029 nsDOMSlots
*slots
= DOMSlots();
1030 slots
->mXBLBinding
= aBinding
;
1031 bindingManager
->AddBoundContent(this);
1033 nsDOMSlots
*slots
= GetExistingDOMSlots();
1035 slots
->mXBLBinding
= nullptr;
1037 bindingManager
->RemoveBoundContent(this);
1039 oldBinding
->SetBoundElement(nullptr);
1045 FragmentOrElement::GetXBLInsertionParent() const
1047 if (HasFlag(NODE_MAY_BE_IN_BINDING_MNGR
)) {
1048 nsDOMSlots
*slots
= GetExistingDOMSlots();
1050 return slots
->mXBLInsertionParent
;
1058 FragmentOrElement::GetShadowRoot() const
1060 nsDOMSlots
*slots
= GetExistingDOMSlots();
1062 return slots
->mShadowRoot
;
1068 FragmentOrElement::GetContainingShadow() const
1070 nsDOMSlots
*slots
= GetExistingDOMSlots();
1072 return slots
->mContainingShadow
;
1078 FragmentOrElement::SetShadowRoot(ShadowRoot
* aShadowRoot
)
1080 nsDOMSlots
*slots
= DOMSlots();
1081 slots
->mShadowRoot
= aShadowRoot
;
1084 nsTArray
<nsIContent
*>&
1085 FragmentOrElement::DestInsertionPoints()
1087 nsDOMSlots
*slots
= DOMSlots();
1088 return slots
->mDestInsertionPoints
;
1091 nsTArray
<nsIContent
*>*
1092 FragmentOrElement::GetExistingDestInsertionPoints() const
1094 nsDOMSlots
*slots
= GetExistingDOMSlots();
1096 return &slots
->mDestInsertionPoints
;
1102 FragmentOrElement::SetXBLInsertionParent(nsIContent
* aContent
)
1105 nsDOMSlots
*slots
= DOMSlots();
1106 SetFlags(NODE_MAY_BE_IN_BINDING_MNGR
);
1107 slots
->mXBLInsertionParent
= aContent
;
1109 nsDOMSlots
*slots
= GetExistingDOMSlots();
1111 slots
->mXBLInsertionParent
= nullptr;
1117 FragmentOrElement::GetCustomElementData() const
1119 nsDOMSlots
*slots
= GetExistingDOMSlots();
1121 return slots
->mCustomElementData
;
1127 FragmentOrElement::SetCustomElementData(CustomElementData
* aData
)
1129 nsDOMSlots
*slots
= DOMSlots();
1130 MOZ_ASSERT(!slots
->mCustomElementData
, "Custom element data may not be changed once set.");
1131 slots
->mCustomElementData
= aData
;
1135 FragmentOrElement::InsertChildAt(nsIContent
* aKid
,
1139 NS_PRECONDITION(aKid
, "null ptr");
1141 return doInsertChildAt(aKid
, aIndex
, aNotify
, mAttrsAndChildren
);
1145 FragmentOrElement::RemoveChildAt(uint32_t aIndex
, bool aNotify
)
1147 nsCOMPtr
<nsIContent
> oldKid
= mAttrsAndChildren
.GetSafeChildAt(aIndex
);
1148 NS_ASSERTION(oldKid
== GetChildAt(aIndex
), "Unexpected child in RemoveChildAt");
1151 doRemoveChildAt(aIndex
, aNotify
, oldKid
, mAttrsAndChildren
);
1156 FragmentOrElement::GetTextContentInternal(nsAString
& aTextContent
,
1157 ErrorResult
& aError
)
1159 if(!nsContentUtils::GetNodeTextContent(this, true, aTextContent
)) {
1160 aError
.Throw(NS_ERROR_OUT_OF_MEMORY
);
1165 FragmentOrElement::SetTextContentInternal(const nsAString
& aTextContent
,
1166 ErrorResult
& aError
)
1168 aError
= nsContentUtils::SetNodeTextContent(this, aTextContent
, false);
1172 FragmentOrElement::DestroyContent()
1174 nsIDocument
*document
= OwnerDoc();
1175 document
->BindingManager()->RemovedFromDocument(this, document
);
1176 document
->ClearBoxObjectFor(this);
1178 // XXX We really should let cycle collection do this, but that currently still
1179 // leaks (see https://bugzilla.mozilla.org/show_bug.cgi?id=406684).
1180 ReleaseWrapper(this);
1182 uint32_t i
, count
= mAttrsAndChildren
.ChildCount();
1183 for (i
= 0; i
< count
; ++i
) {
1184 // The child can remove itself from the parent in BindToTree.
1185 mAttrsAndChildren
.ChildAt(i
)->DestroyContent();
1190 FragmentOrElement::SaveSubtreeState()
1192 uint32_t i
, count
= mAttrsAndChildren
.ChildCount();
1193 for (i
= 0; i
< count
; ++i
) {
1194 mAttrsAndChildren
.ChildAt(i
)->SaveSubtreeState();
1198 //----------------------------------------------------------------------
1200 // Generic DOMNode implementations
1203 FragmentOrElement::FireNodeInserted(nsIDocument
* aDoc
,
1205 nsTArray
<nsCOMPtr
<nsIContent
> >& aNodes
)
1207 uint32_t count
= aNodes
.Length();
1208 for (uint32_t i
= 0; i
< count
; ++i
) {
1209 nsIContent
* childContent
= aNodes
[i
];
1211 if (nsContentUtils::HasMutationListeners(childContent
,
1212 NS_EVENT_BITS_MUTATION_NODEINSERTED
, aParent
)) {
1213 InternalMutationEvent
mutation(true, NS_MUTATION_NODEINSERTED
);
1214 mutation
.mRelatedNode
= do_QueryInterface(aParent
);
1216 mozAutoSubtreeModified
subtree(aDoc
, aParent
);
1217 (new AsyncEventDispatcher(childContent
, mutation
))->RunDOMEventWhenSafe();
1222 //----------------------------------------------------------------------
1224 // nsISupports implementation
1226 #define SUBTREE_UNBINDINGS_PER_RUNNABLE 500
1228 class ContentUnbinder
: public nsRunnable
1241 void UnbindSubtree(nsIContent
* aNode
)
1243 if (aNode
->NodeType() != nsIDOMNode::ELEMENT_NODE
&&
1244 aNode
->NodeType() != nsIDOMNode::DOCUMENT_FRAGMENT_NODE
) {
1247 FragmentOrElement
* container
= static_cast<FragmentOrElement
*>(aNode
);
1248 uint32_t childCount
= container
->mAttrsAndChildren
.ChildCount();
1250 while (childCount
-- > 0) {
1251 // Hold a strong ref to the node when we remove it, because we may be
1252 // the last reference to it. We need to call TakeChildAt() and
1253 // update mFirstChild before calling UnbindFromTree, since this last
1254 // can notify various observers and they should really see consistent
1256 nsCOMPtr
<nsIContent
> child
=
1257 container
->mAttrsAndChildren
.TakeChildAt(childCount
);
1258 if (childCount
== 0) {
1259 container
->mFirstChild
= nullptr;
1261 UnbindSubtree(child
);
1262 child
->UnbindFromTree();
1269 nsAutoScriptBlocker scriptBlocker
;
1270 uint32_t len
= mSubtreeRoots
.Length();
1272 PRTime start
= PR_Now();
1273 for (uint32_t i
= 0; i
< len
; ++i
) {
1274 UnbindSubtree(mSubtreeRoots
[i
]);
1276 mSubtreeRoots
.Clear();
1277 Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_CONTENT_UNBIND
,
1278 uint32_t(PR_Now() - start
) / PR_USEC_PER_MSEC
);
1280 nsCycleCollector_dispatchDeferredDeletion();
1281 if (this == sContentUnbinder
) {
1282 sContentUnbinder
= nullptr;
1284 nsRefPtr
<ContentUnbinder
> next
;
1286 sContentUnbinder
= next
;
1287 next
->mLast
= mLast
;
1289 NS_DispatchToMainThread(next
);
1295 static void UnbindAll()
1297 nsRefPtr
<ContentUnbinder
> ub
= sContentUnbinder
;
1298 sContentUnbinder
= nullptr;
1305 static void Append(nsIContent
* aSubtreeRoot
)
1307 if (!sContentUnbinder
) {
1308 sContentUnbinder
= new ContentUnbinder();
1309 nsCOMPtr
<nsIRunnable
> e
= sContentUnbinder
;
1310 NS_DispatchToMainThread(e
);
1313 if (sContentUnbinder
->mLast
->mSubtreeRoots
.Length() >=
1314 SUBTREE_UNBINDINGS_PER_RUNNABLE
) {
1315 sContentUnbinder
->mLast
->mNext
= new ContentUnbinder();
1316 sContentUnbinder
->mLast
= sContentUnbinder
->mLast
->mNext
;
1318 sContentUnbinder
->mLast
->mSubtreeRoots
.AppendElement(aSubtreeRoot
);
1322 nsAutoTArray
<nsCOMPtr
<nsIContent
>,
1323 SUBTREE_UNBINDINGS_PER_RUNNABLE
> mSubtreeRoots
;
1324 nsRefPtr
<ContentUnbinder
> mNext
;
1325 ContentUnbinder
* mLast
;
1326 static ContentUnbinder
* sContentUnbinder
;
1329 ContentUnbinder
* ContentUnbinder::sContentUnbinder
= nullptr;
1332 FragmentOrElement::ClearContentUnbinder()
1334 ContentUnbinder::UnbindAll();
1337 NS_IMPL_CYCLE_COLLECTION_CLASS(FragmentOrElement
)
1339 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement
)
1340 nsINode::Unlink(tmp
);
1342 // The XBL binding is removed by RemoveFromBindingManagerRunnable
1343 // which is dispatched in UnbindFromTree.
1345 if (tmp
->HasProperties()) {
1346 if (tmp
->IsHTML() || tmp
->IsSVG()) {
1347 nsIAtom
*** props
= Element::HTMLSVGPropertiesToTraverseAndUnlink();
1348 for (uint32_t i
= 0; props
[i
]; ++i
) {
1349 tmp
->DeleteProperty(*props
[i
]);
1354 // Unlink child content (and unbind our subtree).
1355 if (tmp
->UnoptimizableCCNode() || !nsCCUncollectableMarker::sGeneration
) {
1356 uint32_t childCount
= tmp
->mAttrsAndChildren
.ChildCount();
1358 // Don't allow script to run while we're unbinding everything.
1359 nsAutoScriptBlocker scriptBlocker
;
1360 while (childCount
-- > 0) {
1361 // Hold a strong ref to the node when we remove it, because we may be
1362 // the last reference to it. We need to call TakeChildAt() and
1363 // update mFirstChild before calling UnbindFromTree, since this last
1364 // can notify various observers and they should really see consistent
1366 nsCOMPtr
<nsIContent
> child
= tmp
->mAttrsAndChildren
.TakeChildAt(childCount
);
1367 if (childCount
== 0) {
1368 tmp
->mFirstChild
= nullptr;
1370 child
->UnbindFromTree();
1373 } else if (!tmp
->GetParent() && tmp
->mAttrsAndChildren
.ChildCount()) {
1374 ContentUnbinder::Append(tmp
);
1376 The subtree root will end up to a ContentUnbinder, and that will
1377 unbind the child nodes.
1380 // Clear flag here because unlinking slots will clear the
1381 // containing shadow root pointer.
1382 tmp
->UnsetFlags(NODE_IS_IN_SHADOW_TREE
);
1384 // Unlink any DOM slots of interest.
1386 nsDOMSlots
*slots
= tmp
->GetExistingDOMSlots();
1388 slots
->Unlink(tmp
->IsXUL());
1394 if (!tmp
->GetParentNode() && (doc
= tmp
->OwnerDoc())) {
1395 doc
->BindingManager()->RemovedFromDocument(tmp
, doc
);
1398 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1400 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(FragmentOrElement
)
1403 FragmentOrElement::MarkUserData(void* aObject
, nsIAtom
* aKey
, void* aChild
,
1406 uint32_t* gen
= static_cast<uint32_t*>(aData
);
1407 xpc_MarkInCCGeneration(static_cast<nsISupports
*>(aChild
), *gen
);
1411 FragmentOrElement::MarkNodeChildren(nsINode
* aNode
)
1413 JSObject
* o
= GetJSObjectChild(aNode
);
1415 JS::ExposeObjectToActiveJS(o
);
1418 EventListenerManager
* elm
= aNode
->GetExistingListenerManager();
1423 if (aNode
->HasProperties()) {
1424 nsIDocument
* ownerDoc
= aNode
->OwnerDoc();
1425 ownerDoc
->PropertyTable(DOM_USER_DATA
)->
1426 Enumerate(aNode
, FragmentOrElement::MarkUserData
,
1427 &nsCCUncollectableMarker::sGeneration
);
1432 FindOptimizableSubtreeRoot(nsINode
* aNode
)
1435 while ((p
= aNode
->GetParentNode())) {
1436 if (aNode
->UnoptimizableCCNode()) {
1442 if (aNode
->UnoptimizableCCNode()) {
1448 StaticAutoPtr
<nsTHashtable
<nsPtrHashKey
<nsINode
>>> gCCBlackMarkedNodes
;
1450 static PLDHashOperator
1451 VisitBlackMarkedNode(nsPtrHashKey
<nsINode
>* aEntry
, void*)
1453 nsINode
* n
= aEntry
->GetKey();
1454 n
->SetCCMarkedRoot(false);
1455 n
->SetInCCBlackTree(false);
1456 return PL_DHASH_NEXT
;
1460 ClearBlackMarkedNodes()
1462 if (!gCCBlackMarkedNodes
) {
1465 gCCBlackMarkedNodes
->EnumerateEntries(VisitBlackMarkedNode
, nullptr);
1466 gCCBlackMarkedNodes
= nullptr;
1471 FragmentOrElement::RemoveBlackMarkedNode(nsINode
* aNode
)
1473 if (!gCCBlackMarkedNodes
) {
1476 gCCBlackMarkedNodes
->RemoveEntry(aNode
);
1481 FragmentOrElement::CanSkipInCC(nsINode
* aNode
)
1483 // Don't try to optimize anything during shutdown.
1484 if (nsCCUncollectableMarker::sGeneration
== 0) {
1488 //XXXsmaug Need to figure out in which cases Shadow DOM can be optimized out
1489 // from the CC graph.
1490 nsIDocument
* currentDoc
= aNode
->GetUncomposedDoc();
1492 nsCCUncollectableMarker::InGeneration(currentDoc
->GetMarkedCCGeneration())) {
1493 return !NeedsScriptTraverse(aNode
);
1496 // Bail out early if aNode is somewhere in anonymous content,
1497 // or otherwise unusual.
1498 if (aNode
->UnoptimizableCCNode()) {
1503 currentDoc
? static_cast<nsINode
*>(currentDoc
) :
1504 FindOptimizableSubtreeRoot(aNode
);
1509 // Subtree has been traversed already.
1510 if (root
->CCMarkedRoot()) {
1511 return root
->InCCBlackTree() && !NeedsScriptTraverse(aNode
);
1514 if (!gCCBlackMarkedNodes
) {
1515 gCCBlackMarkedNodes
= new nsTHashtable
<nsPtrHashKey
<nsINode
> >(1020);
1518 // nodesToUnpurple contains nodes which will be removed
1519 // from the purple buffer if the DOM tree is black.
1520 nsAutoTArray
<nsIContent
*, 1020> nodesToUnpurple
;
1521 // grayNodes need script traverse, so they aren't removed from
1522 // the purple buffer, but are marked to be in black subtree so that
1523 // traverse is faster.
1524 nsAutoTArray
<nsINode
*, 1020> grayNodes
;
1526 bool foundBlack
= root
->IsBlack();
1527 if (root
!= currentDoc
) {
1528 currentDoc
= nullptr;
1529 if (NeedsScriptTraverse(root
)) {
1530 grayNodes
.AppendElement(root
);
1531 } else if (static_cast<nsIContent
*>(root
)->IsPurple()) {
1532 nodesToUnpurple
.AppendElement(static_cast<nsIContent
*>(root
));
1536 // Traverse the subtree and check if we could know without CC
1537 // that it is black.
1538 // Note, this traverse is non-virtual and inline, so it should be a lot faster
1539 // than CC's generic traverse.
1540 for (nsIContent
* node
= root
->GetFirstChild(); node
;
1541 node
= node
->GetNextNode(root
)) {
1542 foundBlack
= foundBlack
|| node
->IsBlack();
1543 if (foundBlack
&& currentDoc
) {
1544 // If we can mark the whole document black, no need to optimize
1545 // so much, since when the next purple node in the document will be
1546 // handled, it is fast to check that currentDoc is in CCGeneration.
1549 if (NeedsScriptTraverse(node
)) {
1550 // Gray nodes need real CC traverse.
1551 grayNodes
.AppendElement(node
);
1552 } else if (node
->IsPurple()) {
1553 nodesToUnpurple
.AppendElement(node
);
1557 root
->SetCCMarkedRoot(true);
1558 root
->SetInCCBlackTree(foundBlack
);
1559 gCCBlackMarkedNodes
->PutEntry(root
);
1566 // Special case documents. If we know the document is black,
1567 // we can mark the document to be in CCGeneration.
1569 MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration
);
1571 for (uint32_t i
= 0; i
< grayNodes
.Length(); ++i
) {
1572 nsINode
* node
= grayNodes
[i
];
1573 node
->SetInCCBlackTree(true);
1574 gCCBlackMarkedNodes
->PutEntry(node
);
1578 // Subtree is black, we can remove non-gray purple nodes from
1580 for (uint32_t i
= 0; i
< nodesToUnpurple
.Length(); ++i
) {
1581 nsIContent
* purple
= nodesToUnpurple
[i
];
1582 // Can't remove currently handled purple node.
1583 if (purple
!= aNode
) {
1584 purple
->RemovePurple();
1587 return !NeedsScriptTraverse(aNode
);
1590 nsAutoTArray
<nsINode
*, 1020>* gPurpleRoots
= nullptr;
1591 nsAutoTArray
<nsIContent
*, 1020>* gNodesToUnbind
= nullptr;
1593 void ClearCycleCollectorCleanupData()
1596 uint32_t len
= gPurpleRoots
->Length();
1597 for (uint32_t i
= 0; i
< len
; ++i
) {
1598 nsINode
* n
= gPurpleRoots
->ElementAt(i
);
1599 n
->SetIsPurpleRoot(false);
1601 delete gPurpleRoots
;
1602 gPurpleRoots
= nullptr;
1604 if (gNodesToUnbind
) {
1605 uint32_t len
= gNodesToUnbind
->Length();
1606 for (uint32_t i
= 0; i
< len
; ++i
) {
1607 nsIContent
* c
= gNodesToUnbind
->ElementAt(i
);
1608 c
->SetIsPurpleRoot(false);
1609 ContentUnbinder::Append(c
);
1611 delete gNodesToUnbind
;
1612 gNodesToUnbind
= nullptr;
1617 ShouldClearPurple(nsIContent
* aContent
)
1619 if (aContent
&& aContent
->IsPurple()) {
1623 JSObject
* o
= GetJSObjectChild(aContent
);
1624 if (o
&& JS::ObjectIsMarkedGray(o
)) {
1628 if (aContent
->HasListenerManager()) {
1632 return aContent
->HasProperties();
1635 // If aNode is not optimizable, but is an element
1636 // with a frame in a document which has currently active presshell,
1637 // we can act as if it was optimizable. When the primary frame dies, aNode
1638 // will end up to the purple buffer because of the refcount change.
1640 NodeHasActiveFrame(nsIDocument
* aCurrentDoc
, nsINode
* aNode
)
1642 return aCurrentDoc
->GetShell() && aNode
->IsElement() &&
1643 aNode
->AsElement()->GetPrimaryFrame();
1647 OwnedByBindingManager(nsIDocument
* aCurrentDoc
, nsINode
* aNode
)
1649 return aNode
->IsElement() && aNode
->AsElement()->GetXBLBinding();
1652 // CanSkip checks if aNode is black, and if it is, returns
1653 // true. If aNode is in a black DOM tree, CanSkip may also remove other objects
1654 // from purple buffer and unmark event listeners and user data.
1655 // If the root of the DOM tree is a document, less optimizations are done
1656 // since checking the blackness of the current document is usually fast and we
1657 // don't want slow down such common cases.
1659 FragmentOrElement::CanSkip(nsINode
* aNode
, bool aRemovingAllowed
)
1661 // Don't try to optimize anything during shutdown.
1662 if (nsCCUncollectableMarker::sGeneration
== 0) {
1666 bool unoptimizable
= aNode
->UnoptimizableCCNode();
1667 nsIDocument
* currentDoc
= aNode
->GetUncomposedDoc();
1669 nsCCUncollectableMarker::InGeneration(currentDoc
->GetMarkedCCGeneration()) &&
1670 (!unoptimizable
|| NodeHasActiveFrame(currentDoc
, aNode
) ||
1671 OwnedByBindingManager(currentDoc
, aNode
))) {
1672 MarkNodeChildren(aNode
);
1676 if (unoptimizable
) {
1680 nsINode
* root
= currentDoc
? static_cast<nsINode
*>(currentDoc
) :
1681 FindOptimizableSubtreeRoot(aNode
);
1686 // Subtree has been traversed already, and aNode has
1687 // been handled in a way that doesn't require revisiting it.
1688 if (root
->IsPurpleRoot()) {
1692 // nodesToClear contains nodes which are either purple or
1694 nsAutoTArray
<nsIContent
*, 1020> nodesToClear
;
1696 bool foundBlack
= root
->IsBlack();
1697 bool domOnlyCycle
= false;
1698 if (root
!= currentDoc
) {
1699 currentDoc
= nullptr;
1701 domOnlyCycle
= static_cast<nsIContent
*>(root
)->OwnedOnlyByTheDOMTree();
1703 if (ShouldClearPurple(static_cast<nsIContent
*>(root
))) {
1704 nodesToClear
.AppendElement(static_cast<nsIContent
*>(root
));
1708 // Traverse the subtree and check if we could know without CC
1709 // that it is black.
1710 // Note, this traverse is non-virtual and inline, so it should be a lot faster
1711 // than CC's generic traverse.
1712 for (nsIContent
* node
= root
->GetFirstChild(); node
;
1713 node
= node
->GetNextNode(root
)) {
1714 foundBlack
= foundBlack
|| node
->IsBlack();
1716 domOnlyCycle
= false;
1718 // If we can mark the whole document black, no need to optimize
1719 // so much, since when the next purple node in the document will be
1720 // handled, it is fast to check that the currentDoc is in CCGeneration.
1723 // No need to put stuff to the nodesToClear array, if we can clear it
1725 if (node
->IsPurple() && (node
!= aNode
|| aRemovingAllowed
)) {
1726 node
->RemovePurple();
1728 MarkNodeChildren(node
);
1730 domOnlyCycle
= domOnlyCycle
&& node
->OwnedOnlyByTheDOMTree();
1731 if (ShouldClearPurple(node
)) {
1732 // Collect interesting nodes which we can clear if we find that
1733 // they are kept alive in a black tree or are in a DOM-only cycle.
1734 nodesToClear
.AppendElement(node
);
1739 if (!currentDoc
|| !foundBlack
) {
1740 root
->SetIsPurpleRoot(true);
1742 if (!gNodesToUnbind
) {
1743 gNodesToUnbind
= new nsAutoTArray
<nsIContent
*, 1020>();
1745 gNodesToUnbind
->AppendElement(static_cast<nsIContent
*>(root
));
1746 for (uint32_t i
= 0; i
< nodesToClear
.Length(); ++i
) {
1747 nsIContent
* n
= nodesToClear
[i
];
1748 if ((n
!= aNode
|| aRemovingAllowed
) && n
->IsPurple()) {
1754 if (!gPurpleRoots
) {
1755 gPurpleRoots
= new nsAutoTArray
<nsINode
*, 1020>();
1757 gPurpleRoots
->AppendElement(root
);
1766 // Special case documents. If we know the document is black,
1767 // we can mark the document to be in CCGeneration.
1769 MarkUncollectableForCCGeneration(nsCCUncollectableMarker::sGeneration
);
1770 MarkNodeChildren(currentDoc
);
1773 // Subtree is black, so we can remove purple nodes from
1774 // purple buffer and mark stuff that to be certainly alive.
1775 for (uint32_t i
= 0; i
< nodesToClear
.Length(); ++i
) {
1776 nsIContent
* n
= nodesToClear
[i
];
1777 MarkNodeChildren(n
);
1778 // Can't remove currently handled purple node,
1779 // unless aRemovingAllowed is true.
1780 if ((n
!= aNode
|| aRemovingAllowed
) && n
->IsPurple()) {
1788 FragmentOrElement::CanSkipThis(nsINode
* aNode
)
1790 if (nsCCUncollectableMarker::sGeneration
== 0) {
1793 if (aNode
->IsBlack()) {
1796 nsIDocument
* c
= aNode
->GetUncomposedDoc();
1798 ((c
&& nsCCUncollectableMarker::InGeneration(c
->GetMarkedCCGeneration())) ||
1799 aNode
->InCCBlackTree()) && !NeedsScriptTraverse(aNode
);
1803 FragmentOrElement::InitCCCallbacks()
1805 nsCycleCollector_setForgetSkippableCallback(ClearCycleCollectorCleanupData
);
1806 nsCycleCollector_setBeforeUnlinkCallback(ClearBlackMarkedNodes
);
1809 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(FragmentOrElement
)
1810 return FragmentOrElement::CanSkip(tmp
, aRemovingAllowed
);
1811 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1813 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(FragmentOrElement
)
1814 return FragmentOrElement::CanSkipInCC(tmp
);
1815 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1817 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(FragmentOrElement
)
1818 return FragmentOrElement::CanSkipThis(tmp
);
1819 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1821 static const char* kNSURIs
[] = {
1836 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(FragmentOrElement
)
1837 if (MOZ_UNLIKELY(cb
.WantDebugInfo())) {
1839 uint32_t nsid
= tmp
->GetNameSpaceID();
1840 nsAtomCString
localName(tmp
->NodeInfo()->NameAtom());
1842 if (tmp
->OwnerDoc()->GetDocumentURI()) {
1843 tmp
->OwnerDoc()->GetDocumentURI()->GetSpec(uri
);
1847 nsIAtom
* idAtom
= tmp
->GetID();
1849 id
.AppendLiteral(" id='");
1850 id
.Append(nsDependentAtomString(idAtom
));
1854 nsAutoString classes
;
1855 const nsAttrValue
* classAttrValue
= tmp
->GetClasses();
1856 if (classAttrValue
) {
1857 classes
.AppendLiteral(" class='");
1858 nsAutoString classString
;
1859 classAttrValue
->ToString(classString
);
1860 classString
.ReplaceChar(char16_t('\n'), char16_t(' '));
1861 classes
.Append(classString
);
1862 classes
.Append('\'');
1865 nsAutoCString orphan
;
1866 if (!tmp
->IsInDoc() &&
1867 // Ignore xbl:content, which is never in the document and hence always
1868 // appears to be orphaned.
1869 !tmp
->NodeInfo()->Equals(nsGkAtoms::content
, kNameSpaceID_XBL
)) {
1870 orphan
.AppendLiteral(" (orphan)");
1873 const char* nsuri
= nsid
< ArrayLength(kNSURIs
) ? kNSURIs
[nsid
] : "";
1874 PR_snprintf(name
, sizeof(name
), "FragmentOrElement%s %s%s%s%s %s",
1877 NS_ConvertUTF16toUTF8(id
).get(),
1878 NS_ConvertUTF16toUTF8(classes
).get(),
1881 cb
.DescribeRefCountedNode(tmp
->mRefCnt
.get(), name
);
1884 NS_IMPL_CYCLE_COLLECTION_DESCRIBE(FragmentOrElement
, tmp
->mRefCnt
.get())
1887 // Always need to traverse script objects, so do that before we check
1888 // if we're uncollectable.
1889 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
1891 if (!nsINode::Traverse(tmp
, cb
)) {
1892 return NS_SUCCESS_INTERRUPTED_TRAVERSE
;
1895 tmp
->OwnerDoc()->BindingManager()->Traverse(tmp
, cb
);
1897 if (tmp
->HasProperties()) {
1898 if (tmp
->IsHTML() || tmp
->IsSVG()) {
1899 nsIAtom
*** props
= Element::HTMLSVGPropertiesToTraverseAndUnlink();
1900 for (uint32_t i
= 0; props
[i
]; ++i
) {
1901 nsISupports
* property
=
1902 static_cast<nsISupports
*>(tmp
->GetProperty(*props
[i
]));
1903 cb
.NoteXPCOMChild(property
);
1908 // Traverse attribute names and child content.
1911 uint32_t attrs
= tmp
->mAttrsAndChildren
.AttrCount();
1912 for (i
= 0; i
< attrs
; i
++) {
1913 const nsAttrName
* name
= tmp
->mAttrsAndChildren
.AttrNameAt(i
);
1914 if (!name
->IsAtom()) {
1915 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
,
1916 "mAttrsAndChildren[i]->NodeInfo()");
1917 cb
.NoteNativeChild(name
->NodeInfo(),
1918 NS_CYCLE_COLLECTION_PARTICIPANT(NodeInfo
));
1922 uint32_t kids
= tmp
->mAttrsAndChildren
.ChildCount();
1923 for (i
= 0; i
< kids
; i
++) {
1924 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mAttrsAndChildren[i]");
1925 cb
.NoteXPCOMChild(tmp
->mAttrsAndChildren
.GetSafeChildAt(i
));
1929 // Traverse any DOM slots of interest.
1931 nsDOMSlots
*slots
= tmp
->GetExistingDOMSlots();
1933 slots
->Traverse(cb
, tmp
->IsXUL());
1936 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1939 NS_INTERFACE_MAP_BEGIN(FragmentOrElement
)
1940 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
1941 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(FragmentOrElement
)
1942 NS_INTERFACE_MAP_ENTRY(Element
)
1943 NS_INTERFACE_MAP_ENTRY(nsIContent
)
1944 NS_INTERFACE_MAP_ENTRY(nsINode
)
1945 NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget
)
1946 NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget
)
1947 NS_INTERFACE_MAP_ENTRY_TEAROFF(nsISupportsWeakReference
,
1948 new nsNodeSupportsWeakRefTearoff(this))
1949 // DOM bindings depend on the identity pointer being the
1950 // same as nsINode (which nsIContent inherits).
1951 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIContent
)
1952 NS_INTERFACE_MAP_END
1954 NS_IMPL_CYCLE_COLLECTING_ADDREF(FragmentOrElement
)
1955 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(FragmentOrElement
,
1956 nsNodeUtils::LastRelease(this))
1958 //----------------------------------------------------------------------
1961 FragmentOrElement::CopyInnerTo(FragmentOrElement
* aDst
)
1963 uint32_t i
, count
= mAttrsAndChildren
.AttrCount();
1964 for (i
= 0; i
< count
; ++i
) {
1965 const nsAttrName
* name
= mAttrsAndChildren
.AttrNameAt(i
);
1966 const nsAttrValue
* value
= mAttrsAndChildren
.AttrAt(i
);
1967 nsAutoString valStr
;
1968 value
->ToString(valStr
);
1969 nsresult rv
= aDst
->SetAttr(name
->NamespaceID(), name
->LocalName(),
1970 name
->GetPrefix(), valStr
, false);
1971 NS_ENSURE_SUCCESS(rv
, rv
);
1977 const nsTextFragment
*
1978 FragmentOrElement::GetText()
1984 FragmentOrElement::TextLength() const
1986 // We can remove this assertion if it turns out to be useful to be able
1987 // to depend on this returning 0
1988 NS_NOTREACHED("called FragmentOrElement::TextLength");
1994 FragmentOrElement::SetText(const char16_t
* aBuffer
, uint32_t aLength
,
1997 NS_ERROR("called FragmentOrElement::SetText");
1999 return NS_ERROR_FAILURE
;
2003 FragmentOrElement::AppendText(const char16_t
* aBuffer
, uint32_t aLength
,
2006 NS_ERROR("called FragmentOrElement::AppendText");
2008 return NS_ERROR_FAILURE
;
2012 FragmentOrElement::TextIsOnlyWhitespace()
2018 FragmentOrElement::HasTextForTranslation()
2024 FragmentOrElement::AppendTextTo(nsAString
& aResult
)
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");
2032 FragmentOrElement::AppendTextTo(nsAString
& aResult
, const mozilla::fallible_t
&)
2034 // We can remove this assertion if it turns out to be useful to be able
2035 // to depend on this appending nothing.
2036 NS_NOTREACHED("called FragmentOrElement::TextLength");
2042 FragmentOrElement::GetChildCount() const
2044 return mAttrsAndChildren
.ChildCount();
2048 FragmentOrElement::GetChildAt(uint32_t aIndex
) const
2050 return mAttrsAndChildren
.GetSafeChildAt(aIndex
);
2053 nsIContent
* const *
2054 FragmentOrElement::GetChildArray(uint32_t* aChildCount
) const
2056 return mAttrsAndChildren
.GetChildArray(aChildCount
);
2060 FragmentOrElement::IndexOf(const nsINode
* aPossibleChild
) const
2062 return mAttrsAndChildren
.IndexOfChild(aPossibleChild
);
2065 // Try to keep the size of StringBuilder close to a jemalloc bucket size.
2066 #define STRING_BUFFER_UNITS 1020
2070 // We put StringBuilder in the anonymous namespace to prevent anything outside
2071 // this file from accidentally being linked against it.
2079 Unit() : mAtom(nullptr), mType(eUnknown
), mLength(0)
2081 MOZ_COUNT_CTOR(StringBuilder::Unit
);
2085 if (mType
== eString
|| mType
== eStringWithEncode
) {
2088 MOZ_COUNT_DTOR(StringBuilder::Unit
);
2099 eTextFragmentWithEncode
,
2105 const char* mLiteral
;
2106 nsAutoString
* mString
;
2107 const nsTextFragment
* mTextFragment
;
2113 StringBuilder() : mLast(this), mLength(0)
2115 MOZ_COUNT_CTOR(StringBuilder
);
2120 MOZ_COUNT_DTOR(StringBuilder
);
2123 void Append(nsIAtom
* aAtom
)
2125 Unit
* u
= AddUnit();
2127 u
->mType
= Unit::eAtom
;
2128 uint32_t len
= aAtom
->GetLength();
2134 void Append(const char (&aLiteral
)[N
])
2136 Unit
* u
= AddUnit();
2137 u
->mLiteral
= aLiteral
;
2138 u
->mType
= Unit::eLiteral
;
2139 uint32_t len
= N
- 1;
2145 void Append(char (&aLiteral
)[N
])
2147 Unit
* u
= AddUnit();
2148 u
->mLiteral
= aLiteral
;
2149 u
->mType
= Unit::eLiteral
;
2150 uint32_t len
= N
- 1;
2155 void Append(const nsAString
& aString
)
2157 Unit
* u
= AddUnit();
2158 u
->mString
= new nsAutoString(aString
);
2159 u
->mType
= Unit::eString
;
2160 uint32_t len
= aString
.Length();
2165 void Append(nsAutoString
* aString
)
2167 Unit
* u
= AddUnit();
2168 u
->mString
= aString
;
2169 u
->mType
= Unit::eString
;
2170 uint32_t len
= aString
->Length();
2175 void AppendWithAttrEncode(nsAutoString
* aString
, uint32_t aLen
)
2177 Unit
* u
= AddUnit();
2178 u
->mString
= aString
;
2179 u
->mType
= Unit::eStringWithEncode
;
2184 void Append(const nsTextFragment
* aTextFragment
)
2186 Unit
* u
= AddUnit();
2187 u
->mTextFragment
= aTextFragment
;
2188 u
->mType
= Unit::eTextFragment
;
2189 uint32_t len
= aTextFragment
->GetLength();
2194 void AppendWithEncode(const nsTextFragment
* aTextFragment
, uint32_t aLen
)
2196 Unit
* u
= AddUnit();
2197 u
->mTextFragment
= aTextFragment
;
2198 u
->mType
= Unit::eTextFragmentWithEncode
;
2203 bool ToString(nsAString
& aOut
)
2205 if (!aOut
.SetCapacity(mLength
, fallible_t())) {
2209 for (StringBuilder
* current
= this; current
; current
= current
->mNext
) {
2210 uint32_t len
= current
->mUnits
.Length();
2211 for (uint32_t i
= 0; i
< len
; ++i
) {
2212 Unit
& u
= current
->mUnits
[i
];
2215 aOut
.Append(nsDependentAtomString(u
.mAtom
));
2218 aOut
.Append(*(u
.mString
));
2220 case Unit::eStringWithEncode
:
2221 EncodeAttrString(*(u
.mString
), aOut
);
2223 case Unit::eLiteral
:
2224 aOut
.AppendASCII(u
.mLiteral
, u
.mLength
);
2226 case Unit::eTextFragment
:
2227 u
.mTextFragment
->AppendTo(aOut
);
2229 case Unit::eTextFragmentWithEncode
:
2230 EncodeTextFragment(u
.mTextFragment
, aOut
);
2233 MOZ_CRASH("Unknown unit type?");
2242 if (mLast
->mUnits
.Length() == STRING_BUFFER_UNITS
) {
2243 new StringBuilder(this);
2245 return mLast
->mUnits
.AppendElement();
2248 explicit StringBuilder(StringBuilder
* aFirst
)
2249 : mLast(nullptr), mLength(0)
2251 MOZ_COUNT_CTOR(StringBuilder
);
2252 aFirst
->mLast
->mNext
= this;
2253 aFirst
->mLast
= this;
2256 void EncodeAttrString(const nsAutoString
& aValue
, nsAString
& aOut
)
2258 const char16_t
* c
= aValue
.BeginReading();
2259 const char16_t
* end
= aValue
.EndReading();
2263 aOut
.AppendLiteral(""");
2266 aOut
.AppendLiteral("&");
2269 aOut
.AppendLiteral(" ");
2279 void EncodeTextFragment(const nsTextFragment
* aValue
, nsAString
& aOut
)
2281 uint32_t len
= aValue
->GetLength();
2282 if (aValue
->Is2b()) {
2283 const char16_t
* data
= aValue
->Get2b();
2284 for (uint32_t i
= 0; i
< len
; ++i
) {
2285 const char16_t c
= data
[i
];
2288 aOut
.AppendLiteral("<");
2291 aOut
.AppendLiteral(">");
2294 aOut
.AppendLiteral("&");
2297 aOut
.AppendLiteral(" ");
2305 const char* data
= aValue
->Get1b();
2306 for (uint32_t i
= 0; i
< len
; ++i
) {
2307 const unsigned char c
= data
[i
];
2310 aOut
.AppendLiteral("<");
2313 aOut
.AppendLiteral(">");
2316 aOut
.AppendLiteral("&");
2319 aOut
.AppendLiteral(" ");
2329 nsAutoTArray
<Unit
, STRING_BUFFER_UNITS
> mUnits
;
2330 nsAutoPtr
<StringBuilder
> mNext
;
2331 StringBuilder
* mLast
;
2332 // mLength is used only in the first StringBuilder object in the linked list.
2336 } // anonymous namespace
2339 AppendEncodedCharacters(const nsTextFragment
* aText
, StringBuilder
& aBuilder
)
2341 uint32_t extraSpaceNeeded
= 0;
2342 uint32_t len
= aText
->GetLength();
2343 if (aText
->Is2b()) {
2344 const char16_t
* data
= aText
->Get2b();
2345 for (uint32_t i
= 0; i
< len
; ++i
) {
2346 const char16_t c
= data
[i
];
2349 extraSpaceNeeded
+= ArrayLength("<") - 2;
2352 extraSpaceNeeded
+= ArrayLength(">") - 2;
2355 extraSpaceNeeded
+= ArrayLength("&") - 2;
2358 extraSpaceNeeded
+= ArrayLength(" ") - 2;
2365 const char* data
= aText
->Get1b();
2366 for (uint32_t i
= 0; i
< len
; ++i
) {
2367 const unsigned char c
= data
[i
];
2370 extraSpaceNeeded
+= ArrayLength("<") - 2;
2373 extraSpaceNeeded
+= ArrayLength(">") - 2;
2376 extraSpaceNeeded
+= ArrayLength("&") - 2;
2379 extraSpaceNeeded
+= ArrayLength(" ") - 2;
2387 if (extraSpaceNeeded
) {
2388 aBuilder
.AppendWithEncode(aText
, len
+ extraSpaceNeeded
);
2390 aBuilder
.Append(aText
);
2395 AppendEncodedAttributeValue(nsAutoString
* aValue
, StringBuilder
& aBuilder
)
2397 const char16_t
* c
= aValue
->BeginReading();
2398 const char16_t
* end
= aValue
->EndReading();
2400 uint32_t extraSpaceNeeded
= 0;
2404 extraSpaceNeeded
+= ArrayLength(""") - 2;
2407 extraSpaceNeeded
+= ArrayLength("&") - 2;
2410 extraSpaceNeeded
+= ArrayLength(" ") - 2;
2418 if (extraSpaceNeeded
) {
2419 aBuilder
.AppendWithAttrEncode(aValue
, aValue
->Length() + extraSpaceNeeded
);
2421 aBuilder
.Append(aValue
);
2426 StartElement(Element
* aContent
, StringBuilder
& aBuilder
)
2428 nsIAtom
* localName
= aContent
->Tag();
2429 int32_t tagNS
= aContent
->GetNameSpaceID();
2431 aBuilder
.Append("<");
2432 if (aContent
->IsHTML() || aContent
->IsSVG() || aContent
->IsMathML()) {
2433 aBuilder
.Append(localName
);
2435 aBuilder
.Append(aContent
->NodeName());
2438 int32_t count
= aContent
->GetAttrCount();
2439 for (int32_t i
= count
; i
> 0;) {
2441 const nsAttrName
* name
= aContent
->GetAttrNameAt(i
);
2442 int32_t attNs
= name
->NamespaceID();
2443 nsIAtom
* attName
= name
->LocalName();
2445 // Filter out any attribute starting with [-|_]moz
2446 nsDependentAtomString
attrNameStr(attName
);
2447 if (StringBeginsWith(attrNameStr
, NS_LITERAL_STRING("_moz")) ||
2448 StringBeginsWith(attrNameStr
, NS_LITERAL_STRING("-moz"))) {
2452 nsAutoString
* attValue
= new nsAutoString();
2453 aContent
->GetAttr(attNs
, attName
, *attValue
);
2455 // Filter out special case of <br type="_moz*"> used by the editor.
2457 if (localName
== nsGkAtoms::br
&& tagNS
== kNameSpaceID_XHTML
&&
2458 attName
== nsGkAtoms::type
&& attNs
== kNameSpaceID_None
&&
2459 StringBeginsWith(*attValue
, NS_LITERAL_STRING("_moz"))) {
2464 aBuilder
.Append(" ");
2466 if (MOZ_LIKELY(attNs
== kNameSpaceID_None
) ||
2467 (attNs
== kNameSpaceID_XMLNS
&&
2468 attName
== nsGkAtoms::xmlns
)) {
2469 // Nothing else required
2470 } else if (attNs
== kNameSpaceID_XML
) {
2471 aBuilder
.Append("xml:");
2472 } else if (attNs
== kNameSpaceID_XMLNS
) {
2473 aBuilder
.Append("xmlns:");
2474 } else if (attNs
== kNameSpaceID_XLink
) {
2475 aBuilder
.Append("xlink:");
2477 nsIAtom
* prefix
= name
->GetPrefix();
2479 aBuilder
.Append(prefix
);
2480 aBuilder
.Append(":");
2484 aBuilder
.Append(attName
);
2485 aBuilder
.Append("=\"");
2486 AppendEncodedAttributeValue(attValue
, aBuilder
);
2487 aBuilder
.Append("\"");
2490 aBuilder
.Append(">");
2493 // Per HTML spec we should append one \n if the first child of
2494 // pre/textarea/listing is a textnode and starts with a \n.
2495 // But because browsers haven't traditionally had that behavior,
2496 // we're not changing our behavior either - yet.
2497 if (aContent->IsHTML()) {
2498 if (localName == nsGkAtoms::pre || localName == nsGkAtoms::textarea ||
2499 localName == nsGkAtoms::listing) {
2500 nsIContent* fc = aContent->GetFirstChild();
2502 (fc->NodeType() == nsIDOMNode::TEXT_NODE ||
2503 fc->NodeType() == nsIDOMNode::CDATA_SECTION_NODE)) {
2504 const nsTextFragment* text = fc->GetText();
2505 if (text && text->GetLength() && text->CharAt(0) == char16_t('\n')) {
2506 aBuilder.Append("\n");
2514 ShouldEscape(nsIContent
* aParent
)
2516 if (!aParent
|| !aParent
->IsHTML()) {
2520 static const nsIAtom
* nonEscapingElements
[] = {
2521 nsGkAtoms::style
, nsGkAtoms::script
, nsGkAtoms::xmp
,
2522 nsGkAtoms::iframe
, nsGkAtoms::noembed
, nsGkAtoms::noframes
,
2523 nsGkAtoms::plaintext
,
2524 // Per the current spec noscript should be escaped in case
2525 // scripts are disabled or if document doesn't have
2526 // browsing context. However the latter seems to be a spec bug
2527 // and Gecko hasn't traditionally done the former.
2530 static mozilla::BloomFilter
<12, nsIAtom
> sFilter
;
2531 static bool sInitialized
= false;
2532 if (!sInitialized
) {
2533 sInitialized
= true;
2534 for (uint32_t i
= 0; i
< ArrayLength(nonEscapingElements
); ++i
) {
2535 sFilter
.add(nonEscapingElements
[i
]);
2539 nsIAtom
* tag
= aParent
->Tag();
2540 if (sFilter
.mightContain(tag
)) {
2541 for (uint32_t i
= 0; i
< ArrayLength(nonEscapingElements
); ++i
) {
2542 if (tag
== nonEscapingElements
[i
]) {
2551 IsVoidTag(Element
* aElement
)
2553 if (!aElement
->IsHTML()) {
2557 static const nsIAtom
* voidElements
[] = {
2558 nsGkAtoms::area
, nsGkAtoms::base
, nsGkAtoms::basefont
,
2559 nsGkAtoms::bgsound
, nsGkAtoms::br
, nsGkAtoms::col
,
2560 nsGkAtoms::command
, nsGkAtoms::embed
, nsGkAtoms::frame
,
2561 nsGkAtoms::hr
, nsGkAtoms::img
, nsGkAtoms::input
,
2562 nsGkAtoms::keygen
, nsGkAtoms::link
, nsGkAtoms::meta
,
2563 nsGkAtoms::param
, nsGkAtoms::source
, nsGkAtoms::track
,
2567 static mozilla::BloomFilter
<12, nsIAtom
> sFilter
;
2568 static bool sInitialized
= false;
2569 if (!sInitialized
) {
2570 sInitialized
= true;
2571 for (uint32_t i
= 0; i
< ArrayLength(voidElements
); ++i
) {
2572 sFilter
.add(voidElements
[i
]);
2576 nsIAtom
* tag
= aElement
->Tag();
2577 if (sFilter
.mightContain(tag
)) {
2578 for (uint32_t i
= 0; i
< ArrayLength(voidElements
); ++i
) {
2579 if (tag
== voidElements
[i
]) {
2588 Serialize(FragmentOrElement
* aRoot
, bool aDescendentsOnly
, nsAString
& aOut
)
2590 nsINode
* current
= aDescendentsOnly
?
2591 nsNodeUtils::GetFirstChildOfTemplateOrNode(aRoot
) : aRoot
;
2597 StringBuilder builder
;
2600 bool isVoid
= false;
2601 switch (current
->NodeType()) {
2602 case nsIDOMNode::ELEMENT_NODE
: {
2603 Element
* elem
= current
->AsElement();
2604 StartElement(elem
, builder
);
2605 isVoid
= IsVoidTag(elem
);
2607 (next
= nsNodeUtils::GetFirstChildOfTemplateOrNode(current
))) {
2614 case nsIDOMNode::TEXT_NODE
:
2615 case nsIDOMNode::CDATA_SECTION_NODE
: {
2616 const nsTextFragment
* text
= static_cast<nsIContent
*>(current
)->GetText();
2617 nsIContent
* parent
= current
->GetParent();
2618 if (ShouldEscape(parent
)) {
2619 AppendEncodedCharacters(text
, builder
);
2621 builder
.Append(text
);
2626 case nsIDOMNode::COMMENT_NODE
: {
2627 builder
.Append("<!--");
2628 builder
.Append(static_cast<nsIContent
*>(current
)->GetText());
2629 builder
.Append("-->");
2633 case nsIDOMNode::DOCUMENT_TYPE_NODE
: {
2634 builder
.Append("<!DOCTYPE ");
2635 builder
.Append(current
->NodeName());
2636 builder
.Append(">");
2640 case nsIDOMNode::PROCESSING_INSTRUCTION_NODE
: {
2641 builder
.Append("<?");
2642 builder
.Append(current
->NodeName());
2643 builder
.Append(" ");
2644 builder
.Append(static_cast<nsIContent
*>(current
)->GetText());
2645 builder
.Append(">");
2651 if (!isVoid
&& current
->NodeType() == nsIDOMNode::ELEMENT_NODE
) {
2652 builder
.Append("</");
2653 nsIContent
* elem
= static_cast<nsIContent
*>(current
);
2654 if (elem
->IsHTML() || elem
->IsSVG() || elem
->IsMathML()) {
2655 builder
.Append(elem
->Tag());
2657 builder
.Append(current
->NodeName());
2659 builder
.Append(">");
2663 if (current
== aRoot
) {
2664 return builder
.ToString(aOut
);
2667 if ((next
= current
->GetNextSibling())) {
2672 current
= current
->GetParentNode();
2674 // Handle template element. If the parent is a template's content,
2675 // then adjust the parent to be the template element.
2676 if (current
!= aRoot
&&
2677 current
->NodeType() == nsIDOMNode::DOCUMENT_FRAGMENT_NODE
) {
2678 DocumentFragment
* frag
= static_cast<DocumentFragment
*>(current
);
2679 nsIContent
* fragHost
= frag
->GetHost();
2680 if (fragHost
&& nsNodeUtils::IsTemplateElement(fragHost
)) {
2685 if (aDescendentsOnly
&& current
== aRoot
) {
2686 return builder
.ToString(aOut
);
2693 FragmentOrElement::GetMarkup(bool aIncludeSelf
, nsAString
& aMarkup
)
2697 nsIDocument
* doc
= OwnerDoc();
2698 if (IsInHTMLDocument()) {
2699 Serialize(this, !aIncludeSelf
, aMarkup
);
2703 nsAutoString contentType
;
2704 doc
->GetContentType(contentType
);
2705 bool tryToCacheEncoder
= !aIncludeSelf
;
2707 nsCOMPtr
<nsIDocumentEncoder
> docEncoder
= doc
->GetCachedEncoder();
2710 do_CreateInstance(PromiseFlatCString(
2711 nsDependentCString(NS_DOC_ENCODER_CONTRACTID_BASE
) +
2712 NS_ConvertUTF16toUTF8(contentType
)
2716 // This could be some type for which we create a synthetic document. Try
2718 contentType
.AssignLiteral("application/xml");
2719 docEncoder
= do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE
"application/xml");
2720 // Don't try to cache the encoder since it would point to a different
2721 // contentType once it has been reinitialized.
2722 tryToCacheEncoder
= false;
2725 NS_ENSURE_TRUE_VOID(docEncoder
);
2727 uint32_t flags
= nsIDocumentEncoder::OutputEncodeBasicEntities
|
2728 // Output DOM-standard newlines
2729 nsIDocumentEncoder::OutputLFLineBreak
|
2730 // Don't do linebreaking that's not present in
2732 nsIDocumentEncoder::OutputRaw
|
2733 // Only check for mozdirty when necessary (bug 599983)
2734 nsIDocumentEncoder::OutputIgnoreMozDirty
;
2737 nsCOMPtr
<Element
> elem
= do_QueryInterface(this);
2738 nsIEditor
* editor
= elem
? elem
->GetEditorInternal() : nullptr;
2739 if (editor
&& editor
->OutputsMozDirty()) {
2740 flags
&= ~nsIDocumentEncoder::OutputIgnoreMozDirty
;
2744 DebugOnly
<nsresult
> rv
= docEncoder
->NativeInit(doc
, contentType
, flags
);
2745 MOZ_ASSERT(NS_SUCCEEDED(rv
));
2748 docEncoder
->SetNativeNode(this);
2750 docEncoder
->SetNativeContainerNode(this);
2752 rv
= docEncoder
->EncodeToString(aMarkup
);
2753 MOZ_ASSERT(NS_SUCCEEDED(rv
));
2754 if (tryToCacheEncoder
) {
2755 doc
->SetCachedEncoder(docEncoder
.forget());
2760 ContainsMarkup(const nsAString
& aStr
)
2762 // Note: we can't use FindCharInSet because null is one of the characters we
2763 // want to search for.
2764 const char16_t
* start
= aStr
.BeginReading();
2765 const char16_t
* end
= aStr
.EndReading();
2767 while (start
!= end
) {
2768 char16_t c
= *start
;
2769 if (c
== char16_t('<') ||
2770 c
== char16_t('&') ||
2771 c
== char16_t('\r') ||
2772 c
== char16_t('\0')) {
2782 FragmentOrElement::SetInnerHTMLInternal(const nsAString
& aInnerHTML
, ErrorResult
& aError
)
2784 FragmentOrElement
* target
= this;
2785 // Handle template case.
2786 if (nsNodeUtils::IsTemplateElement(target
)) {
2787 DocumentFragment
* frag
=
2788 static_cast<HTMLTemplateElement
*>(target
)->Content();
2793 // Fast-path for strings with no markup. Limit this to short strings, to
2794 // avoid ContainsMarkup taking too long. The choice for 100 is based on
2797 // Don't do this for elements with a weird parser insertion mode, for
2798 // instance setting innerHTML = "" on a <html> element should add the
2799 // optional <head> and <body> elements.
2800 if (!target
->HasWeirdParserInsertionMode() &&
2801 aInnerHTML
.Length() < 100 && !ContainsMarkup(aInnerHTML
)) {
2802 aError
= nsContentUtils::SetNodeTextContent(target
, aInnerHTML
, false);
2806 nsIDocument
* doc
= target
->OwnerDoc();
2808 // Batch possible DOMSubtreeModified events.
2809 mozAutoSubtreeModified
subtree(doc
, nullptr);
2811 target
->FireNodeRemovedForChildren();
2813 // Needed when innerHTML is used in combination with contenteditable
2814 mozAutoDocUpdate
updateBatch(doc
, UPDATE_CONTENT_MODEL
, true);
2816 // Remove childnodes.
2817 uint32_t childCount
= target
->GetChildCount();
2818 nsAutoMutationBatch
mb(target
, true, false);
2819 for (uint32_t i
= 0; i
< childCount
; ++i
) {
2820 target
->RemoveChildAt(0, true);
2824 nsAutoScriptLoaderDisabler
sld(doc
);
2826 nsIAtom
* contextLocalName
= Tag();
2827 int32_t contextNameSpaceID
= GetNameSpaceID();
2829 ShadowRoot
* shadowRoot
= ShadowRoot::FromNode(this);
2831 // Fix up the context to be the host of the ShadowRoot.
2832 contextLocalName
= shadowRoot
->GetHost()->Tag();
2833 contextNameSpaceID
= shadowRoot
->GetHost()->GetNameSpaceID();
2836 if (doc
->IsHTML()) {
2837 int32_t oldChildCount
= target
->GetChildCount();
2838 aError
= nsContentUtils::ParseFragmentHTML(aInnerHTML
,
2842 doc
->GetCompatibilityMode() ==
2843 eCompatibility_NavQuirks
,
2846 // HTML5 parser has notified, but not fired mutation events.
2847 nsContentUtils::FireMutationEventsForDirectParsing(doc
, target
,
2850 nsRefPtr
<DocumentFragment
> df
=
2851 nsContentUtils::CreateContextualFragment(target
, aInnerHTML
, true, aError
);
2852 if (!aError
.Failed()) {
2853 // Suppress assertion about node removal mutation events that can't have
2854 // listeners anyway, because no one has had the chance to register mutation
2855 // listeners on the fragment that comes from the parser.
2856 nsAutoScriptBlockerSuppressNodeRemoved scriptBlocker
;
2858 static_cast<nsINode
*>(target
)->AppendChild(*df
, aError
);
2865 FragmentOrElement::CreateSlots()
2867 return new nsDOMSlots();
2871 FragmentOrElement::FireNodeRemovedForChildren()
2873 nsIDocument
* doc
= OwnerDoc();
2874 // Optimize the common case
2875 if (!nsContentUtils::
2876 HasMutationListeners(doc
, NS_EVENT_BITS_MUTATION_NODEREMOVED
)) {
2880 nsCOMPtr
<nsIDocument
> owningDoc
= doc
;
2882 nsCOMPtr
<nsINode
> child
;
2883 for (child
= GetFirstChild();
2884 child
&& child
->GetParentNode() == this;
2885 child
= child
->GetNextSibling()) {
2886 nsContentUtils::MaybeFireNodeRemoved(child
, this, doc
);
2891 FragmentOrElement::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf
) const
2894 n
+= nsIContent::SizeOfExcludingThis(aMallocSizeOf
);
2895 n
+= mAttrsAndChildren
.SizeOfExcludingThis(aMallocSizeOf
);
2897 nsDOMSlots
* slots
= GetExistingDOMSlots();
2899 n
+= slots
->SizeOfIncludingThis(aMallocSizeOf
);
2906 FragmentOrElement::SetIsElementInStyleScopeFlagOnSubtree(bool aInStyleScope
)
2908 if (aInStyleScope
&& IsElementInStyleScope()) {
2913 SetIsElementInStyleScope(aInStyleScope
);
2914 SetIsElementInStyleScopeFlagOnShadowTree(aInStyleScope
);
2917 nsIContent
* n
= GetNextNode(this);
2919 if (n
->IsElementInStyleScope()) {
2920 n
= n
->GetNextNonChildNode(this);
2922 if (n
->IsElement()) {
2923 n
->SetIsElementInStyleScope(aInStyleScope
);
2924 n
->AsElement()->SetIsElementInStyleScopeFlagOnShadowTree(aInStyleScope
);
2926 n
= n
->GetNextNode(this);
2932 FragmentOrElement::SetIsElementInStyleScopeFlagOnShadowTree(bool aInStyleScope
)
2934 NS_ASSERTION(IsElement(), "calling SetIsElementInStyleScopeFlagOnShadowTree "
2935 "on a non-Element is useless");
2936 ShadowRoot
* shadowRoot
= GetShadowRoot();
2937 while (shadowRoot
) {
2938 shadowRoot
->SetIsElementInStyleScopeFlagOnSubtree(aInStyleScope
);
2939 shadowRoot
= shadowRoot
->GetOlderShadowRoot();