|
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 "nsCOMPtr.h" |
|
7 #include "nsAutoPtr.h" |
|
8 #include "nsIDOMDocument.h" |
|
9 #include "nsIDOMNode.h" |
|
10 #include "nsIDOMElement.h" |
|
11 #include "nsIDOMEvent.h" |
|
12 #include "nsIDOMXPathNSResolver.h" |
|
13 #include "nsIDocument.h" |
|
14 #include "nsIContent.h" |
|
15 #include "nsComponentManagerUtils.h" |
|
16 #include "nsGkAtoms.h" |
|
17 #include "nsIURI.h" |
|
18 #include "nsIArray.h" |
|
19 #include "nsIScriptContext.h" |
|
20 #include "nsArrayUtils.h" |
|
21 #include "nsPIDOMWindow.h" |
|
22 #include "nsXULContentUtils.h" |
|
23 #include "nsXMLHttpRequest.h" |
|
24 |
|
25 #include "nsXULTemplateQueryProcessorXML.h" |
|
26 #include "nsXULTemplateResultXML.h" |
|
27 #include "nsXULSortService.h" |
|
28 |
|
29 using namespace mozilla::dom; |
|
30 |
|
31 NS_IMPL_ISUPPORTS(nsXMLQuery, nsXMLQuery) |
|
32 |
|
33 //---------------------------------------------------------------------- |
|
34 // |
|
35 // nsXULTemplateResultSetXML |
|
36 // |
|
37 |
|
38 NS_IMPL_ISUPPORTS(nsXULTemplateResultSetXML, nsISimpleEnumerator) |
|
39 |
|
40 NS_IMETHODIMP |
|
41 nsXULTemplateResultSetXML::HasMoreElements(bool *aResult) |
|
42 { |
|
43 // if GetSnapshotLength failed, then the return type was not a set of |
|
44 // nodes, so just return false in this case. |
|
45 uint32_t length; |
|
46 if (NS_SUCCEEDED(mResults->GetSnapshotLength(&length))) |
|
47 *aResult = (mPosition < length); |
|
48 else |
|
49 *aResult = false; |
|
50 |
|
51 return NS_OK; |
|
52 } |
|
53 |
|
54 NS_IMETHODIMP |
|
55 nsXULTemplateResultSetXML::GetNext(nsISupports **aResult) |
|
56 { |
|
57 nsCOMPtr<nsIDOMNode> node; |
|
58 nsresult rv = mResults->SnapshotItem(mPosition, getter_AddRefs(node)); |
|
59 NS_ENSURE_SUCCESS(rv, rv); |
|
60 |
|
61 nsXULTemplateResultXML* result = |
|
62 new nsXULTemplateResultXML(mQuery, node, mBindingSet); |
|
63 NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY); |
|
64 |
|
65 ++mPosition; |
|
66 *aResult = result; |
|
67 NS_ADDREF(result); |
|
68 return NS_OK; |
|
69 } |
|
70 |
|
71 |
|
72 //---------------------------------------------------------------------- |
|
73 // |
|
74 // nsXULTemplateQueryProcessorXML |
|
75 // |
|
76 |
|
77 static PLDHashOperator |
|
78 TraverseRuleToBindingsMap(nsISupports* aKey, nsXMLBindingSet* aMatch, void* aContext) |
|
79 { |
|
80 nsCycleCollectionTraversalCallback *cb = |
|
81 static_cast<nsCycleCollectionTraversalCallback*>(aContext); |
|
82 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mRuleToBindingsMap key"); |
|
83 cb->NoteXPCOMChild(aKey); |
|
84 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mRuleToBindingsMap value"); |
|
85 cb->NoteNativeChild(aMatch, NS_CYCLE_COLLECTION_PARTICIPANT(nsXMLBindingSet)); |
|
86 return PL_DHASH_NEXT; |
|
87 } |
|
88 |
|
89 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULTemplateQueryProcessorXML) |
|
90 |
|
91 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULTemplateQueryProcessorXML) |
|
92 tmp->mRuleToBindingsMap.Clear(); |
|
93 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot) |
|
94 NS_IMPL_CYCLE_COLLECTION_UNLINK(mEvaluator) |
|
95 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateBuilder) |
|
96 NS_IMPL_CYCLE_COLLECTION_UNLINK(mRequest) |
|
97 NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
|
98 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULTemplateQueryProcessorXML) |
|
99 tmp->mRuleToBindingsMap.EnumerateRead(TraverseRuleToBindingsMap, &cb); |
|
100 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot) |
|
101 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvaluator) |
|
102 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateBuilder) |
|
103 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRequest) |
|
104 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
|
105 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULTemplateQueryProcessorXML) |
|
106 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULTemplateQueryProcessorXML) |
|
107 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULTemplateQueryProcessorXML) |
|
108 NS_INTERFACE_MAP_ENTRY(nsIXULTemplateQueryProcessor) |
|
109 NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) |
|
110 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULTemplateQueryProcessor) |
|
111 NS_INTERFACE_MAP_END |
|
112 |
|
113 /* |
|
114 * Only the first datasource in aDataSource is used, which should be either an |
|
115 * nsIURI of an XML document, or a DOM node. If the former, GetDatasource will |
|
116 * load the document asynchronously and return null in aResult. Once the |
|
117 * document has loaded, the builder's datasource will be set to the XML |
|
118 * document. If the datasource is a DOM node, the node will be returned in |
|
119 * aResult. |
|
120 */ |
|
121 NS_IMETHODIMP |
|
122 nsXULTemplateQueryProcessorXML::GetDatasource(nsIArray* aDataSources, |
|
123 nsIDOMNode* aRootNode, |
|
124 bool aIsTrusted, |
|
125 nsIXULTemplateBuilder* aBuilder, |
|
126 bool* aShouldDelayBuilding, |
|
127 nsISupports** aResult) |
|
128 { |
|
129 *aResult = nullptr; |
|
130 *aShouldDelayBuilding = false; |
|
131 |
|
132 nsresult rv; |
|
133 uint32_t length; |
|
134 |
|
135 aDataSources->GetLength(&length); |
|
136 if (length == 0) |
|
137 return NS_OK; |
|
138 |
|
139 // we get only the first item, because the query processor supports only |
|
140 // one document as a datasource |
|
141 |
|
142 nsCOMPtr<nsIDOMNode> node = do_QueryElementAt(aDataSources, 0); |
|
143 if (node) { |
|
144 return CallQueryInterface(node, aResult); |
|
145 } |
|
146 |
|
147 nsCOMPtr<nsIURI> uri = do_QueryElementAt(aDataSources, 0); |
|
148 if (!uri) |
|
149 return NS_ERROR_UNEXPECTED; |
|
150 |
|
151 nsAutoCString uriStr; |
|
152 rv = uri->GetSpec(uriStr); |
|
153 NS_ENSURE_SUCCESS(rv, rv); |
|
154 |
|
155 nsCOMPtr<nsIContent> root = do_QueryInterface(aRootNode); |
|
156 if (!root) |
|
157 return NS_ERROR_UNEXPECTED; |
|
158 |
|
159 nsCOMPtr<nsIDocument> doc = root->GetCurrentDoc(); |
|
160 if (!doc) |
|
161 return NS_ERROR_UNEXPECTED; |
|
162 |
|
163 nsIPrincipal *docPrincipal = doc->NodePrincipal(); |
|
164 |
|
165 bool hasHadScriptObject = true; |
|
166 nsIScriptGlobalObject* scriptObject = |
|
167 doc->GetScriptHandlingObject(hasHadScriptObject); |
|
168 NS_ENSURE_STATE(scriptObject); |
|
169 |
|
170 nsIScriptContext *context = scriptObject->GetContext(); |
|
171 NS_ENSURE_TRUE(context, NS_OK); |
|
172 |
|
173 nsCOMPtr<nsIXMLHttpRequest> req = |
|
174 do_CreateInstance(NS_XMLHTTPREQUEST_CONTRACTID, &rv); |
|
175 NS_ENSURE_SUCCESS(rv, rv); |
|
176 |
|
177 rv = req->Init(docPrincipal, context, |
|
178 scriptObject ? scriptObject : doc->GetScopeObject(), |
|
179 nullptr); |
|
180 NS_ENSURE_SUCCESS(rv, rv); |
|
181 |
|
182 rv = req->Open(NS_LITERAL_CSTRING("GET"), uriStr, true, |
|
183 EmptyString(), EmptyString()); |
|
184 NS_ENSURE_SUCCESS(rv, rv); |
|
185 |
|
186 nsCOMPtr<EventTarget> target(do_QueryInterface(req)); |
|
187 rv = target->AddEventListener(NS_LITERAL_STRING("load"), this, false); |
|
188 NS_ENSURE_SUCCESS(rv, rv); |
|
189 |
|
190 rv = target->AddEventListener(NS_LITERAL_STRING("error"), this, false); |
|
191 NS_ENSURE_SUCCESS(rv, rv); |
|
192 |
|
193 rv = req->Send(nullptr); |
|
194 NS_ENSURE_SUCCESS(rv, rv); |
|
195 |
|
196 mTemplateBuilder = aBuilder; |
|
197 mRequest = req; |
|
198 |
|
199 *aShouldDelayBuilding = true; |
|
200 return NS_OK; |
|
201 } |
|
202 |
|
203 NS_IMETHODIMP |
|
204 nsXULTemplateQueryProcessorXML::InitializeForBuilding(nsISupports* aDatasource, |
|
205 nsIXULTemplateBuilder* aBuilder, |
|
206 nsIDOMNode* aRootNode) |
|
207 { |
|
208 if (mGenerationStarted) |
|
209 return NS_ERROR_UNEXPECTED; |
|
210 |
|
211 // the datasource is either a document or a DOM element |
|
212 nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(aDatasource); |
|
213 if (doc) |
|
214 doc->GetDocumentElement(getter_AddRefs(mRoot)); |
|
215 else |
|
216 mRoot = do_QueryInterface(aDatasource); |
|
217 NS_ENSURE_STATE(mRoot); |
|
218 |
|
219 mEvaluator = do_CreateInstance("@mozilla.org/dom/xpath-evaluator;1"); |
|
220 NS_ENSURE_TRUE(mEvaluator, NS_ERROR_OUT_OF_MEMORY); |
|
221 |
|
222 return NS_OK; |
|
223 } |
|
224 |
|
225 NS_IMETHODIMP |
|
226 nsXULTemplateQueryProcessorXML::Done() |
|
227 { |
|
228 mGenerationStarted = false; |
|
229 |
|
230 mRuleToBindingsMap.Clear(); |
|
231 |
|
232 return NS_OK; |
|
233 } |
|
234 |
|
235 NS_IMETHODIMP |
|
236 nsXULTemplateQueryProcessorXML::CompileQuery(nsIXULTemplateBuilder* aBuilder, |
|
237 nsIDOMNode* aQueryNode, |
|
238 nsIAtom* aRefVariable, |
|
239 nsIAtom* aMemberVariable, |
|
240 nsISupports** _retval) |
|
241 { |
|
242 nsresult rv = NS_OK; |
|
243 |
|
244 *_retval = nullptr; |
|
245 |
|
246 nsCOMPtr<nsIContent> content = do_QueryInterface(aQueryNode); |
|
247 |
|
248 nsAutoString expr; |
|
249 content->GetAttr(kNameSpaceID_None, nsGkAtoms::expr, expr); |
|
250 |
|
251 // if an expression is not specified, then the default is to |
|
252 // just take all of the children |
|
253 if (expr.IsEmpty()) |
|
254 expr.AssignLiteral("*"); |
|
255 |
|
256 nsCOMPtr<nsIDOMXPathExpression> compiledexpr; |
|
257 rv = CreateExpression(expr, aQueryNode, getter_AddRefs(compiledexpr)); |
|
258 if (NS_FAILED(rv)) { |
|
259 nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BAD_XPATH); |
|
260 return rv; |
|
261 } |
|
262 |
|
263 nsRefPtr<nsXMLQuery> query = |
|
264 new nsXMLQuery(this, aMemberVariable, compiledexpr); |
|
265 NS_ENSURE_TRUE(query, NS_ERROR_OUT_OF_MEMORY); |
|
266 |
|
267 for (nsIContent* condition = content->GetFirstChild(); |
|
268 condition; |
|
269 condition = condition->GetNextSibling()) { |
|
270 |
|
271 if (condition->NodeInfo()->Equals(nsGkAtoms::assign, |
|
272 kNameSpaceID_XUL)) { |
|
273 nsAutoString var; |
|
274 condition->GetAttr(kNameSpaceID_None, nsGkAtoms::var, var); |
|
275 |
|
276 nsAutoString expr; |
|
277 condition->GetAttr(kNameSpaceID_None, nsGkAtoms::expr, expr); |
|
278 |
|
279 // ignore assignments without a variable or an expression |
|
280 if (!var.IsEmpty() && !expr.IsEmpty()) { |
|
281 nsCOMPtr<nsIDOMNode> conditionNode = |
|
282 do_QueryInterface(condition); |
|
283 rv = CreateExpression(expr, conditionNode, |
|
284 getter_AddRefs(compiledexpr)); |
|
285 if (NS_FAILED(rv)) { |
|
286 nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BAD_ASSIGN_XPATH); |
|
287 return rv; |
|
288 } |
|
289 |
|
290 nsCOMPtr<nsIAtom> varatom = do_GetAtom(var); |
|
291 |
|
292 rv = query->AddBinding(varatom, compiledexpr); |
|
293 NS_ENSURE_SUCCESS(rv, rv); |
|
294 } |
|
295 } |
|
296 } |
|
297 |
|
298 *_retval = query; |
|
299 NS_ADDREF(*_retval); |
|
300 |
|
301 return rv; |
|
302 } |
|
303 |
|
304 NS_IMETHODIMP |
|
305 nsXULTemplateQueryProcessorXML::GenerateResults(nsISupports* aDatasource, |
|
306 nsIXULTemplateResult* aRef, |
|
307 nsISupports* aQuery, |
|
308 nsISimpleEnumerator** aResults) |
|
309 { |
|
310 if (!aQuery) |
|
311 return NS_ERROR_INVALID_ARG; |
|
312 |
|
313 mGenerationStarted = true; |
|
314 |
|
315 nsCOMPtr<nsXMLQuery> xmlquery = do_QueryInterface(aQuery); |
|
316 if (!xmlquery) |
|
317 return NS_ERROR_INVALID_ARG; |
|
318 |
|
319 nsCOMPtr<nsISupports> supports; |
|
320 nsCOMPtr<nsIDOMNode> context; |
|
321 if (aRef) |
|
322 aRef->GetBindingObjectFor(xmlquery->GetMemberVariable(), |
|
323 getter_AddRefs(supports)); |
|
324 context = do_QueryInterface(supports); |
|
325 if (!context) |
|
326 context = mRoot; |
|
327 |
|
328 nsIDOMXPathExpression* expr = xmlquery->GetResultsExpression(); |
|
329 if (!expr) |
|
330 return NS_ERROR_FAILURE; |
|
331 |
|
332 nsCOMPtr<nsISupports> exprsupportsresults; |
|
333 nsresult rv = expr->Evaluate(context, |
|
334 nsIDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE, |
|
335 nullptr, getter_AddRefs(exprsupportsresults)); |
|
336 NS_ENSURE_SUCCESS(rv, rv); |
|
337 |
|
338 nsCOMPtr<nsIDOMXPathResult> exprresults = |
|
339 do_QueryInterface(exprsupportsresults); |
|
340 |
|
341 nsXULTemplateResultSetXML* results = |
|
342 new nsXULTemplateResultSetXML(xmlquery, exprresults, |
|
343 xmlquery->GetBindingSet()); |
|
344 NS_ENSURE_TRUE(results, NS_ERROR_OUT_OF_MEMORY); |
|
345 |
|
346 *aResults = results; |
|
347 NS_ADDREF(*aResults); |
|
348 |
|
349 return NS_OK; |
|
350 } |
|
351 |
|
352 NS_IMETHODIMP |
|
353 nsXULTemplateQueryProcessorXML::AddBinding(nsIDOMNode* aRuleNode, |
|
354 nsIAtom* aVar, |
|
355 nsIAtom* aRef, |
|
356 const nsAString& aExpr) |
|
357 { |
|
358 if (mGenerationStarted) |
|
359 return NS_ERROR_FAILURE; |
|
360 |
|
361 nsRefPtr<nsXMLBindingSet> bindings = mRuleToBindingsMap.GetWeak(aRuleNode); |
|
362 if (!bindings) { |
|
363 bindings = new nsXMLBindingSet(); |
|
364 mRuleToBindingsMap.Put(aRuleNode, bindings); |
|
365 } |
|
366 |
|
367 nsCOMPtr<nsIDOMXPathExpression> compiledexpr; |
|
368 nsresult rv = |
|
369 CreateExpression(aExpr, aRuleNode, getter_AddRefs(compiledexpr)); |
|
370 if (NS_FAILED(rv)) { |
|
371 nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BAD_BINDING_XPATH); |
|
372 return NS_OK; |
|
373 } |
|
374 |
|
375 // aRef isn't currently used for XML query processors |
|
376 return bindings->AddBinding(aVar, compiledexpr); |
|
377 } |
|
378 |
|
379 NS_IMETHODIMP |
|
380 nsXULTemplateQueryProcessorXML::TranslateRef(nsISupports* aDatasource, |
|
381 const nsAString& aRefString, |
|
382 nsIXULTemplateResult** aRef) |
|
383 { |
|
384 *aRef = nullptr; |
|
385 |
|
386 // the datasource is either a document or a DOM element |
|
387 nsCOMPtr<nsIDOMElement> rootElement; |
|
388 nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(aDatasource); |
|
389 if (doc) |
|
390 doc->GetDocumentElement(getter_AddRefs(rootElement)); |
|
391 else |
|
392 rootElement = do_QueryInterface(aDatasource); |
|
393 |
|
394 // if no root element, just return. The document may not have loaded yet |
|
395 if (!rootElement) |
|
396 return NS_OK; |
|
397 |
|
398 nsXULTemplateResultXML* result = |
|
399 new nsXULTemplateResultXML(nullptr, rootElement, nullptr); |
|
400 NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY); |
|
401 |
|
402 *aRef = result; |
|
403 NS_ADDREF(*aRef); |
|
404 |
|
405 return NS_OK; |
|
406 } |
|
407 |
|
408 |
|
409 NS_IMETHODIMP |
|
410 nsXULTemplateQueryProcessorXML::CompareResults(nsIXULTemplateResult* aLeft, |
|
411 nsIXULTemplateResult* aRight, |
|
412 nsIAtom* aVar, |
|
413 uint32_t aSortHints, |
|
414 int32_t* aResult) |
|
415 { |
|
416 *aResult = 0; |
|
417 if (!aVar) |
|
418 return NS_OK; |
|
419 |
|
420 nsAutoString leftVal; |
|
421 if (aLeft) |
|
422 aLeft->GetBindingFor(aVar, leftVal); |
|
423 |
|
424 nsAutoString rightVal; |
|
425 if (aRight) |
|
426 aRight->GetBindingFor(aVar, rightVal); |
|
427 |
|
428 *aResult = XULSortServiceImpl::CompareValues(leftVal, rightVal, aSortHints); |
|
429 return NS_OK; |
|
430 } |
|
431 |
|
432 nsXMLBindingSet* |
|
433 nsXULTemplateQueryProcessorXML::GetOptionalBindingsForRule(nsIDOMNode* aRuleNode) |
|
434 { |
|
435 return mRuleToBindingsMap.GetWeak(aRuleNode); |
|
436 } |
|
437 |
|
438 nsresult |
|
439 nsXULTemplateQueryProcessorXML::CreateExpression(const nsAString& aExpr, |
|
440 nsIDOMNode* aNode, |
|
441 nsIDOMXPathExpression** aCompiledExpr) |
|
442 { |
|
443 nsCOMPtr<nsIDOMXPathNSResolver> nsResolver; |
|
444 |
|
445 nsCOMPtr<nsIDOMDocument> doc; |
|
446 aNode->GetOwnerDocument(getter_AddRefs(doc)); |
|
447 |
|
448 nsCOMPtr<nsIDOMXPathEvaluator> eval = do_QueryInterface(doc); |
|
449 if (eval) { |
|
450 nsresult rv = |
|
451 eval->CreateNSResolver(aNode, getter_AddRefs(nsResolver)); |
|
452 NS_ENSURE_SUCCESS(rv, rv); |
|
453 } |
|
454 |
|
455 return mEvaluator->CreateExpression(aExpr, nsResolver, aCompiledExpr); |
|
456 } |
|
457 |
|
458 NS_IMETHODIMP |
|
459 nsXULTemplateQueryProcessorXML::HandleEvent(nsIDOMEvent* aEvent) |
|
460 { |
|
461 NS_PRECONDITION(aEvent, "aEvent null"); |
|
462 nsAutoString eventType; |
|
463 aEvent->GetType(eventType); |
|
464 |
|
465 if (eventType.EqualsLiteral("load") && mTemplateBuilder) { |
|
466 NS_ASSERTION(mRequest, "request was not set"); |
|
467 nsCOMPtr<nsIDOMDocument> doc; |
|
468 if (NS_SUCCEEDED(mRequest->GetResponseXML(getter_AddRefs(doc)))) |
|
469 mTemplateBuilder->SetDatasource(doc); |
|
470 |
|
471 // to avoid leak. we don't need it after... |
|
472 mTemplateBuilder = nullptr; |
|
473 mRequest = nullptr; |
|
474 } |
|
475 else if (eventType.EqualsLiteral("error")) { |
|
476 mTemplateBuilder = nullptr; |
|
477 mRequest = nullptr; |
|
478 } |
|
479 |
|
480 return NS_OK; |
|
481 } |