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