1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "XPathResult.h"
7 #include "txExprResult.h"
10 #include "mozilla/dom/Attr.h"
11 #include "mozilla/dom/Element.h"
12 #include "nsDOMString.h"
13 #include "txXPathTreeWalker.h"
14 #include "nsCycleCollectionParticipant.h"
15 #include "mozilla/dom/XPathResultBinding.h"
17 namespace mozilla::dom
{
19 XPathResult::XPathResult(nsINode
* aParent
)
23 mResultType(ANY_TYPE
),
24 mInvalidIteratorState(true),
25 mBooleanResult(false),
28 XPathResult::XPathResult(const XPathResult
& aResult
)
29 : mParent(aResult
.mParent
),
30 mResult(aResult
.mResult
),
31 mResultNodes(aResult
.mResultNodes
.Clone()),
32 mDocument(aResult
.mDocument
),
33 mContextNode(aResult
.mContextNode
),
35 mResultType(aResult
.mResultType
),
36 mInvalidIteratorState(aResult
.mInvalidIteratorState
),
37 mBooleanResult(aResult
.mBooleanResult
),
38 mNumberResult(aResult
.mNumberResult
),
39 mStringResult(aResult
.mStringResult
) {
41 mDocument
->AddMutationObserver(this);
45 XPathResult::~XPathResult() { RemoveObserver(); }
47 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(XPathResult
)
49 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XPathResult
)
50 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
51 NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent
) { tmp
->RemoveObserver(); }
52 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument
)
53 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
54 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XPathResult
)
55 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent
)
56 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument
)
57 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResultNodes
)
58 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
60 NS_IMPL_CYCLE_COLLECTING_ADDREF(XPathResult
)
61 NS_IMPL_CYCLE_COLLECTING_RELEASE(XPathResult
)
63 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XPathResult
)
64 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
65 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver
)
66 NS_INTERFACE_MAP_ENTRY(nsISupports
)
69 JSObject
* XPathResult::WrapObject(JSContext
* aCx
,
70 JS::Handle
<JSObject
*> aGivenProto
) {
71 return XPathResult_Binding::Wrap(aCx
, this, aGivenProto
);
74 void XPathResult::RemoveObserver() {
76 mDocument
->RemoveMutationObserver(this);
80 nsINode
* XPathResult::IterateNext(ErrorResult
& aRv
) {
82 aRv
.ThrowTypeError("Result is not an iterator");
87 mDocument
->FlushPendingNotifications(FlushType::Content
);
90 if (mInvalidIteratorState
) {
91 aRv
.ThrowInvalidStateError(
92 "The document has been mutated since the result was returned");
96 return mResultNodes
.SafeElementAt(mCurrentPos
++);
99 void XPathResult::NodeWillBeDestroyed(nsINode
* aNode
) {
100 nsCOMPtr
<nsIMutationObserver
> kungFuDeathGrip(this);
101 // Set to null to avoid unregistring unnecessarily
103 Invalidate(aNode
->IsContent() ? aNode
->AsContent() : nullptr);
106 void XPathResult::CharacterDataChanged(nsIContent
* aContent
,
107 const CharacterDataChangeInfo
&) {
108 Invalidate(aContent
);
111 void XPathResult::AttributeChanged(Element
* aElement
, int32_t aNameSpaceID
,
112 nsAtom
* aAttribute
, int32_t aModType
,
113 const nsAttrValue
* aOldValue
) {
114 Invalidate(aElement
);
117 void XPathResult::ContentAppended(nsIContent
* aFirstNewContent
) {
118 Invalidate(aFirstNewContent
->GetParent());
121 void XPathResult::ContentInserted(nsIContent
* aChild
) {
122 Invalidate(aChild
->GetParent());
125 void XPathResult::ContentRemoved(nsIContent
* aChild
,
126 nsIContent
* aPreviousSibling
) {
127 Invalidate(aChild
->GetParent());
130 void XPathResult::SetExprResult(txAExprResult
* aExprResult
,
131 uint16_t aResultType
, nsINode
* aContextNode
,
133 MOZ_ASSERT(aExprResult
);
135 if ((isSnapshot(aResultType
) || isIterator(aResultType
) ||
136 isNode(aResultType
)) &&
137 aExprResult
->getResultType() != txAExprResult::NODESET
) {
138 // The DOM spec doesn't really say what should happen when reusing an
139 // XPathResult and an error is thrown. Let's not touch the XPathResult
141 aRv
.ThrowTypeError("Result type mismatch");
145 mResultType
= aResultType
;
146 mContextNode
= do_GetWeakReference(aContextNode
);
149 mDocument
->RemoveMutationObserver(this);
153 mResultNodes
.Clear();
155 // XXX This will keep the recycler alive, should we clear it?
156 mResult
= aExprResult
;
157 switch (mResultType
) {
159 mBooleanResult
= mResult
->booleanValue();
163 mNumberResult
= mResult
->numberValue();
167 mResult
->stringValue(mStringResult
);
171 MOZ_ASSERT(isNode() || isIterator() || isSnapshot());
175 if (aExprResult
->getResultType() == txAExprResult::NODESET
) {
176 txNodeSet
* nodeSet
= static_cast<txNodeSet
*>(aExprResult
);
177 int32_t i
, count
= nodeSet
->size();
178 for (i
= 0; i
< count
; ++i
) {
179 nsINode
* node
= txXPathNativeNode::getNode(nodeSet
->get(i
));
180 mResultNodes
.AppendElement(node
);
193 mInvalidIteratorState
= false;
195 if (!mResultNodes
.IsEmpty()) {
196 // If we support the document() function in DOM-XPath we need to
197 // observe all documents that we have resultnodes in.
198 mDocument
= mResultNodes
[0]->OwnerDoc();
199 NS_ASSERTION(mDocument
, "We need a document!");
201 mDocument
->AddMutationObserver(this);
206 void XPathResult::Invalidate(const nsIContent
* aChangeRoot
) {
207 nsCOMPtr
<nsINode
> contextNode
= do_QueryReferent(mContextNode
);
208 // If the changes are happening in a different anonymous trees, no
209 // invalidation should happen.
210 if (contextNode
&& aChangeRoot
&&
211 !nsContentUtils::IsInSameAnonymousTree(contextNode
, aChangeRoot
)) {
215 mInvalidIteratorState
= true;
216 // Make sure nulling out mDocument is the last thing we do.
218 mDocument
->RemoveMutationObserver(this);
223 nsresult
XPathResult::GetExprResult(txAExprResult
** aExprResult
) {
224 if (isIterator() && mInvalidIteratorState
) {
225 return NS_ERROR_DOM_INVALID_STATE_ERR
;
229 NS_ADDREF(*aExprResult
= mResult
);
234 if (mResultNodes
.IsEmpty()) {
235 return NS_ERROR_DOM_INVALID_STATE_ERR
;
238 RefPtr
<txNodeSet
> nodeSet
= new txNodeSet(nullptr);
239 uint32_t i
, count
= mResultNodes
.Length();
240 for (i
= 0; i
< count
; ++i
) {
241 UniquePtr
<txXPathNode
> node(
242 txXPathNativeNode::createXPathNode(mResultNodes
[i
]));
244 return NS_ERROR_OUT_OF_MEMORY
;
247 nodeSet
->append(*node
);
250 NS_ADDREF(*aExprResult
= nodeSet
);
255 already_AddRefed
<XPathResult
> XPathResult::Clone(ErrorResult
& aError
) {
256 if (isIterator() && mInvalidIteratorState
) {
257 aError
= NS_ERROR_DOM_INVALID_STATE_ERR
;
261 return do_AddRef(new XPathResult(*this));
264 } // namespace mozilla::dom