Bug 1874684 - Part 4: Prefer const references instead of copying Instant values....
[gecko.git] / dom / xslt / xpath / XPathResult.cpp
blob091a83528ff778cda523f8e41505399dc9494ee1
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"
8 #include "txNodeSet.h"
9 #include "nsError.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)
20 : mParent(aParent),
21 mDocument(nullptr),
22 mCurrentPos(0),
23 mResultType(ANY_TYPE),
24 mInvalidIteratorState(true),
25 mBooleanResult(false),
26 mNumberResult(0) {}
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),
34 mCurrentPos(0),
35 mResultType(aResult.mResultType),
36 mInvalidIteratorState(aResult.mInvalidIteratorState),
37 mBooleanResult(aResult.mBooleanResult),
38 mNumberResult(aResult.mNumberResult),
39 mStringResult(aResult.mStringResult) {
40 if (mDocument) {
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)
67 NS_INTERFACE_MAP_END
69 JSObject* XPathResult::WrapObject(JSContext* aCx,
70 JS::Handle<JSObject*> aGivenProto) {
71 return XPathResult_Binding::Wrap(aCx, this, aGivenProto);
74 void XPathResult::RemoveObserver() {
75 if (mDocument) {
76 mDocument->RemoveMutationObserver(this);
80 nsINode* XPathResult::IterateNext(ErrorResult& aRv) {
81 if (!isIterator()) {
82 aRv.ThrowTypeError("Result is not an iterator");
83 return nullptr;
86 if (mDocument) {
87 mDocument->FlushPendingNotifications(FlushType::Content);
90 if (mInvalidIteratorState) {
91 aRv.ThrowInvalidStateError(
92 "The document has been mutated since the result was returned");
93 return nullptr;
96 return mResultNodes.SafeElementAt(mCurrentPos++);
99 void XPathResult::NodeWillBeDestroyed(nsINode* aNode) {
100 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
101 // Set to null to avoid unregistring unnecessarily
102 mDocument = nullptr;
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,
132 ErrorResult& aRv) {
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
140 // in that case.
141 aRv.ThrowTypeError("Result type mismatch");
142 return;
145 mResultType = aResultType;
146 mContextNode = do_GetWeakReference(aContextNode);
148 if (mDocument) {
149 mDocument->RemoveMutationObserver(this);
150 mDocument = nullptr;
153 mResultNodes.Clear();
155 // XXX This will keep the recycler alive, should we clear it?
156 mResult = aExprResult;
157 switch (mResultType) {
158 case BOOLEAN_TYPE: {
159 mBooleanResult = mResult->booleanValue();
160 break;
162 case NUMBER_TYPE: {
163 mNumberResult = mResult->numberValue();
164 break;
166 case STRING_TYPE: {
167 mResult->stringValue(mStringResult);
168 break;
170 default: {
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);
183 if (count > 0) {
184 mResult = nullptr;
188 if (!isIterator()) {
189 return;
192 mCurrentPos = 0;
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!");
200 if (mDocument) {
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)) {
212 return;
215 mInvalidIteratorState = true;
216 // Make sure nulling out mDocument is the last thing we do.
217 if (mDocument) {
218 mDocument->RemoveMutationObserver(this);
219 mDocument = nullptr;
223 nsresult XPathResult::GetExprResult(txAExprResult** aExprResult) {
224 if (isIterator() && mInvalidIteratorState) {
225 return NS_ERROR_DOM_INVALID_STATE_ERR;
228 if (mResult) {
229 NS_ADDREF(*aExprResult = mResult);
231 return NS_OK;
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]));
243 if (!node) {
244 return NS_ERROR_OUT_OF_MEMORY;
247 nodeSet->append(*node);
250 NS_ADDREF(*aExprResult = nodeSet);
252 return NS_OK;
255 already_AddRefed<XPathResult> XPathResult::Clone(ErrorResult& aError) {
256 if (isIterator() && mInvalidIteratorState) {
257 aError = NS_ERROR_DOM_INVALID_STATE_ERR;
258 return nullptr;
261 return do_AddRef(new XPathResult(*this));
264 } // namespace mozilla::dom