Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 "nsXPathResult.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 "nsDOMClassInfoID.h"
13 #include "nsIDOMNode.h"
14 #include "nsIDOMDocument.h"
15 #include "nsDOMString.h"
16 #include "txXPathTreeWalker.h"
17 #include "nsCycleCollectionParticipant.h"
19 using namespace mozilla::dom;
21 nsXPathResult::nsXPathResult() : mDocument(nullptr),
22 mCurrentPos(0),
23 mResultType(ANY_TYPE),
24 mInvalidIteratorState(true),
25 mBooleanResult(false),
26 mNumberResult(0)
27 {
28 }
30 nsXPathResult::nsXPathResult(const nsXPathResult &aResult)
31 : mResult(aResult.mResult),
32 mResultNodes(aResult.mResultNodes),
33 mDocument(aResult.mDocument),
34 mCurrentPos(0),
35 mResultType(aResult.mResultType),
36 mContextNode(aResult.mContextNode),
37 mInvalidIteratorState(aResult.mInvalidIteratorState)
38 {
39 if (mDocument) {
40 mDocument->AddMutationObserver(this);
41 }
42 }
44 nsXPathResult::~nsXPathResult()
45 {
46 RemoveObserver();
47 }
49 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXPathResult)
51 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXPathResult)
52 {
53 tmp->RemoveObserver();
54 }
55 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
56 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
57 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXPathResult)
58 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
59 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResultNodes)
60 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
62 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXPathResult)
63 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXPathResult)
65 DOMCI_DATA(XPathResult, nsXPathResult)
67 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXPathResult)
68 NS_INTERFACE_MAP_ENTRY(nsIDOMXPathResult)
69 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
70 NS_INTERFACE_MAP_ENTRY(nsIXPathResult)
71 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMXPathResult)
72 NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XPathResult)
73 NS_INTERFACE_MAP_END
75 void
76 nsXPathResult::RemoveObserver()
77 {
78 if (mDocument) {
79 mDocument->RemoveMutationObserver(this);
80 }
81 }
83 NS_IMETHODIMP
84 nsXPathResult::GetResultType(uint16_t *aResultType)
85 {
86 *aResultType = mResultType;
88 return NS_OK;
89 }
91 NS_IMETHODIMP
92 nsXPathResult::GetNumberValue(double *aNumberValue)
93 {
94 if (mResultType != NUMBER_TYPE) {
95 return NS_ERROR_DOM_TYPE_ERR;
96 }
98 *aNumberValue = mNumberResult;
100 return NS_OK;
101 }
103 NS_IMETHODIMP
104 nsXPathResult::GetStringValue(nsAString &aStringValue)
105 {
106 if (mResultType != STRING_TYPE) {
107 return NS_ERROR_DOM_TYPE_ERR;
108 }
110 aStringValue = mStringResult;
112 return NS_OK;
113 }
115 NS_IMETHODIMP
116 nsXPathResult::GetBooleanValue(bool *aBooleanValue)
117 {
118 if (mResultType != BOOLEAN_TYPE) {
119 return NS_ERROR_DOM_TYPE_ERR;
120 }
122 *aBooleanValue = mBooleanResult;
124 return NS_OK;
125 }
127 NS_IMETHODIMP
128 nsXPathResult::GetSingleNodeValue(nsIDOMNode **aSingleNodeValue)
129 {
130 if (!isNode()) {
131 return NS_ERROR_DOM_TYPE_ERR;
132 }
134 if (mResultNodes.Count() > 0) {
135 NS_ADDREF(*aSingleNodeValue = mResultNodes[0]);
136 }
137 else {
138 *aSingleNodeValue = nullptr;
139 }
141 return NS_OK;
142 }
144 NS_IMETHODIMP
145 nsXPathResult::GetInvalidIteratorState(bool *aInvalidIteratorState)
146 {
147 *aInvalidIteratorState = isIterator() && mInvalidIteratorState;
149 return NS_OK;
150 }
152 NS_IMETHODIMP
153 nsXPathResult::GetSnapshotLength(uint32_t *aSnapshotLength)
154 {
155 if (!isSnapshot()) {
156 return NS_ERROR_DOM_TYPE_ERR;
157 }
159 *aSnapshotLength = (uint32_t)mResultNodes.Count();
161 return NS_OK;
162 }
164 NS_IMETHODIMP
165 nsXPathResult::IterateNext(nsIDOMNode **aResult)
166 {
167 if (!isIterator()) {
168 return NS_ERROR_DOM_TYPE_ERR;
169 }
171 if (mDocument) {
172 mDocument->FlushPendingNotifications(Flush_Content);
173 }
175 if (mInvalidIteratorState) {
176 return NS_ERROR_DOM_INVALID_STATE_ERR;
177 }
179 if (mCurrentPos < (uint32_t)mResultNodes.Count()) {
180 NS_ADDREF(*aResult = mResultNodes[mCurrentPos++]);
181 }
182 else {
183 *aResult = nullptr;
184 }
186 return NS_OK;
187 }
189 NS_IMETHODIMP
190 nsXPathResult::SnapshotItem(uint32_t aIndex, nsIDOMNode **aResult)
191 {
192 if (!isSnapshot()) {
193 return NS_ERROR_DOM_TYPE_ERR;
194 }
196 NS_IF_ADDREF(*aResult = mResultNodes.SafeObjectAt(aIndex));
198 return NS_OK;
199 }
201 void
202 nsXPathResult::NodeWillBeDestroyed(const nsINode* aNode)
203 {
204 nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
205 // Set to null to avoid unregistring unnecessarily
206 mDocument = nullptr;
207 Invalidate(aNode->IsNodeOfType(nsINode::eCONTENT) ?
208 static_cast<const nsIContent*>(aNode) : nullptr);
209 }
211 void
212 nsXPathResult::CharacterDataChanged(nsIDocument* aDocument,
213 nsIContent *aContent,
214 CharacterDataChangeInfo* aInfo)
215 {
216 Invalidate(aContent);
217 }
219 void
220 nsXPathResult::AttributeChanged(nsIDocument* aDocument,
221 Element* aElement,
222 int32_t aNameSpaceID,
223 nsIAtom* aAttribute,
224 int32_t aModType)
225 {
226 Invalidate(aElement);
227 }
229 void
230 nsXPathResult::ContentAppended(nsIDocument* aDocument,
231 nsIContent* aContainer,
232 nsIContent* aFirstNewContent,
233 int32_t aNewIndexInContainer)
234 {
235 Invalidate(aContainer);
236 }
238 void
239 nsXPathResult::ContentInserted(nsIDocument* aDocument,
240 nsIContent* aContainer,
241 nsIContent* aChild,
242 int32_t aIndexInContainer)
243 {
244 Invalidate(aContainer);
245 }
247 void
248 nsXPathResult::ContentRemoved(nsIDocument* aDocument,
249 nsIContent* aContainer,
250 nsIContent* aChild,
251 int32_t aIndexInContainer,
252 nsIContent* aPreviousSibling)
253 {
254 Invalidate(aContainer);
255 }
257 nsresult
258 nsXPathResult::SetExprResult(txAExprResult* aExprResult, uint16_t aResultType,
259 nsINode* aContextNode)
260 {
261 MOZ_ASSERT(aExprResult);
263 if ((isSnapshot(aResultType) || isIterator(aResultType) ||
264 isNode(aResultType)) &&
265 aExprResult->getResultType() != txAExprResult::NODESET) {
266 // The DOM spec doesn't really say what should happen when reusing an
267 // XPathResult and an error is thrown. Let's not touch the XPathResult
268 // in that case.
269 return NS_ERROR_DOM_TYPE_ERR;
270 }
272 mResultType = aResultType;
273 mContextNode = do_GetWeakReference(aContextNode);
275 if (mDocument) {
276 mDocument->RemoveMutationObserver(this);
277 mDocument = nullptr;
278 }
280 mResultNodes.Clear();
282 // XXX This will keep the recycler alive, should we clear it?
283 mResult = aExprResult;
284 switch (mResultType) {
285 case BOOLEAN_TYPE:
286 {
287 mBooleanResult = mResult->booleanValue();
288 break;
289 }
290 case NUMBER_TYPE:
291 {
292 mNumberResult = mResult->numberValue();
293 break;
294 }
295 case STRING_TYPE:
296 {
297 mResult->stringValue(mStringResult);
298 break;
299 }
300 default:
301 {
302 MOZ_ASSERT(isNode() || isIterator() || isSnapshot());
303 }
304 }
306 if (aExprResult->getResultType() == txAExprResult::NODESET) {
307 txNodeSet *nodeSet = static_cast<txNodeSet*>(aExprResult);
308 nsCOMPtr<nsIDOMNode> node;
309 int32_t i, count = nodeSet->size();
310 for (i = 0; i < count; ++i) {
311 txXPathNativeNode::getNode(nodeSet->get(i), getter_AddRefs(node));
312 if (node) {
313 mResultNodes.AppendObject(node);
314 }
315 }
317 if (count > 0) {
318 mResult = nullptr;
319 }
320 }
322 if (!isIterator()) {
323 return NS_OK;
324 }
326 mInvalidIteratorState = false;
328 if (mResultNodes.Count() > 0) {
329 // If we support the document() function in DOM-XPath we need to
330 // observe all documents that we have resultnodes in.
331 nsCOMPtr<nsIDOMDocument> document;
332 mResultNodes[0]->GetOwnerDocument(getter_AddRefs(document));
333 if (document) {
334 mDocument = do_QueryInterface(document);
335 }
336 else {
337 mDocument = do_QueryInterface(mResultNodes[0]);
338 }
340 NS_ASSERTION(mDocument, "We need a document!");
341 if (mDocument) {
342 mDocument->AddMutationObserver(this);
343 }
344 }
346 return NS_OK;
347 }
349 void
350 nsXPathResult::Invalidate(const nsIContent* aChangeRoot)
351 {
352 nsCOMPtr<nsINode> contextNode = do_QueryReferent(mContextNode);
353 if (contextNode && aChangeRoot && aChangeRoot->GetBindingParent()) {
354 // If context node is in anonymous content, changes to
355 // non-anonymous content need to invalidate the XPathResult. If
356 // the changes are happening in a different anonymous trees, no
357 // invalidation should happen.
358 nsIContent* ctxBindingParent = nullptr;
359 if (contextNode->IsNodeOfType(nsINode::eCONTENT)) {
360 ctxBindingParent =
361 static_cast<nsIContent*>(contextNode.get())
362 ->GetBindingParent();
363 } else if (contextNode->IsNodeOfType(nsINode::eATTRIBUTE)) {
364 Element* parent =
365 static_cast<Attr*>(contextNode.get())->GetElement();
366 if (parent) {
367 ctxBindingParent = parent->GetBindingParent();
368 }
369 }
370 if (ctxBindingParent != aChangeRoot->GetBindingParent()) {
371 return;
372 }
373 }
375 mInvalidIteratorState = true;
376 // Make sure nulling out mDocument is the last thing we do.
377 if (mDocument) {
378 mDocument->RemoveMutationObserver(this);
379 mDocument = nullptr;
380 }
381 }
383 nsresult
384 nsXPathResult::GetExprResult(txAExprResult** aExprResult)
385 {
386 if (isIterator() && mInvalidIteratorState) {
387 return NS_ERROR_DOM_INVALID_STATE_ERR;
388 }
390 if (mResult) {
391 NS_ADDREF(*aExprResult = mResult);
393 return NS_OK;
394 }
396 if (mResultNodes.Count() == 0) {
397 return NS_ERROR_DOM_INVALID_STATE_ERR;
398 }
400 nsRefPtr<txNodeSet> nodeSet = new txNodeSet(nullptr);
401 if (!nodeSet) {
402 return NS_ERROR_OUT_OF_MEMORY;
403 }
405 uint32_t i, count = mResultNodes.Count();
406 for (i = 0; i < count; ++i) {
407 nsAutoPtr<txXPathNode> node(txXPathNativeNode::createXPathNode(mResultNodes[i]));
408 if (!node) {
409 return NS_ERROR_OUT_OF_MEMORY;
410 }
412 nodeSet->append(*node);
413 }
415 NS_ADDREF(*aExprResult = nodeSet);
417 return NS_OK;
418 }
420 nsresult
421 nsXPathResult::Clone(nsIXPathResult **aResult)
422 {
423 *aResult = nullptr;
425 if (isIterator() && mInvalidIteratorState) {
426 return NS_ERROR_DOM_INVALID_STATE_ERR;
427 }
429 nsCOMPtr<nsIXPathResult> result = new nsXPathResult(*this);
430 if (!result) {
431 return NS_ERROR_OUT_OF_MEMORY;
432 }
434 result.swap(*aResult);
436 return NS_OK;
437 }