Line data Source code
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/. */
5 :
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 "nsDOMClassInfoID.h"
13 : #include "nsIDOMNode.h"
14 : #include "nsIDOMDocument.h"
15 : #include "nsDOMString.h"
16 : #include "txXPathTreeWalker.h"
17 : #include "nsCycleCollectionParticipant.h"
18 : #include "mozilla/dom/XPathResultBinding.h"
19 :
20 : namespace mozilla {
21 : namespace dom {
22 :
23 0 : XPathResult::XPathResult(nsINode* aParent)
24 : : mParent(aParent),
25 : mDocument(nullptr),
26 : mCurrentPos(0),
27 : mResultType(ANY_TYPE),
28 : mInvalidIteratorState(true),
29 : mBooleanResult(false),
30 0 : mNumberResult(0)
31 : {
32 0 : }
33 :
34 0 : XPathResult::XPathResult(const XPathResult &aResult)
35 : : mParent(aResult.mParent),
36 : mResult(aResult.mResult),
37 : mResultNodes(aResult.mResultNodes),
38 : mDocument(aResult.mDocument),
39 : mContextNode(aResult.mContextNode),
40 : mCurrentPos(0),
41 0 : mResultType(aResult.mResultType),
42 0 : mInvalidIteratorState(aResult.mInvalidIteratorState)
43 : {
44 0 : if (mDocument) {
45 0 : mDocument->AddMutationObserver(this);
46 : }
47 0 : }
48 :
49 0 : XPathResult::~XPathResult()
50 : {
51 0 : RemoveObserver();
52 0 : }
53 :
54 : NS_IMPL_CYCLE_COLLECTION_CLASS(XPathResult)
55 :
56 0 : NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(XPathResult)
57 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(XPathResult)
58 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
59 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
60 : {
61 0 : tmp->RemoveObserver();
62 : }
63 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument)
64 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
65 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XPathResult)
66 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
67 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument)
68 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResultNodes)
69 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
70 :
71 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(XPathResult)
72 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(XPathResult)
73 :
74 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(XPathResult)
75 0 : NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
76 0 : NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
77 0 : NS_INTERFACE_MAP_ENTRY(nsIXPathResult)
78 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXPathResult)
79 0 : NS_INTERFACE_MAP_END
80 :
81 : JSObject*
82 0 : XPathResult::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
83 : {
84 0 : return XPathResultBinding::Wrap(aCx, this, aGivenProto);
85 : }
86 :
87 : void
88 0 : XPathResult::RemoveObserver()
89 : {
90 0 : if (mDocument) {
91 0 : mDocument->RemoveMutationObserver(this);
92 : }
93 0 : }
94 :
95 : nsINode*
96 0 : XPathResult::IterateNext(ErrorResult& aRv)
97 : {
98 0 : if (!isIterator()) {
99 0 : aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
100 0 : return nullptr;
101 : }
102 :
103 0 : if (mDocument) {
104 0 : mDocument->FlushPendingNotifications(FlushType::Content);
105 : }
106 :
107 0 : if (mInvalidIteratorState) {
108 0 : aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
109 0 : return nullptr;
110 : }
111 :
112 0 : return mResultNodes.SafeObjectAt(mCurrentPos++);
113 : }
114 :
115 : void
116 0 : XPathResult::NodeWillBeDestroyed(const nsINode* aNode)
117 : {
118 0 : nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
119 : // Set to null to avoid unregistring unnecessarily
120 0 : mDocument = nullptr;
121 0 : Invalidate(aNode->IsNodeOfType(nsINode::eCONTENT) ?
122 0 : static_cast<const nsIContent*>(aNode) : nullptr);
123 0 : }
124 :
125 : void
126 0 : XPathResult::CharacterDataChanged(nsIDocument* aDocument,
127 : nsIContent *aContent,
128 : CharacterDataChangeInfo* aInfo)
129 : {
130 0 : Invalidate(aContent);
131 0 : }
132 :
133 : void
134 0 : XPathResult::AttributeChanged(nsIDocument* aDocument,
135 : Element* aElement,
136 : int32_t aNameSpaceID,
137 : nsIAtom* aAttribute,
138 : int32_t aModType,
139 : const nsAttrValue* aOldValue)
140 : {
141 0 : Invalidate(aElement);
142 0 : }
143 :
144 : void
145 0 : XPathResult::ContentAppended(nsIDocument* aDocument,
146 : nsIContent* aContainer,
147 : nsIContent* aFirstNewContent,
148 : int32_t aNewIndexInContainer)
149 : {
150 0 : Invalidate(aContainer);
151 0 : }
152 :
153 : void
154 0 : XPathResult::ContentInserted(nsIDocument* aDocument,
155 : nsIContent* aContainer,
156 : nsIContent* aChild,
157 : int32_t aIndexInContainer)
158 : {
159 0 : Invalidate(aContainer);
160 0 : }
161 :
162 : void
163 0 : XPathResult::ContentRemoved(nsIDocument* aDocument,
164 : nsIContent* aContainer,
165 : nsIContent* aChild,
166 : int32_t aIndexInContainer,
167 : nsIContent* aPreviousSibling)
168 : {
169 0 : Invalidate(aContainer);
170 0 : }
171 :
172 : nsresult
173 0 : XPathResult::SetExprResult(txAExprResult* aExprResult, uint16_t aResultType,
174 : nsINode* aContextNode)
175 : {
176 0 : MOZ_ASSERT(aExprResult);
177 :
178 0 : if ((isSnapshot(aResultType) || isIterator(aResultType) ||
179 0 : isNode(aResultType)) &&
180 0 : aExprResult->getResultType() != txAExprResult::NODESET) {
181 : // The DOM spec doesn't really say what should happen when reusing an
182 : // XPathResult and an error is thrown. Let's not touch the XPathResult
183 : // in that case.
184 0 : return NS_ERROR_DOM_TYPE_ERR;
185 : }
186 :
187 0 : mResultType = aResultType;
188 0 : mContextNode = do_GetWeakReference(aContextNode);
189 :
190 0 : if (mDocument) {
191 0 : mDocument->RemoveMutationObserver(this);
192 0 : mDocument = nullptr;
193 : }
194 :
195 0 : mResultNodes.Clear();
196 :
197 : // XXX This will keep the recycler alive, should we clear it?
198 0 : mResult = aExprResult;
199 0 : switch (mResultType) {
200 : case BOOLEAN_TYPE:
201 : {
202 0 : mBooleanResult = mResult->booleanValue();
203 0 : break;
204 : }
205 : case NUMBER_TYPE:
206 : {
207 0 : mNumberResult = mResult->numberValue();
208 0 : break;
209 : }
210 : case STRING_TYPE:
211 : {
212 0 : mResult->stringValue(mStringResult);
213 0 : break;
214 : }
215 : default:
216 : {
217 0 : MOZ_ASSERT(isNode() || isIterator() || isSnapshot());
218 : }
219 : }
220 :
221 0 : if (aExprResult->getResultType() == txAExprResult::NODESET) {
222 0 : txNodeSet *nodeSet = static_cast<txNodeSet*>(aExprResult);
223 0 : int32_t i, count = nodeSet->size();
224 0 : for (i = 0; i < count; ++i) {
225 0 : nsINode* node = txXPathNativeNode::getNode(nodeSet->get(i));
226 0 : mResultNodes.AppendObject(node);
227 : }
228 :
229 0 : if (count > 0) {
230 0 : mResult = nullptr;
231 : }
232 : }
233 :
234 0 : if (!isIterator()) {
235 0 : return NS_OK;
236 : }
237 :
238 0 : mInvalidIteratorState = false;
239 :
240 0 : if (mResultNodes.Count() > 0) {
241 : // If we support the document() function in DOM-XPath we need to
242 : // observe all documents that we have resultnodes in.
243 0 : mDocument = mResultNodes[0]->OwnerDoc();
244 0 : NS_ASSERTION(mDocument, "We need a document!");
245 0 : if (mDocument) {
246 0 : mDocument->AddMutationObserver(this);
247 : }
248 : }
249 :
250 0 : return NS_OK;
251 : }
252 :
253 : void
254 0 : XPathResult::Invalidate(const nsIContent* aChangeRoot)
255 : {
256 0 : nsCOMPtr<nsINode> contextNode = do_QueryReferent(mContextNode);
257 0 : if (contextNode && aChangeRoot && aChangeRoot->GetBindingParent()) {
258 : // If context node is in anonymous content, changes to
259 : // non-anonymous content need to invalidate the XPathResult. If
260 : // the changes are happening in a different anonymous trees, no
261 : // invalidation should happen.
262 0 : nsIContent* ctxBindingParent = nullptr;
263 0 : if (contextNode->IsNodeOfType(nsINode::eCONTENT)) {
264 : ctxBindingParent =
265 0 : static_cast<nsIContent*>(contextNode.get())
266 0 : ->GetBindingParent();
267 0 : } else if (contextNode->IsNodeOfType(nsINode::eATTRIBUTE)) {
268 : Element* parent =
269 0 : static_cast<Attr*>(contextNode.get())->GetElement();
270 0 : if (parent) {
271 0 : ctxBindingParent = parent->GetBindingParent();
272 : }
273 : }
274 0 : if (ctxBindingParent != aChangeRoot->GetBindingParent()) {
275 0 : return;
276 : }
277 : }
278 :
279 0 : mInvalidIteratorState = true;
280 : // Make sure nulling out mDocument is the last thing we do.
281 0 : if (mDocument) {
282 0 : mDocument->RemoveMutationObserver(this);
283 0 : mDocument = nullptr;
284 : }
285 : }
286 :
287 : nsresult
288 0 : XPathResult::GetExprResult(txAExprResult** aExprResult)
289 : {
290 0 : if (isIterator() && mInvalidIteratorState) {
291 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
292 : }
293 :
294 0 : if (mResult) {
295 0 : NS_ADDREF(*aExprResult = mResult);
296 :
297 0 : return NS_OK;
298 : }
299 :
300 0 : if (mResultNodes.Count() == 0) {
301 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
302 : }
303 :
304 0 : RefPtr<txNodeSet> nodeSet = new txNodeSet(nullptr);
305 0 : if (!nodeSet) {
306 0 : return NS_ERROR_OUT_OF_MEMORY;
307 : }
308 :
309 0 : uint32_t i, count = mResultNodes.Count();
310 0 : for (i = 0; i < count; ++i) {
311 0 : nsAutoPtr<txXPathNode> node(txXPathNativeNode::createXPathNode(mResultNodes[i]));
312 0 : if (!node) {
313 0 : return NS_ERROR_OUT_OF_MEMORY;
314 : }
315 :
316 0 : nodeSet->append(*node);
317 : }
318 :
319 0 : NS_ADDREF(*aExprResult = nodeSet);
320 :
321 0 : return NS_OK;
322 : }
323 :
324 : nsresult
325 0 : XPathResult::Clone(nsIXPathResult **aResult)
326 : {
327 0 : *aResult = nullptr;
328 :
329 0 : if (isIterator() && mInvalidIteratorState) {
330 0 : return NS_ERROR_DOM_INVALID_STATE_ERR;
331 : }
332 :
333 0 : NS_ADDREF(*aResult = new XPathResult(*this));
334 :
335 0 : return NS_OK;
336 : }
337 :
338 : } // namespace dom
339 : } // namespace mozilla
|