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/. */
8 * Base class for all DOM nodes.
13 #include "AccessCheck.h"
15 #include "mozAutoDocUpdate.h"
16 #include "mozilla/AsyncEventDispatcher.h"
17 #include "mozilla/CORSMode.h"
18 #include "mozilla/EventDispatcher.h"
19 #include "mozilla/EventListenerManager.h"
20 #include "mozilla/InternalMutationEvent.h"
21 #include "mozilla/Likely.h"
22 #include "mozilla/MemoryReporting.h"
23 #include "mozilla/Telemetry.h"
24 #include "mozilla/TimeStamp.h"
25 #include "mozilla/dom/Element.h"
26 #include "mozilla/dom/Event.h"
27 #include "mozilla/dom/ShadowRoot.h"
28 #include "nsAttrValueOrString.h"
29 #include "nsBindingManager.h"
30 #include "nsCCUncollectableMarker.h"
31 #include "nsContentCreatorFunctions.h"
32 #include "nsContentList.h"
33 #include "nsContentUtils.h"
34 #include "nsCycleCollectionParticipant.h"
35 #include "nsDocument.h"
36 #include "mozilla/dom/Attr.h"
37 #include "nsDOMAttributeMap.h"
39 #include "nsDOMCSSAttrDeclaration.h"
41 #include "nsDOMMutationObserver.h"
42 #include "nsDOMString.h"
43 #include "nsDOMTokenList.h"
44 #include "nsFocusManager.h"
45 #include "nsFrameSelection.h"
46 #include "nsGenericHTMLElement.h"
47 #include "nsGkAtoms.h"
48 #include "nsIAnonymousContentCreator.h"
50 #include "nsIBaseWindow.h"
51 #include "nsICategoryManager.h"
52 #include "nsIContentIterator.h"
53 #include "nsIControllers.h"
54 #include "nsIDocument.h"
55 #include "nsIDOMDocument.h"
56 #include "nsIDOMDocumentType.h"
57 #include "nsIDOMEvent.h"
58 #include "nsIDOMEventListener.h"
59 #include "nsIDOMMutationEvent.h"
60 #include "nsIDOMNodeList.h"
61 #include "nsIEditor.h"
62 #include "nsIEditorIMESupport.h"
63 #include "nsILinkHandler.h"
64 #include "mozilla/dom/NodeInfo.h"
65 #include "mozilla/dom/NodeInfoInlines.h"
66 #include "nsIPresShell.h"
67 #include "nsIScriptError.h"
68 #include "nsIScriptGlobalObject.h"
69 #include "nsIScriptSecurityManager.h"
70 #include "nsIScrollableFrame.h"
71 #include "nsIServiceManager.h"
74 #include "nsViewManager.h"
75 #include "nsIWebNavigation.h"
76 #include "nsIWidget.h"
77 #include "nsLayoutUtils.h"
78 #include "nsNameSpaceManager.h"
79 #include "nsNetUtil.h"
80 #include "nsNodeInfoManager.h"
81 #include "nsNodeUtils.h"
82 #include "nsPIBoxObject.h"
83 #include "nsPIDOMWindow.h"
84 #include "nsPresContext.h"
85 #include "nsRuleProcessorData.h"
87 #include "nsStyleConsts.h"
88 #include "nsSVGFeatures.h"
89 #include "nsSVGUtils.h"
90 #include "nsTextNode.h"
91 #include "nsUnicharUtils.h"
92 #include "nsXBLBinding.h"
93 #include "nsXBLPrototypeBinding.h"
94 #include "mozilla/Preferences.h"
96 #include "xpcpublic.h"
97 #include "nsCSSRuleProcessor.h"
98 #include "nsCSSParser.h"
99 #include "HTMLLegendElement.h"
100 #include "nsWrapperCacheInlines.h"
101 #include "WrapperFactory.h"
102 #include "DocumentType.h"
104 #include "nsGlobalWindow.h"
105 #include "nsDOMMutationObserver.h"
106 #include "GeometryUtils.h"
108 using namespace mozilla
;
109 using namespace mozilla::dom
;
111 nsINode::nsSlots::~nsSlots()
114 mChildNodes
->DropReference();
115 NS_RELEASE(mChildNodes
);
118 if (mWeakReference
) {
119 mWeakReference
->NoticeNodeDestruction();
124 nsINode::nsSlots::Traverse(nsCycleCollectionTraversalCallback
&cb
)
126 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mSlots->mChildNodes");
127 cb
.NoteXPCOMChild(mChildNodes
);
131 nsINode::nsSlots::Unlink()
134 mChildNodes
->DropReference();
135 NS_RELEASE(mChildNodes
);
139 //----------------------------------------------------------------------
143 MOZ_ASSERT(!HasSlots(), "nsNodeUtils::LastRelease was not called?");
144 MOZ_ASSERT(mSubtreeRoot
== this, "Didn't restore state properly?");
148 nsINode::GetProperty(uint16_t aCategory
, nsIAtom
*aPropertyName
,
149 nsresult
*aStatus
) const
151 return OwnerDoc()->PropertyTable(aCategory
)->GetProperty(this, aPropertyName
,
156 nsINode::SetProperty(uint16_t aCategory
, nsIAtom
*aPropertyName
, void *aValue
,
157 NSPropertyDtorFunc aDtor
, bool aTransfer
,
160 nsresult rv
= OwnerDoc()->PropertyTable(aCategory
)->SetProperty(this,
166 if (NS_SUCCEEDED(rv
)) {
167 SetFlags(NODE_HAS_PROPERTIES
);
174 nsINode::DeleteProperty(uint16_t aCategory
, nsIAtom
*aPropertyName
)
176 OwnerDoc()->PropertyTable(aCategory
)->DeleteProperty(this, aPropertyName
);
180 nsINode::UnsetProperty(uint16_t aCategory
, nsIAtom
*aPropertyName
,
183 return OwnerDoc()->PropertyTable(aCategory
)->UnsetProperty(this,
189 nsINode::CreateSlots()
191 return new nsSlots();
195 nsINode::IsEditableInternal() const
197 if (HasFlag(NODE_IS_EDITABLE
)) {
198 // The node is in an editable contentEditable subtree.
202 nsIDocument
*doc
= GetUncomposedDoc();
204 // Check if the node is in a document and the document is in designMode.
205 return doc
&& doc
->HasFlag(NODE_IS_EDITABLE
);
208 static nsIContent
* GetEditorRootContent(nsIEditor
* aEditor
)
210 nsCOMPtr
<nsIDOMElement
> rootElement
;
211 aEditor
->GetRootElement(getter_AddRefs(rootElement
));
212 nsCOMPtr
<nsIContent
> rootContent(do_QueryInterface(rootElement
));
217 nsINode::GetTextEditorRootContent(nsIEditor
** aEditor
)
221 for (nsINode
* node
= this; node
; node
= node
->GetParentNode()) {
222 if (!node
->IsElement() ||
223 !node
->AsElement()->IsHTML())
226 nsCOMPtr
<nsIEditor
> editor
=
227 static_cast<nsGenericHTMLElement
*>(node
)->GetEditorInternal();
231 nsIContent
* rootContent
= GetEditorRootContent(editor
);
233 editor
.swap(*aEditor
);
240 nsINode::SubtreeRoot() const
242 // There are four cases of interest here. nsINodes that are really:
243 // 1. nsIDocument nodes - Are always in the document.
244 // 2.a nsIContent nodes not in a shadow tree - Are either in the document,
245 // or mSubtreeRoot is updated in BindToTree/UnbindFromTree.
246 // 2.b nsIContent nodes in a shadow tree - Are never in the document,
247 // ignore mSubtreeRoot and return the containing shadow root.
248 // 4. nsIAttribute nodes - Are never in the document, and mSubtreeRoot
249 // is always 'this' (as set in nsINode's ctor).
252 node
= OwnerDocAsNode();
253 } else if (IsContent()) {
254 ShadowRoot
* containingShadow
= AsContent()->GetContainingShadow();
255 node
= containingShadow
? containingShadow
: mSubtreeRoot
;
259 NS_ASSERTION(node
, "Should always have a node here!");
262 const nsINode
* slowNode
= this;
263 const nsINode
* iter
= slowNode
;
264 while ((iter
= iter
->GetParentNode())) {
268 NS_ASSERTION(slowNode
== node
, "These should always be in sync!");
274 static nsIContent
* GetRootForContentSubtree(nsIContent
* aContent
)
276 NS_ENSURE_TRUE(aContent
, nullptr);
278 // Special case for ShadowRoot because the ShadowRoot itself is
279 // the root. This is necessary to prevent selection from crossing
280 // the ShadowRoot boundary.
281 ShadowRoot
* containingShadow
= aContent
->GetContainingShadow();
282 if (containingShadow
) {
283 return containingShadow
;
286 nsIContent
* stop
= aContent
->GetBindingParent();
288 nsIContent
* parent
= aContent
->GetParent();
289 if (parent
== stop
) {
298 nsINode::GetSelectionRootContent(nsIPresShell
* aPresShell
)
300 NS_ENSURE_TRUE(aPresShell
, nullptr);
302 if (IsNodeOfType(eDOCUMENT
))
303 return static_cast<nsIDocument
*>(this)->GetRootElement();
304 if (!IsNodeOfType(eCONTENT
))
307 if (GetCrossShadowCurrentDoc() != aPresShell
->GetDocument()) {
311 if (static_cast<nsIContent
*>(this)->HasIndependentSelection()) {
312 // This node should be a descendant of input/textarea editor.
313 nsIContent
* content
= GetTextEditorRootContent();
318 nsPresContext
* presContext
= aPresShell
->GetPresContext();
320 nsIEditor
* editor
= nsContentUtils::GetHTMLEditor(presContext
);
322 // This node is in HTML editor.
323 nsIDocument
* doc
= GetCrossShadowCurrentDoc();
324 if (!doc
|| doc
->HasFlag(NODE_IS_EDITABLE
) ||
325 !HasFlag(NODE_IS_EDITABLE
)) {
326 nsIContent
* editorRoot
= GetEditorRootContent(editor
);
327 NS_ENSURE_TRUE(editorRoot
, nullptr);
328 return nsContentUtils::IsInSameAnonymousTree(this, editorRoot
) ?
330 GetRootForContentSubtree(static_cast<nsIContent
*>(this));
332 // If the document isn't editable but this is editable, this is in
333 // contenteditable. Use the editing host element for selection root.
334 return static_cast<nsIContent
*>(this)->GetEditingHost();
338 nsRefPtr
<nsFrameSelection
> fs
= aPresShell
->FrameSelection();
339 nsIContent
* content
= fs
->GetLimiter();
341 content
= fs
->GetAncestorLimiter();
343 nsIDocument
* doc
= aPresShell
->GetDocument();
344 NS_ENSURE_TRUE(doc
, nullptr);
345 content
= doc
->GetRootElement();
351 // This node might be in another subtree, if so, we should find this subtree's
352 // root. Otherwise, we can return the content simply.
353 NS_ENSURE_TRUE(content
, nullptr);
354 if (!nsContentUtils::IsInSameAnonymousTree(this, content
)) {
355 content
= GetRootForContentSubtree(static_cast<nsIContent
*>(this));
356 // Fixup for ShadowRoot because the ShadowRoot itself does not have a frame.
357 // Use the host as the root.
358 ShadowRoot
* shadowRoot
= ShadowRoot::FromNode(content
);
360 content
= shadowRoot
->GetHost();
368 nsINode::ChildNodes()
370 nsSlots
* slots
= Slots();
371 if (!slots
->mChildNodes
) {
372 slots
->mChildNodes
= new nsChildContentList(this);
373 if (slots
->mChildNodes
) {
374 NS_ADDREF(slots
->mChildNodes
);
378 return slots
->mChildNodes
;
382 nsINode::GetTextContentInternal(nsAString
& aTextContent
, ErrorResult
& aError
)
384 SetDOMStringToNull(aTextContent
);
388 nsINode::GetComposedDocInternal() const
390 MOZ_ASSERT(HasFlag(NODE_IS_IN_SHADOW_TREE
) && IsContent(),
391 "Should only be caled on nodes in the shadow tree.");
393 // Cross ShadowRoot boundary.
394 ShadowRoot
* containingShadow
= AsContent()->GetContainingShadow();
396 nsIContent
* poolHost
= containingShadow
->GetPoolHost();
398 // This node is in an older shadow root that does not get projected into
399 // an insertion point, thus this node can not be in the composed document.
403 return poolHost
->GetComposedDoc();
408 nsINode::CheckNotNativeAnonymous() const
410 if (!IsNodeOfType(eCONTENT
))
412 nsIContent
* content
= static_cast<const nsIContent
*>(this)->GetBindingParent();
414 if (content
->IsRootOfNativeAnonymousSubtree()) {
415 NS_ERROR("Element not marked to be in native anonymous subtree!");
418 content
= content
->GetBindingParent();
424 nsINode::IsInAnonymousSubtree() const
430 return AsContent()->IsInAnonymousSubtree();
434 nsINode::IsAnonymousContentInSVGUseSubtree() const
436 MOZ_ASSERT(IsInAnonymousSubtree());
437 nsIContent
* parent
= AsContent()->GetBindingParent();
438 // Watch out for parentless native-anonymous subtrees.
439 return parent
&& parent
->IsSVG(nsGkAtoms::use
);
443 nsINode::GetParentNode(nsIDOMNode
** aParentNode
)
445 *aParentNode
= nullptr;
447 nsINode
*parent
= GetParentNode();
449 return parent
? CallQueryInterface(parent
, aParentNode
) : NS_OK
;
453 nsINode::GetParentElement(nsIDOMElement
** aParentElement
)
455 *aParentElement
= nullptr;
456 nsINode
* parent
= GetParentElement();
457 return parent
? CallQueryInterface(parent
, aParentElement
) : NS_OK
;
461 nsINode::GetChildNodes(nsIDOMNodeList
** aChildNodes
)
463 NS_ADDREF(*aChildNodes
= ChildNodes());
469 nsINode::GetFirstChild(nsIDOMNode
** aNode
)
471 nsIContent
* child
= GetFirstChild();
473 return CallQueryInterface(child
, aNode
);
482 nsINode::GetLastChild(nsIDOMNode
** aNode
)
484 nsIContent
* child
= GetLastChild();
486 return CallQueryInterface(child
, aNode
);
495 nsINode::GetPreviousSibling(nsIDOMNode
** aPrevSibling
)
497 *aPrevSibling
= nullptr;
499 nsIContent
*sibling
= GetPreviousSibling();
501 return sibling
? CallQueryInterface(sibling
, aPrevSibling
) : NS_OK
;
505 nsINode::GetNextSibling(nsIDOMNode
** aNextSibling
)
507 *aNextSibling
= nullptr;
509 nsIContent
*sibling
= GetNextSibling();
511 return sibling
? CallQueryInterface(sibling
, aNextSibling
) : NS_OK
;
515 nsINode::GetOwnerDocument(nsIDOMDocument
** aOwnerDocument
)
517 *aOwnerDocument
= nullptr;
519 nsIDocument
*ownerDoc
= GetOwnerDocument();
521 return ownerDoc
? CallQueryInterface(ownerDoc
, aOwnerDocument
) : NS_OK
;
525 nsINode::GetNodeValueInternal(nsAString
& aNodeValue
)
527 SetDOMStringToNull(aNodeValue
);
531 nsINode::RemoveChild(nsINode
& aOldChild
, ErrorResult
& aError
)
533 if (IsNodeOfType(eDATA_NODE
)) {
534 // aOldChild can't be one of our children.
535 aError
.Throw(NS_ERROR_DOM_NOT_FOUND_ERR
);
539 if (aOldChild
.GetParentNode() == this) {
540 nsContentUtils::MaybeFireNodeRemoved(&aOldChild
, this, OwnerDoc());
543 int32_t index
= IndexOf(&aOldChild
);
545 // aOldChild isn't one of our children.
546 aError
.Throw(NS_ERROR_DOM_NOT_FOUND_ERR
);
550 RemoveChildAt(index
, true);
555 nsINode::RemoveChild(nsIDOMNode
* aOldChild
, nsIDOMNode
** aReturn
)
557 nsCOMPtr
<nsINode
> oldChild
= do_QueryInterface(aOldChild
);
559 return NS_ERROR_NULL_POINTER
;
563 RemoveChild(*oldChild
, rv
);
565 NS_ADDREF(*aReturn
= aOldChild
);
567 return rv
.ErrorCode();
573 // First collect list of nodes to be removed
574 nsAutoTArray
<nsCOMPtr
<nsIContent
>, 50> nodes
;
576 bool canMerge
= false;
577 for (nsIContent
* node
= this->GetFirstChild();
579 node
= node
->GetNextNode(this)) {
580 if (node
->NodeType() != nsIDOMNode::TEXT_NODE
) {
585 if (canMerge
|| node
->TextLength() == 0) {
586 // No need to touch canMerge. That way we can merge across empty
587 // textnodes if and only if the node before is a textnode
588 nodes
.AppendElement(node
);
594 // If there's no following sibling, then we need to ensure that we don't
595 // collect following siblings of our (grand)parent as to-be-removed
596 canMerge
= canMerge
&& !!node
->GetNextSibling();
599 if (nodes
.IsEmpty()) {
603 // We're relying on mozAutoSubtreeModified to keep the doc alive here.
604 nsIDocument
* doc
= OwnerDoc();
606 // Batch possible DOMSubtreeModified events.
607 mozAutoSubtreeModified
subtree(doc
, nullptr);
609 // Fire all DOMNodeRemoved events. Optimize the common case of there being
611 bool hasRemoveListeners
= nsContentUtils::
612 HasMutationListeners(doc
, NS_EVENT_BITS_MUTATION_NODEREMOVED
);
613 if (hasRemoveListeners
) {
614 for (uint32_t i
= 0; i
< nodes
.Length(); ++i
) {
615 nsINode
* parentNode
= nodes
[i
]->GetParentNode();
616 if (parentNode
) { // Node may have already been removed.
617 nsContentUtils::MaybeFireNodeRemoved(nodes
[i
], parentNode
,
623 mozAutoDocUpdate
batch(doc
, UPDATE_CONTENT_MODEL
, true);
625 // Merge and remove all nodes
627 for (uint32_t i
= 0; i
< nodes
.Length(); ++i
) {
628 nsIContent
* node
= nodes
[i
];
629 // Merge with previous node unless empty
630 const nsTextFragment
* text
= node
->GetText();
631 if (text
->GetLength()) {
632 nsIContent
* target
= node
->GetPreviousSibling();
633 NS_ASSERTION((target
&& target
->NodeType() == nsIDOMNode::TEXT_NODE
) ||
635 "Should always have a previous text sibling unless "
636 "mutation events messed us up");
637 if (!hasRemoveListeners
||
638 (target
&& target
->NodeType() == nsIDOMNode::TEXT_NODE
)) {
639 nsTextNode
* t
= static_cast<nsTextNode
*>(target
);
641 t
->AppendTextForNormalize(text
->Get2b(), text
->GetLength(), true, node
);
645 text
->AppendTo(tmpStr
);
646 t
->AppendTextForNormalize(tmpStr
.get(), tmpStr
.Length(), true, node
);
652 nsCOMPtr
<nsINode
> parent
= node
->GetParentNode();
653 NS_ASSERTION(parent
|| hasRemoveListeners
,
654 "Should always have a parent unless "
655 "mutation events messed us up");
657 parent
->RemoveChildAt(parent
->IndexOf(node
), true);
663 nsINode::GetBaseURI(nsAString
&aURI
) const
665 nsCOMPtr
<nsIURI
> baseURI
= GetBaseURI();
669 baseURI
->GetSpec(spec
);
672 CopyUTF8toUTF16(spec
, aURI
);
676 nsINode::GetBaseURIFromJS(nsAString
& aURI
) const
678 nsCOMPtr
<nsIURI
> baseURI
= GetBaseURI(nsContentUtils::IsCallerChrome());
681 baseURI
->GetSpec(spec
);
683 CopyUTF8toUTF16(spec
, aURI
);
686 already_AddRefed
<nsIURI
>
687 nsINode::GetBaseURIObject() const
689 return GetBaseURI(true);
693 nsINode::LookupPrefix(const nsAString
& aNamespaceURI
, nsAString
& aPrefix
)
695 Element
*element
= GetNameSpaceElement();
697 // XXX Waiting for DOM spec to list error codes.
699 // Trace up the content parent chain looking for the namespace
700 // declaration that defines the aNamespaceURI namespace. Once found,
701 // return the prefix (i.e. the attribute localName).
702 for (nsIContent
* content
= element
; content
;
703 content
= content
->GetParent()) {
704 uint32_t attrCount
= content
->GetAttrCount();
706 for (uint32_t i
= 0; i
< attrCount
; ++i
) {
707 const nsAttrName
* name
= content
->GetAttrNameAt(i
);
709 if (name
->NamespaceEquals(kNameSpaceID_XMLNS
) &&
710 content
->AttrValueIs(kNameSpaceID_XMLNS
, name
->LocalName(),
711 aNamespaceURI
, eCaseMatters
)) {
712 // If the localName is "xmlns", the prefix we output should be
714 nsIAtom
*localName
= name
->LocalName();
716 if (localName
!= nsGkAtoms::xmlns
) {
717 localName
->ToString(aPrefix
);
720 SetDOMStringToNull(aPrefix
);
728 SetDOMStringToNull(aPrefix
);
732 SetUserDataProperty(uint16_t aCategory
, nsINode
*aNode
, nsIAtom
*aKey
,
733 nsISupports
* aValue
, void** aOldValue
)
735 nsresult rv
= aNode
->SetProperty(aCategory
, aKey
, aValue
,
736 nsPropertyTable::SupportsDtorFunc
, true,
738 NS_ENSURE_SUCCESS(rv
, rv
);
740 // Property table owns it now.
747 nsINode::SetUserData(const nsAString
&aKey
, nsIVariant
*aData
, nsIVariant
**aResult
)
749 OwnerDoc()->WarnOnceAbout(nsIDocument::eGetSetUserData
);
752 nsCOMPtr
<nsIAtom
> key
= do_GetAtom(aKey
);
754 return NS_ERROR_OUT_OF_MEMORY
;
760 rv
= SetUserDataProperty(DOM_USER_DATA
, this, key
, aData
, &data
);
761 NS_ENSURE_SUCCESS(rv
, rv
);
764 data
= UnsetProperty(DOM_USER_DATA
, key
);
767 // Take over ownership of the old data from the property table.
768 nsCOMPtr
<nsIVariant
> oldData
= dont_AddRef(static_cast<nsIVariant
*>(data
));
769 oldData
.swap(*aResult
);
774 nsINode::SetUserData(JSContext
* aCx
, const nsAString
& aKey
,
775 JS::Handle
<JS::Value
> aData
,
776 JS::MutableHandle
<JS::Value
> aRetval
,
779 nsCOMPtr
<nsIVariant
> data
;
780 aError
= nsContentUtils::XPConnect()->JSValToVariant(aCx
, aData
, getter_AddRefs(data
));
781 if (aError
.Failed()) {
785 nsCOMPtr
<nsIVariant
> oldData
;
786 aError
= SetUserData(aKey
, data
, getter_AddRefs(oldData
));
787 if (aError
.Failed()) {
796 JSAutoCompartment
ac(aCx
, GetWrapper());
797 aError
= nsContentUtils::XPConnect()->VariantToJS(aCx
, GetWrapper(), oldData
,
802 nsINode::GetUserData(const nsAString
& aKey
)
804 OwnerDoc()->WarnOnceAbout(nsIDocument::eGetSetUserData
);
805 nsCOMPtr
<nsIAtom
> key
= do_GetAtom(aKey
);
810 return static_cast<nsIVariant
*>(GetProperty(DOM_USER_DATA
, key
));
814 nsINode::GetUserData(JSContext
* aCx
, const nsAString
& aKey
,
815 JS::MutableHandle
<JS::Value
> aRetval
, ErrorResult
& aError
)
817 nsIVariant
* data
= GetUserData(aKey
);
823 JSAutoCompartment
ac(aCx
, GetWrapper());
824 aError
= nsContentUtils::XPConnect()->VariantToJS(aCx
, GetWrapper(), data
,
829 nsINode::CompareDocumentPosition(nsINode
& aOtherNode
) const
831 if (this == &aOtherNode
) {
834 if (GetPreviousSibling() == &aOtherNode
) {
835 MOZ_ASSERT(GetParentNode() == aOtherNode
.GetParentNode());
836 return static_cast<uint16_t>(nsIDOMNode::DOCUMENT_POSITION_PRECEDING
);
838 if (GetNextSibling() == &aOtherNode
) {
839 MOZ_ASSERT(GetParentNode() == aOtherNode
.GetParentNode());
840 return static_cast<uint16_t>(nsIDOMNode::DOCUMENT_POSITION_FOLLOWING
);
843 nsAutoTArray
<const nsINode
*, 32> parents1
, parents2
;
845 const nsINode
*node1
= &aOtherNode
, *node2
= this;
847 // Check if either node is an attribute
848 const Attr
* attr1
= nullptr;
849 if (node1
->IsNodeOfType(nsINode::eATTRIBUTE
)) {
850 attr1
= static_cast<const Attr
*>(node1
);
851 const Element
* elem
= attr1
->GetElement();
852 // If there is an owner element add the attribute
853 // to the chain and walk up to the element
856 parents1
.AppendElement(attr1
);
859 if (node2
->IsNodeOfType(nsINode::eATTRIBUTE
)) {
860 const Attr
* attr2
= static_cast<const Attr
*>(node2
);
861 const Element
* elem
= attr2
->GetElement();
862 if (elem
== node1
&& attr1
) {
863 // Both nodes are attributes on the same element.
864 // Compare position between the attributes.
867 const nsAttrName
* attrName
;
868 for (i
= 0; (attrName
= elem
->GetAttrNameAt(i
)); ++i
) {
869 if (attrName
->Equals(attr1
->NodeInfo())) {
870 NS_ASSERTION(!attrName
->Equals(attr2
->NodeInfo()),
871 "Different attrs at same position");
872 return nsIDOMNode::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
|
873 nsIDOMNode::DOCUMENT_POSITION_PRECEDING
;
875 if (attrName
->Equals(attr2
->NodeInfo())) {
876 return nsIDOMNode::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
|
877 nsIDOMNode::DOCUMENT_POSITION_FOLLOWING
;
880 NS_NOTREACHED("neither attribute in the element");
881 return nsIDOMNode::DOCUMENT_POSITION_DISCONNECTED
;
886 parents2
.AppendElement(attr2
);
890 // We now know that both nodes are either nsIContents or nsIDocuments.
891 // If either node started out as an attribute, that attribute will have
892 // the same relative position as its ownerElement, except if the
893 // ownerElement ends up being the container for the other node
895 // Build the chain of parents
897 parents1
.AppendElement(node1
);
898 node1
= node1
->GetParentNode();
901 parents2
.AppendElement(node2
);
902 node2
= node2
->GetParentNode();
905 // Check if the nodes are disconnected.
906 uint32_t pos1
= parents1
.Length();
907 uint32_t pos2
= parents2
.Length();
908 const nsINode
* top1
= parents1
.ElementAt(--pos1
);
909 const nsINode
* top2
= parents2
.ElementAt(--pos2
);
912 (nsIDOMNode::DOCUMENT_POSITION_PRECEDING
|
913 nsIDOMNode::DOCUMENT_POSITION_DISCONNECTED
|
914 nsIDOMNode::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
) :
915 (nsIDOMNode::DOCUMENT_POSITION_FOLLOWING
|
916 nsIDOMNode::DOCUMENT_POSITION_DISCONNECTED
|
917 nsIDOMNode::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
);
920 // Find where the parent chain differs and check indices in the parent.
921 const nsINode
* parent
= top1
;
923 for (len
= std::min(pos1
, pos2
); len
> 0; --len
) {
924 const nsINode
* child1
= parents1
.ElementAt(--pos1
);
925 const nsINode
* child2
= parents2
.ElementAt(--pos2
);
926 if (child1
!= child2
) {
927 // child1 or child2 can be an attribute here. This will work fine since
928 // IndexOf will return -1 for the attribute making the attribute be
929 // considered before any child.
930 return parent
->IndexOf(child1
) < parent
->IndexOf(child2
) ?
931 static_cast<uint16_t>(nsIDOMNode::DOCUMENT_POSITION_PRECEDING
) :
932 static_cast<uint16_t>(nsIDOMNode::DOCUMENT_POSITION_FOLLOWING
);
937 // We hit the end of one of the parent chains without finding a difference
938 // between the chains. That must mean that one node is an ancestor of the
939 // other. The one with the shortest chain must be the ancestor.
941 (nsIDOMNode::DOCUMENT_POSITION_PRECEDING
|
942 nsIDOMNode::DOCUMENT_POSITION_CONTAINS
) :
943 (nsIDOMNode::DOCUMENT_POSITION_FOLLOWING
|
944 nsIDOMNode::DOCUMENT_POSITION_CONTAINED_BY
);
948 nsINode::IsEqualNode(nsINode
* aOther
)
954 nsAutoString string1
, string2
;
956 nsINode
* node1
= this;
957 nsINode
* node2
= aOther
;
959 uint16_t nodeType
= node1
->NodeType();
960 if (nodeType
!= node2
->NodeType()) {
964 mozilla::dom::NodeInfo
* nodeInfo1
= node1
->mNodeInfo
;
965 mozilla::dom::NodeInfo
* nodeInfo2
= node2
->mNodeInfo
;
966 if (!nodeInfo1
->Equals(nodeInfo2
) ||
967 nodeInfo1
->GetExtraName() != nodeInfo2
->GetExtraName()) {
972 case nsIDOMNode::ELEMENT_NODE
:
974 // Both are elements (we checked that their nodeinfos are equal). Do the
975 // check on attributes.
976 Element
* element1
= node1
->AsElement();
977 Element
* element2
= node2
->AsElement();
978 uint32_t attrCount
= element1
->GetAttrCount();
979 if (attrCount
!= element2
->GetAttrCount()) {
983 // Iterate over attributes.
984 for (uint32_t i
= 0; i
< attrCount
; ++i
) {
985 const nsAttrName
* attrName
= element1
->GetAttrNameAt(i
);
989 element1
->GetAttr(attrName
->NamespaceID(), attrName
->LocalName(),
991 NS_ASSERTION(hasAttr
, "Why don't we have an attr?");
993 if (!element2
->AttrValueIs(attrName
->NamespaceID(),
994 attrName
->LocalName(),
1002 case nsIDOMNode::TEXT_NODE
:
1003 case nsIDOMNode::COMMENT_NODE
:
1004 case nsIDOMNode::CDATA_SECTION_NODE
:
1005 case nsIDOMNode::PROCESSING_INSTRUCTION_NODE
:
1008 static_cast<nsIContent
*>(node1
)->AppendTextTo(string1
);
1010 static_cast<nsIContent
*>(node2
)->AppendTextTo(string2
);
1012 if (!string1
.Equals(string2
)) {
1018 case nsIDOMNode::DOCUMENT_NODE
:
1019 case nsIDOMNode::DOCUMENT_FRAGMENT_NODE
:
1021 case nsIDOMNode::ATTRIBUTE_NODE
:
1023 NS_ASSERTION(node1
== this && node2
== aOther
,
1024 "Did we come upon an attribute node while walking a "
1026 node1
->GetNodeValue(string1
);
1027 node2
->GetNodeValue(string2
);
1029 // Returning here as to not bother walking subtree. And there is no
1030 // risk that we're half way through walking some other subtree since
1031 // attribute nodes doesn't appear in subtrees.
1032 return string1
.Equals(string2
);
1034 case nsIDOMNode::DOCUMENT_TYPE_NODE
:
1036 nsCOMPtr
<nsIDOMDocumentType
> docType1
= do_QueryInterface(node1
);
1037 nsCOMPtr
<nsIDOMDocumentType
> docType2
= do_QueryInterface(node2
);
1039 NS_ASSERTION(docType1
&& docType2
, "Why don't we have a document type node?");
1042 docType1
->GetPublicId(string1
);
1043 docType2
->GetPublicId(string2
);
1044 if (!string1
.Equals(string2
)) {
1049 docType1
->GetSystemId(string1
);
1050 docType2
->GetSystemId(string2
);
1051 if (!string1
.Equals(string2
)) {
1056 docType1
->GetInternalSubset(string1
);
1057 docType2
->GetInternalSubset(string2
);
1058 if (!string1
.Equals(string2
)) {
1065 NS_ABORT_IF_FALSE(false, "Unknown node type");
1068 nsINode
* nextNode
= node1
->GetFirstChild();
1071 node2
= node2
->GetFirstChild();
1074 if (node2
->GetFirstChild()) {
1075 // node2 has a firstChild, but node1 doesn't
1079 // Find next sibling, possibly walking parent chain.
1081 if (node1
== this) {
1082 NS_ASSERTION(node2
== aOther
, "Should have reached the start node "
1083 "for both trees at the same time");
1087 nextNode
= node1
->GetNextSibling();
1090 node2
= node2
->GetNextSibling();
1094 if (node2
->GetNextSibling()) {
1095 // node2 has a nextSibling, but node1 doesn't
1099 node1
= node1
->GetParentNode();
1100 node2
= node2
->GetParentNode();
1101 NS_ASSERTION(node1
&& node2
, "no parent while walking subtree");
1110 nsINode::LookupNamespaceURI(const nsAString
& aNamespacePrefix
,
1111 nsAString
& aNamespaceURI
)
1113 Element
*element
= GetNameSpaceElement();
1115 NS_FAILED(element
->LookupNamespaceURIInternal(aNamespacePrefix
,
1117 SetDOMStringToNull(aNamespaceURI
);
1121 NS_IMPL_DOMTARGET_DEFAULTS(nsINode
)
1124 nsINode::AddEventListener(const nsAString
& aType
,
1125 nsIDOMEventListener
*aListener
,
1127 bool aWantsUntrusted
,
1128 uint8_t aOptionalArgc
)
1130 NS_ASSERTION(!aWantsUntrusted
|| aOptionalArgc
> 1,
1131 "Won't check if this is chrome, you want to set "
1132 "aWantsUntrusted to false or make the aWantsUntrusted "
1133 "explicit by making aOptionalArgc non-zero.");
1135 if (!aWantsUntrusted
&&
1136 (aOptionalArgc
< 2 &&
1137 !nsContentUtils::IsChromeDoc(OwnerDoc()))) {
1138 aWantsUntrusted
= true;
1141 EventListenerManager
* listener_manager
= GetOrCreateListenerManager();
1142 NS_ENSURE_STATE(listener_manager
);
1143 listener_manager
->AddEventListener(aType
, aListener
, aUseCapture
,
1149 nsINode::AddEventListener(const nsAString
& aType
,
1150 EventListener
* aListener
,
1152 const Nullable
<bool>& aWantsUntrusted
,
1155 bool wantsUntrusted
;
1156 if (aWantsUntrusted
.IsNull()) {
1157 wantsUntrusted
= !nsContentUtils::IsChromeDoc(OwnerDoc());
1159 wantsUntrusted
= aWantsUntrusted
.Value();
1162 EventListenerManager
* listener_manager
= GetOrCreateListenerManager();
1163 if (!listener_manager
) {
1164 aRv
.Throw(NS_ERROR_UNEXPECTED
);
1167 listener_manager
->AddEventListener(aType
, aListener
, aUseCapture
,
1172 nsINode::AddSystemEventListener(const nsAString
& aType
,
1173 nsIDOMEventListener
*aListener
,
1175 bool aWantsUntrusted
,
1176 uint8_t aOptionalArgc
)
1178 NS_ASSERTION(!aWantsUntrusted
|| aOptionalArgc
> 1,
1179 "Won't check if this is chrome, you want to set "
1180 "aWantsUntrusted to false or make the aWantsUntrusted "
1181 "explicit by making aOptionalArgc non-zero.");
1183 if (!aWantsUntrusted
&&
1184 (aOptionalArgc
< 2 &&
1185 !nsContentUtils::IsChromeDoc(OwnerDoc()))) {
1186 aWantsUntrusted
= true;
1189 return NS_AddSystemEventListener(this, aType
, aListener
, aUseCapture
,
1194 nsINode::RemoveEventListener(const nsAString
& aType
,
1195 nsIDOMEventListener
* aListener
,
1198 EventListenerManager
* elm
= GetExistingListenerManager();
1200 elm
->RemoveEventListener(aType
, aListener
, aUseCapture
);
1205 NS_IMPL_REMOVE_SYSTEM_EVENT_LISTENER(nsINode
)
1208 nsINode::PreHandleEvent(EventChainPreVisitor
& aVisitor
)
1210 // This is only here so that we can use the NS_DECL_NSIDOMTARGET macro
1212 return NS_ERROR_NOT_IMPLEMENTED
;
1216 nsINode::GetBoxQuads(const BoxQuadOptions
& aOptions
,
1217 nsTArray
<nsRefPtr
<DOMQuad
> >& aResult
,
1218 mozilla::ErrorResult
& aRv
)
1220 mozilla::GetBoxQuads(this, aOptions
, aResult
, aRv
);
1223 already_AddRefed
<DOMQuad
>
1224 nsINode::ConvertQuadFromNode(DOMQuad
& aQuad
,
1225 const GeometryNode
& aFrom
,
1226 const ConvertCoordinateOptions
& aOptions
,
1229 return mozilla::ConvertQuadFromNode(this, aQuad
, aFrom
, aOptions
, aRv
);
1232 already_AddRefed
<DOMQuad
>
1233 nsINode::ConvertRectFromNode(DOMRectReadOnly
& aRect
,
1234 const GeometryNode
& aFrom
,
1235 const ConvertCoordinateOptions
& aOptions
,
1238 return mozilla::ConvertRectFromNode(this, aRect
, aFrom
, aOptions
, aRv
);
1241 already_AddRefed
<DOMPoint
>
1242 nsINode::ConvertPointFromNode(const DOMPointInit
& aPoint
,
1243 const GeometryNode
& aFrom
,
1244 const ConvertCoordinateOptions
& aOptions
,
1247 return mozilla::ConvertPointFromNode(this, aPoint
, aFrom
, aOptions
, aRv
);
1251 nsINode::DispatchEvent(nsIDOMEvent
*aEvent
, bool* aRetVal
)
1253 // XXX sXBL/XBL2 issue -- do we really want the owner here? What
1254 // if that's the XBL document? Would we want its presshell? Or what?
1255 nsCOMPtr
<nsIDocument
> document
= OwnerDoc();
1257 // Do nothing if the element does not belong to a document
1263 // Obtain a presentation shell
1264 nsIPresShell
*shell
= document
->GetShell();
1265 nsRefPtr
<nsPresContext
> context
;
1267 context
= shell
->GetPresContext();
1270 nsEventStatus status
= nsEventStatus_eIgnore
;
1272 EventDispatcher::DispatchDOMEvent(this, nullptr, aEvent
, context
, &status
);
1273 *aRetVal
= (status
!= nsEventStatus_eConsumeNoDefault
);
1278 nsINode::PostHandleEvent(EventChainPostVisitor
& /*aVisitor*/)
1284 nsINode::DispatchDOMEvent(WidgetEvent
* aEvent
,
1285 nsIDOMEvent
* aDOMEvent
,
1286 nsPresContext
* aPresContext
,
1287 nsEventStatus
* aEventStatus
)
1289 return EventDispatcher::DispatchDOMEvent(this, aEvent
, aDOMEvent
,
1290 aPresContext
, aEventStatus
);
1293 EventListenerManager
*
1294 nsINode::GetOrCreateListenerManager()
1296 return nsContentUtils::GetListenerManagerForNode(this);
1299 EventListenerManager
*
1300 nsINode::GetExistingListenerManager() const
1302 return nsContentUtils::GetExistingListenerManagerForNode(this);
1306 nsINode::GetContextForEventHandlers(nsresult
* aRv
)
1308 return nsContentUtils::GetContextForEventHandlers(this, aRv
);
1312 nsINode::GetOwnerGlobal()
1315 return nsPIDOMWindow::GetOuterFromCurrentInner(
1316 static_cast<nsGlobalWindow
*>(OwnerDoc()->GetScriptHandlingObject(dummy
)));
1320 nsINode::UnoptimizableCCNode() const
1322 const uintptr_t problematicFlags
= (NODE_IS_ANONYMOUS_ROOT
|
1323 NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE
|
1324 NODE_IS_NATIVE_ANONYMOUS_ROOT
|
1325 NODE_MAY_BE_IN_BINDING_MNGR
|
1326 NODE_IS_IN_SHADOW_TREE
);
1327 return HasFlag(problematicFlags
) ||
1328 NodeType() == nsIDOMNode::ATTRIBUTE_NODE
||
1329 // For strange cases like xbl:content/xbl:children
1331 AsElement()->IsInNamespace(kNameSpaceID_XBL
));
1336 nsINode::Traverse(nsINode
*tmp
, nsCycleCollectionTraversalCallback
&cb
)
1338 if (MOZ_LIKELY(!cb
.WantAllTraces())) {
1339 nsIDocument
*currentDoc
= tmp
->GetUncomposedDoc();
1341 nsCCUncollectableMarker::InGeneration(currentDoc
->GetMarkedCCGeneration())) {
1345 if (nsCCUncollectableMarker::sGeneration
) {
1346 // If we're black no need to traverse.
1347 if (tmp
->IsBlack() || tmp
->InCCBlackTree()) {
1351 if (!tmp
->UnoptimizableCCNode()) {
1352 // If we're in a black document, return early.
1353 if ((currentDoc
&& currentDoc
->IsBlack())) {
1356 // If we're not in anonymous content and we have a black parent,
1358 nsIContent
* parent
= tmp
->GetParent();
1359 if (parent
&& !parent
->UnoptimizableCCNode() && parent
->IsBlack()) {
1360 NS_ABORT_IF_FALSE(parent
->IndexOf(tmp
) >= 0, "Parent doesn't own us?");
1367 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNodeInfo
)
1368 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(GetParent())
1370 nsSlots
*slots
= tmp
->GetExistingSlots();
1372 slots
->Traverse(cb
);
1375 if (tmp
->HasProperties()) {
1376 nsNodeUtils::TraverseUserData(tmp
, cb
);
1377 nsCOMArray
<nsISupports
>* objects
=
1378 static_cast<nsCOMArray
<nsISupports
>*>(tmp
->GetProperty(nsGkAtoms::keepobjectsalive
));
1380 for (int32_t i
= 0; i
< objects
->Count(); ++i
) {
1381 cb
.NoteXPCOMChild(objects
->ObjectAt(i
));
1386 if (tmp
->NodeType() != nsIDOMNode::DOCUMENT_NODE
&&
1387 tmp
->HasFlag(NODE_HAS_LISTENERMANAGER
)) {
1388 nsContentUtils::TraverseListenerManager(tmp
, cb
);
1396 nsINode::Unlink(nsINode
* tmp
)
1398 tmp
->ReleaseWrapper(tmp
);
1400 nsSlots
*slots
= tmp
->GetExistingSlots();
1405 if (tmp
->NodeType() != nsIDOMNode::DOCUMENT_NODE
&&
1406 tmp
->HasFlag(NODE_HAS_LISTENERMANAGER
)) {
1407 nsContentUtils::RemoveListenerManager(tmp
);
1408 tmp
->UnsetFlags(NODE_HAS_LISTENERMANAGER
);
1411 if (tmp
->HasProperties()) {
1412 nsNodeUtils::UnlinkUserData(tmp
);
1413 tmp
->DeleteProperty(nsGkAtoms::keepobjectsalive
);
1418 ReleaseURI(void*, /* aObject*/
1419 nsIAtom
*, /* aPropertyName */
1420 void* aPropertyValue
,
1423 nsIURI
* uri
= static_cast<nsIURI
*>(aPropertyValue
);
1428 nsINode::SetExplicitBaseURI(nsIURI
* aURI
)
1430 nsresult rv
= SetProperty(nsGkAtoms::baseURIProperty
, aURI
, ReleaseURI
);
1431 if (NS_SUCCEEDED(rv
)) {
1432 SetHasExplicitBaseURI();
1439 AdoptNodeIntoOwnerDoc(nsINode
*aParent
, nsINode
*aNode
)
1441 NS_ASSERTION(!aNode
->GetParentNode(),
1442 "Should have removed from parent already");
1444 nsIDocument
*doc
= aParent
->OwnerDoc();
1447 nsCOMPtr
<nsIDOMDocument
> domDoc
= do_QueryInterface(doc
, &rv
);
1448 NS_ENSURE_SUCCESS(rv
, rv
);
1450 nsCOMPtr
<nsIDOMNode
> node
= do_QueryInterface(aNode
, &rv
);
1451 NS_ENSURE_SUCCESS(rv
, rv
);
1453 nsCOMPtr
<nsIDOMNode
> adoptedNode
;
1454 rv
= domDoc
->AdoptNode(node
, getter_AddRefs(adoptedNode
));
1455 NS_ENSURE_SUCCESS(rv
, rv
);
1457 NS_ASSERTION(aParent
->OwnerDoc() == doc
,
1458 "ownerDoc chainged while adopting");
1459 NS_ASSERTION(adoptedNode
== node
, "Uh, adopt node changed nodes?");
1460 NS_ASSERTION(aParent
->OwnerDoc() == aNode
->OwnerDoc(),
1461 "ownerDocument changed again after adopting!");
1467 CheckForOutdatedParent(nsINode
* aParent
, nsINode
* aNode
)
1469 if (JSObject
* existingObjUnrooted
= aNode
->GetWrapper()) {
1470 JSRuntime
* runtime
= JS_GetObjectRuntime(existingObjUnrooted
);
1471 JS::Rooted
<JSObject
*> existingObj(runtime
, existingObjUnrooted
);
1474 nsIGlobalObject
* global
= aParent
->OwnerDoc()->GetScopeObject();
1477 if (js::GetGlobalForObjectCrossCompartment(existingObj
) !=
1478 global
->GetGlobalJSObject()) {
1479 JSAutoCompartment
ac(cx
, existingObj
);
1480 nsresult rv
= ReparentWrapper(cx
, existingObj
);
1481 NS_ENSURE_SUCCESS(rv
, rv
);
1489 nsINode::doInsertChildAt(nsIContent
* aKid
, uint32_t aIndex
,
1490 bool aNotify
, nsAttrAndChildArray
& aChildArray
)
1492 NS_PRECONDITION(!aKid
->GetParentNode(),
1493 "Inserting node that already has parent");
1496 // The id-handling code, and in the future possibly other code, need to
1497 // react to unexpected attribute changes.
1498 nsMutationGuard::DidMutate();
1500 // Do this before checking the child-count since this could cause mutations
1501 nsIDocument
* doc
= GetUncomposedDoc();
1502 mozAutoDocUpdate
updateBatch(GetCrossShadowCurrentDoc(), UPDATE_CONTENT_MODEL
, aNotify
);
1504 if (OwnerDoc() != aKid
->OwnerDoc()) {
1505 rv
= AdoptNodeIntoOwnerDoc(this, aKid
);
1506 NS_ENSURE_SUCCESS(rv
, rv
);
1507 } else if (OwnerDoc()->DidDocumentOpen()) {
1508 rv
= CheckForOutdatedParent(this, aKid
);
1509 NS_ENSURE_SUCCESS(rv
, rv
);
1512 uint32_t childCount
= aChildArray
.ChildCount();
1513 NS_ENSURE_TRUE(aIndex
<= childCount
, NS_ERROR_ILLEGAL_VALUE
);
1514 bool isAppend
= (aIndex
== childCount
);
1516 rv
= aChildArray
.InsertChildAt(aKid
, aIndex
);
1517 NS_ENSURE_SUCCESS(rv
, rv
);
1522 nsIContent
* parent
=
1523 IsNodeOfType(eDOCUMENT
) ? nullptr : static_cast<nsIContent
*>(this);
1525 rv
= aKid
->BindToTree(doc
, parent
,
1526 parent
? parent
->GetBindingParent() : nullptr,
1528 if (NS_FAILED(rv
)) {
1529 if (GetFirstChild() == aKid
) {
1530 mFirstChild
= aKid
->GetNextSibling();
1532 aChildArray
.RemoveChildAt(aIndex
);
1533 aKid
->UnbindFromTree();
1537 NS_ASSERTION(aKid
->GetParentNode() == this,
1538 "Did we run script inappropriately?");
1541 // Note that we always want to call ContentInserted when things are added
1542 // as kids to documents
1543 if (parent
&& isAppend
) {
1544 nsNodeUtils::ContentAppended(parent
, aKid
, aIndex
);
1546 nsNodeUtils::ContentInserted(this, aKid
, aIndex
);
1549 if (nsContentUtils::HasMutationListeners(aKid
,
1550 NS_EVENT_BITS_MUTATION_NODEINSERTED
, this)) {
1551 InternalMutationEvent
mutation(true, NS_MUTATION_NODEINSERTED
);
1552 mutation
.mRelatedNode
= do_QueryInterface(this);
1554 mozAutoSubtreeModified
subtree(OwnerDoc(), this);
1555 (new AsyncEventDispatcher(aKid
, mutation
))->RunDOMEventWhenSafe();
1563 nsINode::GetPreviousElementSibling() const
1565 nsIContent
* previousSibling
= GetPreviousSibling();
1566 while (previousSibling
) {
1567 if (previousSibling
->IsElement()) {
1568 return previousSibling
->AsElement();
1570 previousSibling
= previousSibling
->GetPreviousSibling();
1577 nsINode::GetNextElementSibling() const
1579 nsIContent
* nextSibling
= GetNextSibling();
1580 while (nextSibling
) {
1581 if (nextSibling
->IsElement()) {
1582 return nextSibling
->AsElement();
1584 nextSibling
= nextSibling
->GetNextSibling();
1593 nsCOMPtr
<nsINode
> parent
= GetParentNode();
1597 int32_t index
= parent
->IndexOf(this);
1599 NS_WARNING("Ignoring call to nsINode::Remove on anonymous child.");
1602 parent
->RemoveChildAt(uint32_t(index
), true);
1606 nsINode::GetFirstElementChild() const
1608 for (nsIContent
* child
= GetFirstChild();
1610 child
= child
->GetNextSibling()) {
1611 if (child
->IsElement()) {
1612 return child
->AsElement();
1620 nsINode::GetLastElementChild() const
1622 for (nsIContent
* child
= GetLastChild();
1624 child
= child
->GetPreviousSibling()) {
1625 if (child
->IsElement()) {
1626 return child
->AsElement();
1634 nsINode::doRemoveChildAt(uint32_t aIndex
, bool aNotify
,
1635 nsIContent
* aKid
, nsAttrAndChildArray
& aChildArray
)
1637 NS_PRECONDITION(aKid
&& aKid
->GetParentNode() == this &&
1638 aKid
== GetChildAt(aIndex
) &&
1639 IndexOf(aKid
) == (int32_t)aIndex
, "Bogus aKid");
1641 nsMutationGuard::DidMutate();
1642 mozAutoDocUpdate
updateBatch(GetCrossShadowCurrentDoc(), UPDATE_CONTENT_MODEL
, aNotify
);
1644 nsIContent
* previousSibling
= aKid
->GetPreviousSibling();
1646 if (GetFirstChild() == aKid
) {
1647 mFirstChild
= aKid
->GetNextSibling();
1650 aChildArray
.RemoveChildAt(aIndex
);
1653 nsNodeUtils::ContentRemoved(this, aKid
, aIndex
, previousSibling
);
1656 aKid
->UnbindFromTree();
1659 // When replacing, aRefChild is the content being replaced; when
1660 // inserting it's the content before which we're inserting. In the
1661 // latter case it may be null.
1663 bool IsAllowedAsChild(nsIContent
* aNewChild
, nsINode
* aParent
,
1664 bool aIsReplace
, nsINode
* aRefChild
)
1666 MOZ_ASSERT(aNewChild
, "Must have new child");
1667 MOZ_ASSERT_IF(aIsReplace
, aRefChild
);
1668 MOZ_ASSERT(aParent
);
1669 MOZ_ASSERT(aParent
->IsNodeOfType(nsINode::eDOCUMENT
) ||
1670 aParent
->IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT
) ||
1671 aParent
->IsElement(),
1672 "Nodes that are not documents, document fragments or elements "
1673 "can't be parents!");
1675 // A common case is that aNewChild has no kids, in which case
1676 // aParent can't be a descendant of aNewChild unless they're
1677 // actually equal to each other. Fast-path that case, since aParent
1678 // could be pretty deep in the DOM tree.
1679 if (aNewChild
== aParent
||
1680 ((aNewChild
->GetFirstChild() ||
1681 // HTML template elements and ShadowRoot hosts need
1682 // to be checked to ensure that they are not inserted into
1683 // the hosted content.
1684 aNewChild
->Tag() == nsGkAtoms::_template
||
1685 aNewChild
->GetShadowRoot()) &&
1686 nsContentUtils::ContentIsHostIncludingDescendantOf(aParent
,
1691 // The allowed child nodes differ for documents and elements
1692 switch (aNewChild
->NodeType()) {
1693 case nsIDOMNode::COMMENT_NODE
:
1694 case nsIDOMNode::PROCESSING_INSTRUCTION_NODE
:
1697 case nsIDOMNode::TEXT_NODE
:
1698 case nsIDOMNode::CDATA_SECTION_NODE
:
1699 case nsIDOMNode::ENTITY_REFERENCE_NODE
:
1700 // Allowed under Elements and DocumentFragments
1701 return aParent
->NodeType() != nsIDOMNode::DOCUMENT_NODE
;
1702 case nsIDOMNode::ELEMENT_NODE
:
1704 if (!aParent
->IsNodeOfType(nsINode::eDOCUMENT
)) {
1705 // Always ok to have elements under other elements or document fragments
1709 nsIDocument
* parentDocument
= static_cast<nsIDocument
*>(aParent
);
1710 Element
* rootElement
= parentDocument
->GetRootElement();
1712 // Already have a documentElement, so this is only OK if we're
1714 return aIsReplace
&& rootElement
== aRefChild
;
1717 // We don't have a documentElement yet. Our one remaining constraint is
1718 // that the documentElement must come after the doctype.
1720 // Appending is just fine.
1724 nsIContent
* docTypeContent
= parentDocument
->GetDoctype();
1725 if (!docTypeContent
) {
1730 int32_t doctypeIndex
= aParent
->IndexOf(docTypeContent
);
1731 int32_t insertIndex
= aParent
->IndexOf(aRefChild
);
1733 // Now we're OK in the following two cases only:
1734 // 1) We're replacing something that's not before the doctype
1735 // 2) We're inserting before something that comes after the doctype
1736 return aIsReplace
? (insertIndex
>= doctypeIndex
) :
1737 insertIndex
> doctypeIndex
;
1739 case nsIDOMNode::DOCUMENT_TYPE_NODE
:
1741 if (!aParent
->IsNodeOfType(nsINode::eDOCUMENT
)) {
1742 // doctypes only allowed under documents
1746 nsIDocument
* parentDocument
= static_cast<nsIDocument
*>(aParent
);
1747 nsIContent
* docTypeContent
= parentDocument
->GetDoctype();
1748 if (docTypeContent
) {
1749 // Already have a doctype, so this is only OK if we're replacing it
1750 return aIsReplace
&& docTypeContent
== aRefChild
;
1753 // We don't have a doctype yet. Our one remaining constraint is
1754 // that the doctype must come before the documentElement.
1755 Element
* rootElement
= parentDocument
->GetRootElement();
1762 // Trying to append a doctype, but have a documentElement
1766 int32_t rootIndex
= aParent
->IndexOf(rootElement
);
1767 int32_t insertIndex
= aParent
->IndexOf(aRefChild
);
1769 // Now we're OK if and only if insertIndex <= rootIndex. Indeed, either
1770 // we end up replacing aRefChild or we end up before it. Either one is
1771 // ok as long as aRefChild is not after rootElement.
1772 return insertIndex
<= rootIndex
;
1774 case nsIDOMNode::DOCUMENT_FRAGMENT_NODE
:
1776 // Note that for now we only allow nodes inside document fragments if
1777 // they're allowed inside elements. If we ever change this to allow
1778 // doctype nodes in document fragments, we'll need to update this code.
1779 // Also, there's a version of this code in ReplaceOrInsertBefore. If you
1780 // change this code, change that too.
1781 if (!aParent
->IsNodeOfType(nsINode::eDOCUMENT
)) {
1786 bool sawElement
= false;
1787 for (nsIContent
* child
= aNewChild
->GetFirstChild();
1789 child
= child
->GetNextSibling()) {
1790 if (child
->IsElement()) {
1792 // Can't put two elements into a document
1797 // If we can put this content at the the right place, we might be ok;
1798 // if not, we bail out.
1799 if (!IsAllowedAsChild(child
, aParent
, aIsReplace
, aRefChild
)) {
1804 // Everything in the fragment checked out ok, so we can stick it in here
1809 * aNewChild is of invalid type.
1818 nsINode::ReplaceOrInsertBefore(bool aReplace
, nsINode
* aNewChild
,
1819 nsINode
* aRefChild
, ErrorResult
& aError
)
1821 // XXXbz I wish I could assert that nsContentUtils::IsSafeToRunScript() so we
1822 // could rely on scriptblockers going out of scope to actually run XBL
1823 // teardown, but various crud adds nodes under scriptblockers (e.g. native
1824 // anonymous content). The only good news is those insertions can't trigger
1825 // the bad XBL cases.
1826 MOZ_ASSERT_IF(aReplace
, aRefChild
);
1828 if ((!IsNodeOfType(eDOCUMENT
) &&
1829 !IsNodeOfType(eDOCUMENT_FRAGMENT
) &&
1831 !aNewChild
->IsNodeOfType(eCONTENT
)) {
1832 aError
.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR
);
1836 uint16_t nodeType
= aNewChild
->NodeType();
1838 // Before we do anything else, fire all DOMNodeRemoved mutation events
1839 // We do this up front as to avoid having to deal with script running
1840 // at random places further down.
1841 // Scope firing mutation events so that we don't carry any state that
1844 // This check happens again further down (though then using IndexOf).
1845 // We're only checking this here to avoid firing mutation events when
1846 // none should be fired.
1847 // It's ok that we do the check twice in the case when firing mutation
1848 // events as we need to recheck after running script anyway.
1849 if (aRefChild
&& aRefChild
->GetParentNode() != this) {
1850 aError
.Throw(NS_ERROR_DOM_NOT_FOUND_ERR
);
1854 // If we're replacing, fire for node-to-be-replaced.
1855 // If aRefChild == aNewChild then we'll fire for it in check below
1856 if (aReplace
&& aRefChild
!= aNewChild
) {
1857 nsContentUtils::MaybeFireNodeRemoved(aRefChild
, this, OwnerDoc());
1860 // If the new node already has a parent, fire for removing from old
1862 nsINode
* oldParent
= aNewChild
->GetParentNode();
1864 nsContentUtils::MaybeFireNodeRemoved(aNewChild
, oldParent
,
1865 aNewChild
->OwnerDoc());
1868 // If we're inserting a fragment, fire for all the children of the
1870 if (nodeType
== nsIDOMNode::DOCUMENT_FRAGMENT_NODE
) {
1871 static_cast<FragmentOrElement
*>(aNewChild
)->FireNodeRemovedForChildren();
1873 // Verify that our aRefChild is still sensible
1874 if (aRefChild
&& aRefChild
->GetParentNode() != this) {
1875 aError
.Throw(NS_ERROR_DOM_NOT_FOUND_ERR
);
1880 nsIDocument
* doc
= OwnerDoc();
1881 nsIContent
* newContent
= static_cast<nsIContent
*>(aNewChild
);
1882 if (newContent
->IsRootOfAnonymousSubtree()) {
1883 // This is anonymous content. Don't allow its insertion
1884 // anywhere, since it might have UnbindFromTree calls coming
1886 aError
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
1890 // Make sure that the inserted node is allowed as a child of its new parent.
1891 if (!IsAllowedAsChild(newContent
, this, aReplace
, aRefChild
)) {
1892 aError
.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR
);
1896 // Record the node to insert before, if any
1897 nsINode
* nodeToInsertBefore
;
1899 nodeToInsertBefore
= aRefChild
->GetNextSibling();
1901 nodeToInsertBefore
= aRefChild
;
1903 if (nodeToInsertBefore
== aNewChild
) {
1904 // We're going to remove aNewChild from its parent, so use its next sibling
1905 // as the node to insert before.
1906 nodeToInsertBefore
= nodeToInsertBefore
->GetNextSibling();
1909 Maybe
<nsAutoTArray
<nsCOMPtr
<nsIContent
>, 50> > fragChildren
;
1911 // Remove the new child from the old parent if one exists
1912 nsCOMPtr
<nsINode
> oldParent
= newContent
->GetParentNode();
1914 int32_t removeIndex
= oldParent
->IndexOf(newContent
);
1915 if (removeIndex
< 0) {
1916 // newContent is anonymous. We can't deal with this, so just bail
1917 NS_ERROR("How come our flags didn't catch this?");
1918 aError
.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
1922 // Hold a strong ref to nodeToInsertBefore across the removal of newContent
1923 nsCOMPtr
<nsINode
> kungFuDeathGrip
= nodeToInsertBefore
;
1925 // Removing a child can run script, via XBL destructors.
1926 nsMutationGuard guard
;
1928 // Scope for the mutation batch and scriptblocker, so they go away
1929 // while kungFuDeathGrip is still alive.
1931 mozAutoDocUpdate
batch(newContent
->GetComposedDoc(),
1932 UPDATE_CONTENT_MODEL
, true);
1933 nsAutoMutationBatch
mb(oldParent
, true, true);
1934 oldParent
->RemoveChildAt(removeIndex
, true);
1935 if (nsAutoMutationBatch::GetCurrentBatch() == &mb
) {
1937 mb
.SetPrevSibling(oldParent
->GetChildAt(removeIndex
- 1));
1938 mb
.SetNextSibling(oldParent
->GetChildAt(removeIndex
));
1942 // We expect one mutation (the removal) to have happened.
1943 if (guard
.Mutated(1)) {
1944 // XBL destructors, yuck.
1946 // Verify that nodeToInsertBefore, if non-null, is still our child. If
1947 // it's not, there's no way we can do this insert sanely; just bail out.
1948 if (nodeToInsertBefore
&& nodeToInsertBefore
->GetParent() != this) {
1949 aError
.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR
);
1953 // Verify that newContent has no parent.
1954 if (newContent
->GetParentNode()) {
1955 aError
.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR
);
1959 // And verify that newContent is still allowed as our child.
1960 if (aNewChild
== aRefChild
) {
1961 // We've already removed aRefChild. So even if we were doing a replace,
1962 // now we're doing a simple insert before nodeToInsertBefore.
1963 if (!IsAllowedAsChild(newContent
, this, false, nodeToInsertBefore
)) {
1964 aError
.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR
);
1968 if ((aRefChild
&& aRefChild
->GetParent() != this) ||
1969 !IsAllowedAsChild(newContent
, this, aReplace
, aRefChild
)) {
1970 aError
.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR
);
1973 // And recompute nodeToInsertBefore, just in case.
1975 nodeToInsertBefore
= aRefChild
->GetNextSibling();
1977 nodeToInsertBefore
= aRefChild
;
1981 } else if (nodeType
== nsIDOMNode::DOCUMENT_FRAGMENT_NODE
) {
1982 // Make sure to remove all the fragment's kids. We need to do this before
1983 // we start inserting anything, so we will run out XBL destructors and
1984 // binding teardown (GOD, I HATE THESE THINGS) before we insert anything
1986 uint32_t count
= newContent
->GetChildCount();
1988 fragChildren
.emplace();
1990 // Copy the children into a separate array to avoid having to deal with
1991 // mutations to the fragment later on here.
1992 fragChildren
->SetCapacity(count
);
1993 for (nsIContent
* child
= newContent
->GetFirstChild();
1995 child
= child
->GetNextSibling()) {
1996 NS_ASSERTION(child
->GetComposedDoc() == nullptr,
1997 "How did we get a child with a current doc?");
1998 fragChildren
->AppendElement(child
);
2001 // Hold a strong ref to nodeToInsertBefore across the removals
2002 nsCOMPtr
<nsINode
> kungFuDeathGrip
= nodeToInsertBefore
;
2004 nsMutationGuard guard
;
2006 // Scope for the mutation batch and scriptblocker, so they go away
2007 // while kungFuDeathGrip is still alive.
2009 mozAutoDocUpdate
batch(newContent
->GetComposedDoc(),
2010 UPDATE_CONTENT_MODEL
, true);
2011 nsAutoMutationBatch
mb(newContent
, false, true);
2013 for (uint32_t i
= count
; i
> 0;) {
2014 newContent
->RemoveChildAt(--i
, true);
2018 // We expect |count| removals
2019 if (guard
.Mutated(count
)) {
2020 // XBL destructors, yuck.
2022 // Verify that nodeToInsertBefore, if non-null, is still our child. If
2023 // it's not, there's no way we can do this insert sanely; just bail out.
2024 if (nodeToInsertBefore
&& nodeToInsertBefore
->GetParent() != this) {
2025 aError
.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR
);
2029 // Verify that all the things in fragChildren have no parent.
2030 for (uint32_t i
= 0; i
< count
; ++i
) {
2031 if (fragChildren
->ElementAt(i
)->GetParentNode()) {
2032 aError
.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR
);
2037 // Note that unlike the single-element case above, none of our kids can
2038 // be aRefChild, so we can always pass through aReplace in the
2039 // IsAllowedAsChild checks below and don't have to worry about whether
2040 // recomputing nodeToInsertBefore is OK.
2042 // Verify that our aRefChild is still sensible
2043 if (aRefChild
&& aRefChild
->GetParent() != this) {
2044 aError
.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR
);
2048 // Recompute nodeToInsertBefore, just in case.
2050 nodeToInsertBefore
= aRefChild
->GetNextSibling();
2052 nodeToInsertBefore
= aRefChild
;
2055 // And verify that newContent is still allowed as our child. Sadly, we
2056 // need to reimplement the relevant part of IsAllowedAsChild() because
2057 // now our nodes are in an array and all. If you change this code,
2058 // change the code there.
2059 if (IsNodeOfType(nsINode::eDOCUMENT
)) {
2060 bool sawElement
= false;
2061 for (uint32_t i
= 0; i
< count
; ++i
) {
2062 nsIContent
* child
= fragChildren
->ElementAt(i
);
2063 if (child
->IsElement()) {
2066 aError
.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR
);
2071 if (!IsAllowedAsChild(child
, this, aReplace
, aRefChild
)) {
2072 aError
.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR
);
2080 mozAutoDocUpdate
batch(GetCrossShadowCurrentDoc(), UPDATE_CONTENT_MODEL
, true);
2081 nsAutoMutationBatch mb
;
2083 // Figure out which index we want to insert at. Note that we use
2084 // nodeToInsertBefore to determine this, because it's possible that
2085 // aRefChild == aNewChild, in which case we just removed it from the
2088 if (nodeToInsertBefore
) {
2089 insPos
= IndexOf(nodeToInsertBefore
);
2091 // XXXbz How the heck would _that_ happen, exactly?
2092 aError
.Throw(NS_ERROR_DOM_NOT_FOUND_ERR
);
2097 insPos
= GetChildCount();
2100 // If we're replacing and we haven't removed aRefChild yet, do so now
2101 if (aReplace
&& aRefChild
!= aNewChild
) {
2102 mb
.Init(this, true, true);
2104 // Since aRefChild is never null in the aReplace case, we know that at
2105 // this point nodeToInsertBefore is the next sibling of aRefChild.
2106 NS_ASSERTION(aRefChild
->GetNextSibling() == nodeToInsertBefore
,
2107 "Unexpected nodeToInsertBefore");
2109 // An since nodeToInsertBefore is at index insPos, we want to remove
2110 // at the previous index.
2111 NS_ASSERTION(insPos
>= 1, "insPos too small");
2112 RemoveChildAt(insPos
-1, true);
2116 // Move new child over to our document if needed. Do this after removing
2117 // it from its parent so that AdoptNode doesn't fire DOMNodeRemoved
2118 // DocumentType nodes are the only nodes that can have a null
2119 // ownerDocument according to the DOM spec, and we need to allow
2120 // inserting them w/o calling AdoptNode().
2121 if (doc
!= newContent
->OwnerDoc()) {
2122 aError
= AdoptNodeIntoOwnerDoc(this, aNewChild
);
2123 if (aError
.Failed()) {
2126 } else if (doc
->DidDocumentOpen()) {
2127 aError
= CheckForOutdatedParent(this, aNewChild
);
2128 if (aError
.Failed()) {
2134 * Check if we're inserting a document fragment. If we are, we need
2135 * to actually add its children individually (i.e. we don't add the
2136 * actual document fragment).
2138 nsINode
* result
= aReplace
? aRefChild
: aNewChild
;
2139 if (nodeType
== nsIDOMNode::DOCUMENT_FRAGMENT_NODE
) {
2141 mb
.Init(this, true, true);
2143 nsAutoMutationBatch
* mutationBatch
= nsAutoMutationBatch::GetCurrentBatch();
2144 if (mutationBatch
) {
2145 mutationBatch
->RemovalDone();
2146 mutationBatch
->SetPrevSibling(GetChildAt(insPos
- 1));
2147 mutationBatch
->SetNextSibling(GetChildAt(insPos
));
2150 uint32_t count
= fragChildren
->Length();
2156 !IsNodeOfType(eDOCUMENT
) && uint32_t(insPos
) == GetChildCount();
2157 int32_t firstInsPos
= insPos
;
2158 nsIContent
* firstInsertedContent
= fragChildren
->ElementAt(0);
2160 // Iterate through the fragment's children, and insert them in the new
2162 for (uint32_t i
= 0; i
< count
; ++i
, ++insPos
) {
2163 // XXXbz how come no reparenting here? That seems odd...
2164 // Insert the child.
2165 aError
= InsertChildAt(fragChildren
->ElementAt(i
), insPos
,
2167 if (aError
.Failed()) {
2168 // Make sure to notify on any children that we did succeed to insert
2169 if (appending
&& i
!= 0) {
2170 nsNodeUtils::ContentAppended(static_cast<nsIContent
*>(this),
2171 firstInsertedContent
,
2178 if (mutationBatch
&& !appending
) {
2179 mutationBatch
->NodesAdded();
2182 // Notify and fire mutation events when appending
2184 nsNodeUtils::ContentAppended(static_cast<nsIContent
*>(this),
2185 firstInsertedContent
, firstInsPos
);
2186 if (mutationBatch
) {
2187 mutationBatch
->NodesAdded();
2189 // Optimize for the case when there are no listeners
2190 if (nsContentUtils::
2191 HasMutationListeners(doc
, NS_EVENT_BITS_MUTATION_NODEINSERTED
)) {
2192 Element::FireNodeInserted(doc
, this, *fragChildren
);
2197 // Not inserting a fragment but rather a single node.
2199 // FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=544654
2200 // We need to reparent here for nodes for which the parent of their
2201 // wrapper is not the wrapper for their ownerDocument (XUL elements,
2202 // form controls, ...). Also applies in the fragment code above.
2204 if (nsAutoMutationBatch::GetCurrentBatch() == &mb
) {
2206 mb
.SetPrevSibling(GetChildAt(insPos
- 1));
2207 mb
.SetNextSibling(GetChildAt(insPos
));
2209 aError
= InsertChildAt(newContent
, insPos
, true);
2210 if (aError
.Failed()) {
2219 nsINode::ReplaceOrInsertBefore(bool aReplace
, nsIDOMNode
*aNewChild
,
2220 nsIDOMNode
*aRefChild
, nsIDOMNode
**aReturn
)
2222 nsCOMPtr
<nsINode
> newChild
= do_QueryInterface(aNewChild
);
2224 return NS_ERROR_NULL_POINTER
;
2227 if (aReplace
&& !aRefChild
) {
2228 return NS_ERROR_NULL_POINTER
;
2231 nsCOMPtr
<nsINode
> refChild
= do_QueryInterface(aRefChild
);
2232 if (aRefChild
&& !refChild
) {
2233 return NS_NOINTERFACE
;
2237 nsINode
* result
= ReplaceOrInsertBefore(aReplace
, newChild
, refChild
, rv
);
2239 NS_ADDREF(*aReturn
= result
->AsDOMNode());
2241 return rv
.ErrorCode();
2245 nsINode::CompareDocumentPosition(nsIDOMNode
* aOther
, uint16_t* aReturn
)
2247 nsCOMPtr
<nsINode
> other
= do_QueryInterface(aOther
);
2248 NS_ENSURE_ARG(other
);
2249 *aReturn
= CompareDocumentPosition(*other
);
2254 nsINode::IsEqualNode(nsIDOMNode
* aOther
, bool* aReturn
)
2256 nsCOMPtr
<nsINode
> other
= do_QueryInterface(aOther
);
2257 *aReturn
= IsEqualNode(other
);
2262 nsINode::BindObject(nsISupports
* aObject
)
2264 nsCOMArray
<nsISupports
>* objects
=
2265 static_cast<nsCOMArray
<nsISupports
>*>(GetProperty(nsGkAtoms::keepobjectsalive
));
2267 objects
= new nsCOMArray
<nsISupports
>();
2268 SetProperty(nsGkAtoms::keepobjectsalive
, objects
,
2269 nsINode::DeleteProperty
< nsCOMArray
<nsISupports
> >, true);
2271 objects
->AppendObject(aObject
);
2275 nsINode::UnbindObject(nsISupports
* aObject
)
2277 nsCOMArray
<nsISupports
>* objects
=
2278 static_cast<nsCOMArray
<nsISupports
>*>(GetProperty(nsGkAtoms::keepobjectsalive
));
2280 objects
->RemoveObject(aObject
);
2285 nsINode::GetBoundMutationObservers(nsTArray
<nsRefPtr
<nsDOMMutationObserver
> >& aResult
)
2287 nsCOMArray
<nsISupports
>* objects
=
2288 static_cast<nsCOMArray
<nsISupports
>*>(GetProperty(nsGkAtoms::keepobjectsalive
));
2290 for (int32_t i
= 0; i
< objects
->Count(); ++i
) {
2291 nsCOMPtr
<nsDOMMutationObserver
> mo
= do_QueryInterface(objects
->ObjectAt(i
));
2293 MOZ_ASSERT(!aResult
.Contains(mo
));
2294 aResult
.AppendElement(mo
.forget());
2301 nsINode::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf
) const
2304 EventListenerManager
* elm
= GetExistingListenerManager();
2306 n
+= elm
->SizeOfIncludingThis(aMallocSizeOf
);
2309 // Measurement of the following members may be added later if DMD finds it is
2314 // The following members are not measured:
2315 // - mParent, mNextSibling, mPreviousSibling, mFirstChild: because they're
2320 #define EVENT(name_, id_, type_, struct_) \
2321 EventHandlerNonNull* nsINode::GetOn##name_() { \
2322 EventListenerManager *elm = GetExistingListenerManager(); \
2323 return elm ? elm->GetEventHandler(nsGkAtoms::on##name_, EmptyString()) \
2326 void nsINode::SetOn##name_(EventHandlerNonNull* handler) \
2328 EventListenerManager *elm = GetOrCreateListenerManager(); \
2330 elm->SetEventHandler(nsGkAtoms::on##name_, EmptyString(), handler); \
2333 #define TOUCH_EVENT EVENT
2334 #define DOCUMENT_ONLY_EVENT EVENT
2335 #include "mozilla/EventNameList.h"
2336 #undef DOCUMENT_ONLY_EVENT
2341 nsINode::Contains(const nsINode
* aOther
) const
2343 if (aOther
== this) {
2347 OwnerDoc() != aOther
->OwnerDoc() ||
2348 IsInDoc() != aOther
->IsInDoc() ||
2349 !(aOther
->IsElement() ||
2350 aOther
->IsNodeOfType(nsINode::eCONTENT
)) ||
2355 const nsIContent
* other
= static_cast<const nsIContent
*>(aOther
);
2356 if (this == OwnerDoc()) {
2357 // document.contains(aOther) returns true if aOther is in the document,
2358 // but is not in any anonymous subtree.
2359 // IsInDoc() check is done already before this.
2360 return !other
->IsInAnonymousSubtree();
2363 if (!IsElement() && !IsNodeOfType(nsINode::eDOCUMENT_FRAGMENT
)) {
2367 const nsIContent
* thisContent
= static_cast<const nsIContent
*>(this);
2368 if (thisContent
->GetBindingParent() != other
->GetBindingParent()) {
2372 return nsContentUtils::ContentIsDescendantOf(other
, this);
2376 nsINode::Contains(nsIDOMNode
* aOther
, bool* aReturn
)
2378 nsCOMPtr
<nsINode
> node
= do_QueryInterface(aOther
);
2379 *aReturn
= Contains(node
);
2384 nsINode::Length() const
2386 switch (NodeType()) {
2387 case nsIDOMNode::DOCUMENT_TYPE_NODE
:
2390 case nsIDOMNode::TEXT_NODE
:
2391 case nsIDOMNode::CDATA_SECTION_NODE
:
2392 case nsIDOMNode::PROCESSING_INSTRUCTION_NODE
:
2393 case nsIDOMNode::COMMENT_NODE
:
2394 MOZ_ASSERT(IsNodeOfType(eCONTENT
));
2395 return static_cast<const nsIContent
*>(this)->TextLength();
2398 return GetChildCount();
2403 nsINode::ParseSelectorList(const nsAString
& aSelectorString
,
2406 nsIDocument
* doc
= OwnerDoc();
2407 nsIDocument::SelectorCache
& cache
= doc
->GetSelectorCache();
2408 nsCSSSelectorList
* selectorList
= nullptr;
2409 bool haveCachedList
= cache
.GetList(aSelectorString
, &selectorList
);
2410 if (haveCachedList
) {
2411 if (!selectorList
) {
2412 // Invalid selector.
2413 aRv
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
2415 return selectorList
;
2418 nsCSSParser
parser(doc
->CSSLoader());
2420 aRv
= parser
.ParseSelectorString(aSelectorString
,
2421 doc
->GetDocumentURI(),
2422 0, // XXXbz get the line number!
2425 // We hit this for syntax errors, which are quite common, so don't
2426 // use NS_ENSURE_SUCCESS. (For example, jQuery has an extended set
2427 // of selectors, but it sees if we can parse them first.)
2428 MOZ_ASSERT(aRv
.ErrorCode() == NS_ERROR_DOM_SYNTAX_ERR
,
2429 "Unexpected error, so cached version won't return it");
2430 cache
.CacheList(aSelectorString
, nullptr);
2434 // Filter out pseudo-element selectors from selectorList
2435 nsCSSSelectorList
** slot
= &selectorList
;
2437 nsCSSSelectorList
* cur
= *slot
;
2438 if (cur
->mSelectors
->IsPseudoElement()) {
2440 cur
->mNext
= nullptr;
2448 NS_ASSERTION(selectorList
->mSelectors
,
2449 "How can we not have any selectors?");
2450 cache
.CacheList(aSelectorString
, selectorList
);
2452 // This is the "only pseudo-element selectors" case, which is
2453 // not common, so just don't worry about caching it. That way a
2454 // null cached value can always indicate an invalid selector.
2457 return selectorList
;
2461 AddScopeElements(TreeMatchContext
& aMatchContext
,
2462 nsINode
* aMatchContextNode
)
2464 if (aMatchContextNode
->IsElement()) {
2465 aMatchContext
.SetHasSpecifiedScope();
2466 aMatchContext
.AddScopeElement(aMatchContextNode
->AsElement());
2471 struct SelectorMatchInfo
{
2472 nsCSSSelectorList
* const mSelectorList
;
2473 TreeMatchContext
& mMatchContext
;
2477 // Given an id, find elements with that id under aRoot that match aMatchInfo if
2478 // any is provided. If no SelectorMatchInfo is provided, just find the ones
2479 // with the given id. aRoot must be in the document.
2480 template<bool onlyFirstMatch
, class T
>
2482 FindMatchingElementsWithId(const nsAString
& aId
, nsINode
* aRoot
,
2483 SelectorMatchInfo
* aMatchInfo
,
2486 MOZ_ASSERT(aRoot
->IsInDoc(),
2487 "Don't call me if the root is not in the document");
2488 MOZ_ASSERT(aRoot
->IsElement() || aRoot
->IsNodeOfType(nsINode::eDOCUMENT
),
2489 "The optimization below to check ContentIsDescendantOf only for "
2490 "elements depends on aRoot being either an element or a "
2491 "document if it's in the document. Note that document fragments "
2492 "can't be IsInDoc(), so should never show up here.");
2494 const nsSmallVoidArray
* elements
= aRoot
->OwnerDoc()->GetAllElementsForId(aId
);
2497 // Nothing to do; we're done
2501 // XXXbz: Should we fall back to the tree walk if aRoot is not the
2502 // document and |elements| is long, for some value of "long"?
2503 for (int32_t i
= 0; i
< elements
->Count(); ++i
) {
2504 Element
*element
= static_cast<Element
*>(elements
->ElementAt(i
));
2505 if (!aRoot
->IsElement() ||
2506 (element
!= aRoot
&&
2507 nsContentUtils::ContentIsDescendantOf(element
, aRoot
))) {
2508 // We have an element with the right id and it's a strict descendant
2509 // of aRoot. Make sure it really matches the selector.
2511 nsCSSRuleProcessor::SelectorListMatches(element
,
2512 aMatchInfo
->mMatchContext
,
2513 aMatchInfo
->mSelectorList
)) {
2514 aList
.AppendElement(element
);
2515 if (onlyFirstMatch
) {
2523 // Actually find elements matching aSelectorList (which must not be
2524 // null) and which are descendants of aRoot and put them in aList. If
2525 // onlyFirstMatch, then stop once the first one is found.
2526 template<bool onlyFirstMatch
, class Collector
, class T
>
2527 MOZ_ALWAYS_INLINE
static void
2528 FindMatchingElements(nsINode
* aRoot
, nsCSSSelectorList
* aSelectorList
, T
&aList
,
2531 nsIDocument
* doc
= aRoot
->OwnerDoc();
2533 TreeMatchContext
matchingContext(false, nsRuleWalker::eRelevantLinkUnvisited
,
2534 doc
, TreeMatchContext::eNeverMatchVisited
);
2535 doc
->FlushPendingLinkUpdates();
2536 AddScopeElements(matchingContext
, aRoot
);
2538 // Fast-path selectors involving IDs. We can only do this if aRoot
2539 // is in the document and the document is not in quirks mode, since
2540 // ID selectors are case-insensitive in quirks mode. Also, only do
2541 // this if aSelectorList only has one selector, because otherwise
2542 // ordering the elements correctly is a pain.
2543 NS_ASSERTION(aRoot
->IsElement() || aRoot
->IsNodeOfType(nsINode::eDOCUMENT
) ||
2545 "The optimization below to check ContentIsDescendantOf only for "
2546 "elements depends on aRoot being either an element or a "
2547 "document if it's in the document.");
2548 if (aRoot
->IsInDoc() &&
2549 doc
->GetCompatibilityMode() != eCompatibility_NavQuirks
&&
2550 !aSelectorList
->mNext
&&
2551 aSelectorList
->mSelectors
->mIDList
) {
2552 nsIAtom
* id
= aSelectorList
->mSelectors
->mIDList
->mAtom
;
2553 SelectorMatchInfo info
= { aSelectorList
, matchingContext
};
2554 FindMatchingElementsWithId
<onlyFirstMatch
, T
>(nsDependentAtomString(id
),
2555 aRoot
, &info
, aList
);
2560 for (nsIContent
* cur
= aRoot
->GetFirstChild();
2562 cur
= cur
->GetNextNode(aRoot
)) {
2563 if (cur
->IsElement() &&
2564 nsCSSRuleProcessor::SelectorListMatches(cur
->AsElement(),
2567 if (onlyFirstMatch
) {
2568 aList
.AppendElement(cur
->AsElement());
2571 results
.AppendElement(cur
->AsElement());
2575 const uint32_t len
= results
.Length();
2577 aList
.SetCapacity(len
);
2578 for (uint32_t i
= 0; i
< len
; ++i
) {
2579 aList
.AppendElement(results
.ElementAt(i
));
2584 struct ElementHolder
{
2585 ElementHolder() : mElement(nullptr) {}
2586 void AppendElement(Element
* aElement
) {
2587 NS_ABORT_IF_FALSE(!mElement
, "Should only get one element");
2588 mElement
= aElement
;
2590 void SetCapacity(uint32_t aCapacity
) { MOZ_CRASH("Don't call me!"); }
2591 uint32_t Length() { return 0; }
2592 Element
* ElementAt(uint32_t aIndex
) { return nullptr; }
2598 nsINode::QuerySelector(const nsAString
& aSelector
, ErrorResult
& aResult
)
2600 nsCSSSelectorList
* selectorList
= ParseSelectorList(aSelector
, aResult
);
2601 if (!selectorList
) {
2602 // Either we failed (and aResult already has the exception), or this
2603 // is a pseudo-element-only selector that matches nothing.
2606 ElementHolder holder
;
2607 FindMatchingElements
<true, ElementHolder
>(this, selectorList
, holder
, aResult
);
2608 return holder
.mElement
;
2611 already_AddRefed
<nsINodeList
>
2612 nsINode::QuerySelectorAll(const nsAString
& aSelector
, ErrorResult
& aResult
)
2614 nsRefPtr
<nsSimpleContentList
> contentList
= new nsSimpleContentList(this);
2616 nsCSSSelectorList
* selectorList
= ParseSelectorList(aSelector
, aResult
);
2618 FindMatchingElements
<false, nsAutoTArray
<Element
*, 128>>(this,
2623 // Either we failed (and aResult already has the exception), or this
2624 // is a pseudo-element-only selector that matches nothing.
2627 return contentList
.forget();
2631 nsINode::QuerySelector(const nsAString
& aSelector
, nsIDOMElement
**aReturn
)
2634 Element
* result
= nsINode::QuerySelector(aSelector
, rv
);
2636 return rv
.ErrorCode();
2638 nsCOMPtr
<nsIDOMElement
> elt
= do_QueryInterface(result
);
2639 elt
.forget(aReturn
);
2644 nsINode::QuerySelectorAll(const nsAString
& aSelector
, nsIDOMNodeList
**aReturn
)
2647 *aReturn
= nsINode::QuerySelectorAll(aSelector
, rv
).take();
2648 return rv
.ErrorCode();
2652 nsINode::GetElementById(const nsAString
& aId
)
2654 MOZ_ASSERT(IsElement() || IsNodeOfType(eDOCUMENT_FRAGMENT
),
2655 "Bogus this object for GetElementById call");
2657 ElementHolder holder
;
2658 FindMatchingElementsWithId
<true>(aId
, this, nullptr, holder
);
2659 return holder
.mElement
;
2662 for (nsIContent
* kid
= GetFirstChild(); kid
; kid
= kid
->GetNextNode(this)) {
2663 if (!kid
->IsElement()) {
2666 nsIAtom
* id
= kid
->AsElement()->GetID();
2667 if (id
&& id
->Equals(aId
)) {
2668 return kid
->AsElement();
2675 nsINode::WrapObject(JSContext
*aCx
)
2677 // Make sure one of these is true
2678 // (1) our owner document has a script handling object,
2679 // (2) Our owner document has had a script handling object, or has been marked
2681 // (3) we are running a privileged script.
2682 // Event handling is possible only if (1). If (2) event handling is
2684 // If the document has never had a script handling object, untrusted
2685 // scripts (3) shouldn't touch it!
2686 bool hasHadScriptHandlingObject
= false;
2687 if (!OwnerDoc()->GetScriptHandlingObject(hasHadScriptHandlingObject
) &&
2688 !hasHadScriptHandlingObject
&&
2689 !nsContentUtils::IsCallerChrome()) {
2690 Throw(aCx
, NS_ERROR_UNEXPECTED
);
2694 JS::Rooted
<JSObject
*> obj(aCx
, WrapNode(aCx
));
2695 MOZ_ASSERT_IF(ChromeOnlyAccess(),
2696 xpc::IsInContentXBLScope(obj
) || !xpc::UseContentXBLScope(js::GetObjectCompartment(obj
)));
2700 already_AddRefed
<nsINode
>
2701 nsINode::CloneNode(bool aDeep
, ErrorResult
& aError
)
2703 nsCOMPtr
<nsINode
> result
;
2704 aError
= nsNodeUtils::CloneNodeImpl(this, aDeep
, getter_AddRefs(result
));
2705 return result
.forget();
2709 nsINode::GetAttributes()
2714 return AsElement()->Attributes();
2718 EventTarget::DispatchEvent(Event
& aEvent
,
2721 bool result
= false;
2722 aRv
= DispatchEvent(&aEvent
, &result
);
2727 nsINode::GetParentElementCrossingShadowRoot() const
2733 if (mParent
->IsElement()) {
2734 return mParent
->AsElement();
2737 ShadowRoot
* shadowRoot
= ShadowRoot::FromNode(mParent
);
2739 nsIContent
* host
= shadowRoot
->GetHost();
2740 MOZ_ASSERT(host
, "ShowRoots should always have a host");
2741 MOZ_ASSERT(host
->IsElement(), "ShadowRoot hosts should always be Elements");
2742 return host
->AsElement();
2749 nsINode::HasBoxQuadsSupport(JSContext
* aCx
, JSObject
* /* unused */)
2751 return xpc::AccessCheck::isChrome(js::GetContextCompartment(aCx
)) ||
2752 Preferences::GetBool("layout.css.getBoxQuads.enabled");
2756 nsINode::GetScopeChainParent() const