1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is TransforMiiX XSLT processor code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 2001
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Peter Van der Beken <peterv@propagandism.org>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either the GNU General Public License Version 2 or later (the "GPL"), or
27 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 : #include "nsXPathResult.h"
40 : #include "txExprResult.h"
41 : #include "txNodeSet.h"
42 : #include "nsDOMError.h"
43 : #include "mozilla/dom/Element.h"
44 : #include "nsIAttribute.h"
45 : #include "nsDOMClassInfoID.h"
46 : #include "nsIDOMNode.h"
47 : #include "nsIDOMDocument.h"
48 : #include "nsDOMString.h"
49 : #include "txXPathTreeWalker.h"
50 : #include "nsCycleCollectionParticipant.h"
51 :
52 : using namespace mozilla::dom;
53 :
54 42 : nsXPathResult::nsXPathResult() : mDocument(nsnull),
55 : mCurrentPos(0),
56 : mResultType(ANY_TYPE),
57 : mInvalidIteratorState(true),
58 : mBooleanResult(false),
59 42 : mNumberResult(0)
60 : {
61 42 : }
62 :
63 0 : nsXPathResult::nsXPathResult(const nsXPathResult &aResult)
64 : : mResult(aResult.mResult),
65 : mResultNodes(aResult.mResultNodes),
66 : mDocument(aResult.mDocument),
67 : mCurrentPos(0),
68 : mResultType(aResult.mResultType),
69 : mContextNode(aResult.mContextNode),
70 0 : mInvalidIteratorState(aResult.mInvalidIteratorState)
71 : {
72 0 : if (mDocument) {
73 0 : mDocument->AddMutationObserver(this);
74 : }
75 0 : }
76 :
77 84 : nsXPathResult::~nsXPathResult()
78 : {
79 42 : RemoveObserver();
80 42 : }
81 :
82 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsXPathResult)
83 26 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXPathResult)
84 : {
85 26 : tmp->RemoveObserver();
86 : }
87 26 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocument)
88 26 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
89 26 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXPathResult)
90 26 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocument)
91 26 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mResultNodes)
92 26 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
93 :
94 278 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXPathResult)
95 320 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXPathResult)
96 :
97 : DOMCI_DATA(XPathResult, nsXPathResult)
98 :
99 770 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXPathResult)
100 420 : NS_INTERFACE_MAP_ENTRY(nsIDOMXPathResult)
101 378 : NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
102 378 : NS_INTERFACE_MAP_ENTRY(nsIXPathResult)
103 336 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMXPathResult)
104 210 : NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XPathResult)
105 168 : NS_INTERFACE_MAP_END
106 :
107 : void
108 68 : nsXPathResult::RemoveObserver()
109 : {
110 68 : if (mDocument) {
111 0 : mDocument->RemoveMutationObserver(this);
112 : }
113 68 : }
114 :
115 : NS_IMETHODIMP
116 0 : nsXPathResult::GetResultType(PRUint16 *aResultType)
117 : {
118 0 : *aResultType = mResultType;
119 :
120 0 : return NS_OK;
121 : }
122 :
123 : NS_IMETHODIMP
124 0 : nsXPathResult::GetNumberValue(double *aNumberValue)
125 : {
126 0 : if (mResultType != NUMBER_TYPE) {
127 0 : return NS_ERROR_DOM_TYPE_ERR;
128 : }
129 :
130 0 : *aNumberValue = mNumberResult;
131 :
132 0 : return NS_OK;
133 : }
134 :
135 : NS_IMETHODIMP
136 0 : nsXPathResult::GetStringValue(nsAString &aStringValue)
137 : {
138 0 : if (mResultType != STRING_TYPE) {
139 0 : return NS_ERROR_DOM_TYPE_ERR;
140 : }
141 :
142 0 : aStringValue = mStringResult;
143 :
144 0 : return NS_OK;
145 : }
146 :
147 : NS_IMETHODIMP
148 0 : nsXPathResult::GetBooleanValue(bool *aBooleanValue)
149 : {
150 0 : if (mResultType != BOOLEAN_TYPE) {
151 0 : return NS_ERROR_DOM_TYPE_ERR;
152 : }
153 :
154 0 : *aBooleanValue = mBooleanResult;
155 :
156 0 : return NS_OK;
157 : }
158 :
159 : NS_IMETHODIMP
160 42 : nsXPathResult::GetSingleNodeValue(nsIDOMNode **aSingleNodeValue)
161 : {
162 42 : if (!isNode()) {
163 0 : return NS_ERROR_DOM_TYPE_ERR;
164 : }
165 :
166 42 : if (mResultNodes.Count() > 0) {
167 42 : NS_ADDREF(*aSingleNodeValue = mResultNodes[0]);
168 : }
169 : else {
170 0 : *aSingleNodeValue = nsnull;
171 : }
172 :
173 42 : return NS_OK;
174 : }
175 :
176 : NS_IMETHODIMP
177 0 : nsXPathResult::GetInvalidIteratorState(bool *aInvalidIteratorState)
178 : {
179 0 : *aInvalidIteratorState = isIterator() && mInvalidIteratorState;
180 :
181 0 : return NS_OK;
182 : }
183 :
184 : NS_IMETHODIMP
185 0 : nsXPathResult::GetSnapshotLength(PRUint32 *aSnapshotLength)
186 : {
187 0 : if (!isSnapshot()) {
188 0 : return NS_ERROR_DOM_TYPE_ERR;
189 : }
190 :
191 0 : *aSnapshotLength = (PRUint32)mResultNodes.Count();
192 :
193 0 : return NS_OK;
194 : }
195 :
196 : NS_IMETHODIMP
197 0 : nsXPathResult::IterateNext(nsIDOMNode **aResult)
198 : {
199 0 : if (!isIterator()) {
200 0 : return NS_ERROR_DOM_TYPE_ERR;
201 : }
202 :
203 0 : if (mDocument) {
204 0 : mDocument->FlushPendingNotifications(Flush_Content);
205 : }
206 :
207 0 : if (mInvalidIteratorState) {
208 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
209 : }
210 :
211 0 : if (mCurrentPos < (PRUint32)mResultNodes.Count()) {
212 0 : NS_ADDREF(*aResult = mResultNodes[mCurrentPos++]);
213 : }
214 : else {
215 0 : *aResult = nsnull;
216 : }
217 :
218 0 : return NS_OK;
219 : }
220 :
221 : NS_IMETHODIMP
222 0 : nsXPathResult::SnapshotItem(PRUint32 aIndex, nsIDOMNode **aResult)
223 : {
224 0 : if (!isSnapshot()) {
225 0 : return NS_ERROR_DOM_TYPE_ERR;
226 : }
227 :
228 0 : NS_IF_ADDREF(*aResult = mResultNodes.SafeObjectAt(aIndex));
229 :
230 0 : return NS_OK;
231 : }
232 :
233 : void
234 0 : nsXPathResult::NodeWillBeDestroyed(const nsINode* aNode)
235 : {
236 0 : nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
237 : // Set to null to avoid unregistring unnecessarily
238 0 : mDocument = nsnull;
239 0 : Invalidate(aNode->IsNodeOfType(nsINode::eCONTENT) ?
240 0 : static_cast<const nsIContent*>(aNode) : nsnull);
241 0 : }
242 :
243 : void
244 0 : nsXPathResult::CharacterDataChanged(nsIDocument* aDocument,
245 : nsIContent *aContent,
246 : CharacterDataChangeInfo* aInfo)
247 : {
248 0 : Invalidate(aContent);
249 0 : }
250 :
251 : void
252 0 : nsXPathResult::AttributeChanged(nsIDocument* aDocument,
253 : Element* aElement,
254 : PRInt32 aNameSpaceID,
255 : nsIAtom* aAttribute,
256 : PRInt32 aModType)
257 : {
258 0 : Invalidate(aElement);
259 0 : }
260 :
261 : void
262 0 : nsXPathResult::ContentAppended(nsIDocument* aDocument,
263 : nsIContent* aContainer,
264 : nsIContent* aFirstNewContent,
265 : PRInt32 aNewIndexInContainer)
266 : {
267 0 : Invalidate(aContainer);
268 0 : }
269 :
270 : void
271 0 : nsXPathResult::ContentInserted(nsIDocument* aDocument,
272 : nsIContent* aContainer,
273 : nsIContent* aChild,
274 : PRInt32 aIndexInContainer)
275 : {
276 0 : Invalidate(aContainer);
277 0 : }
278 :
279 : void
280 0 : nsXPathResult::ContentRemoved(nsIDocument* aDocument,
281 : nsIContent* aContainer,
282 : nsIContent* aChild,
283 : PRInt32 aIndexInContainer,
284 : nsIContent* aPreviousSibling)
285 : {
286 0 : Invalidate(aContainer);
287 0 : }
288 :
289 : nsresult
290 42 : nsXPathResult::SetExprResult(txAExprResult* aExprResult, PRUint16 aResultType,
291 : nsINode* aContextNode)
292 : {
293 126 : if ((isSnapshot(aResultType) || isIterator(aResultType) ||
294 42 : isNode(aResultType)) &&
295 42 : aExprResult->getResultType() != txAExprResult::NODESET) {
296 : // The DOM spec doesn't really say what should happen when reusing an
297 : // XPathResult and an error is thrown. Let's not touch the XPathResult
298 : // in that case.
299 0 : return NS_ERROR_DOM_TYPE_ERR;
300 : }
301 :
302 42 : mResultType = aResultType;
303 42 : mContextNode = do_GetWeakReference(aContextNode);
304 :
305 42 : if (mDocument) {
306 0 : mDocument->RemoveMutationObserver(this);
307 0 : mDocument = nsnull;
308 : }
309 :
310 42 : mResultNodes.Clear();
311 :
312 : // XXX This will keep the recycler alive, should we clear it?
313 42 : mResult = aExprResult;
314 42 : mBooleanResult = mResult->booleanValue();
315 42 : mNumberResult = mResult->numberValue();
316 42 : mResult->stringValue(mStringResult);
317 :
318 42 : if (aExprResult && aExprResult->getResultType() == txAExprResult::NODESET) {
319 42 : txNodeSet *nodeSet = static_cast<txNodeSet*>(aExprResult);
320 84 : nsCOMPtr<nsIDOMNode> node;
321 42 : PRInt32 i, count = nodeSet->size();
322 84 : for (i = 0; i < count; ++i) {
323 42 : txXPathNativeNode::getNode(nodeSet->get(i), getter_AddRefs(node));
324 42 : if (node) {
325 42 : mResultNodes.AppendObject(node);
326 : }
327 : }
328 :
329 42 : if (count > 0) {
330 42 : mResult = nsnull;
331 : }
332 : }
333 :
334 42 : if (!isIterator()) {
335 42 : return NS_OK;
336 : }
337 :
338 0 : mInvalidIteratorState = false;
339 :
340 0 : if (mResultNodes.Count() > 0) {
341 : // If we support the document() function in DOM-XPath we need to
342 : // observe all documents that we have resultnodes in.
343 0 : nsCOMPtr<nsIDOMDocument> document;
344 0 : mResultNodes[0]->GetOwnerDocument(getter_AddRefs(document));
345 0 : if (document) {
346 0 : mDocument = do_QueryInterface(document);
347 : }
348 : else {
349 0 : mDocument = do_QueryInterface(mResultNodes[0]);
350 : }
351 :
352 0 : NS_ASSERTION(mDocument, "We need a document!");
353 0 : if (mDocument) {
354 0 : mDocument->AddMutationObserver(this);
355 : }
356 : }
357 :
358 0 : return NS_OK;
359 : }
360 :
361 : void
362 0 : nsXPathResult::Invalidate(const nsIContent* aChangeRoot)
363 : {
364 0 : nsCOMPtr<nsINode> contextNode = do_QueryReferent(mContextNode);
365 0 : if (contextNode && aChangeRoot && aChangeRoot->GetBindingParent()) {
366 : // If context node is in anonymous content, changes to
367 : // non-anonymous content need to invalidate the XPathResult. If
368 : // the changes are happening in a different anonymous trees, no
369 : // invalidation should happen.
370 0 : nsIContent* ctxBindingParent = nsnull;
371 0 : if (contextNode->IsNodeOfType(nsINode::eCONTENT)) {
372 : ctxBindingParent =
373 0 : static_cast<nsIContent*>(contextNode.get())
374 0 : ->GetBindingParent();
375 0 : } else if (contextNode->IsNodeOfType(nsINode::eATTRIBUTE)) {
376 : nsIContent* parent =
377 0 : static_cast<nsIAttribute*>(contextNode.get())->GetContent();
378 0 : if (parent) {
379 0 : ctxBindingParent = parent->GetBindingParent();
380 : }
381 : }
382 0 : if (ctxBindingParent != aChangeRoot->GetBindingParent()) {
383 : return;
384 : }
385 : }
386 :
387 0 : mInvalidIteratorState = true;
388 : // Make sure nulling out mDocument is the last thing we do.
389 0 : if (mDocument) {
390 0 : mDocument->RemoveMutationObserver(this);
391 0 : mDocument = nsnull;
392 : }
393 : }
394 :
395 : nsresult
396 0 : nsXPathResult::GetExprResult(txAExprResult** aExprResult)
397 : {
398 0 : if (isIterator() && mInvalidIteratorState) {
399 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
400 : }
401 :
402 0 : if (mResult) {
403 0 : NS_ADDREF(*aExprResult = mResult);
404 :
405 0 : return NS_OK;
406 : }
407 :
408 0 : if (mResultNodes.Count() == 0) {
409 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
410 : }
411 :
412 0 : nsRefPtr<txNodeSet> nodeSet = new txNodeSet(nsnull);
413 0 : if (!nodeSet) {
414 0 : return NS_ERROR_OUT_OF_MEMORY;
415 : }
416 :
417 0 : PRUint32 i, count = mResultNodes.Count();
418 0 : for (i = 0; i < count; ++i) {
419 0 : nsAutoPtr<txXPathNode> node(txXPathNativeNode::createXPathNode(mResultNodes[i]));
420 0 : if (!node) {
421 0 : return NS_ERROR_OUT_OF_MEMORY;
422 : }
423 :
424 0 : nodeSet->append(*node);
425 : }
426 :
427 0 : NS_ADDREF(*aExprResult = nodeSet);
428 :
429 0 : return NS_OK;
430 : }
431 :
432 : nsresult
433 0 : nsXPathResult::Clone(nsIXPathResult **aResult)
434 : {
435 0 : *aResult = nsnull;
436 :
437 0 : if (isIterator() && mInvalidIteratorState) {
438 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
439 : }
440 :
441 0 : nsCOMPtr<nsIXPathResult> result = new nsXPathResult(*this);
442 0 : if (!result) {
443 0 : return NS_ERROR_OUT_OF_MEMORY;
444 : }
445 :
446 0 : result.swap(*aResult);
447 :
448 0 : return NS_OK;
449 4392 : }
|