Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / content / xul / templates / src / nsXULTemplateQueryProcessorXML.cpp
blobb30ee05fc316f30b09fab9f1e92aa1b8bc6ae819
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
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/
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.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is Neil Deakin
18 * Portions created by the Initial Developer are Copyright (C) 2006
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
22 * Laurent Jouanneau <laurent.jouanneau@disruptive-innovations.com>
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 #include "nsCOMPtr.h"
39 #include "nsAutoPtr.h"
40 #include "nsIDOMDocument.h"
41 #include "nsIDOMXMLDocument.h"
42 #include "nsIDOMNode.h"
43 #include "nsIDOMNodeList.h"
44 #include "nsIDOMElement.h"
45 #include "nsIDOMEvent.h"
46 #include "nsIDOMEventTarget.h"
47 #include "nsIDOMXPathNSResolver.h"
48 #include "nsIDocument.h"
49 #include "nsIContent.h"
50 #include "nsINameSpaceManager.h"
51 #include "nsGkAtoms.h"
52 #include "nsIServiceManager.h"
53 #include "nsUnicharUtils.h"
54 #include "nsIURI.h"
55 #include "nsIArray.h"
56 #include "nsContentUtils.h"
57 #include "nsArrayUtils.h"
58 #include "nsPIDOMWindow.h"
60 #include "nsXULTemplateBuilder.h"
61 #include "nsXULTemplateQueryProcessorXML.h"
62 #include "nsXULTemplateResultXML.h"
64 NS_IMPL_ISUPPORTS1(nsXMLQuery, nsXMLQuery)
66 //----------------------------------------------------------------------
68 // nsXULTemplateResultSetXML
71 NS_IMPL_ISUPPORTS1(nsXULTemplateResultSetXML, nsISimpleEnumerator)
73 NS_IMETHODIMP
74 nsXULTemplateResultSetXML::HasMoreElements(PRBool *aResult)
76 // if GetSnapshotLength failed, then the return type was not a set of
77 // nodes, so just return false in this case.
78 PRUint32 length;
79 if (NS_SUCCEEDED(mResults->GetSnapshotLength(&length)))
80 *aResult = (mPosition < length);
81 else
82 *aResult = PR_FALSE;
84 return NS_OK;
87 NS_IMETHODIMP
88 nsXULTemplateResultSetXML::GetNext(nsISupports **aResult)
90 nsCOMPtr<nsIDOMNode> node;
91 nsresult rv = mResults->SnapshotItem(mPosition, getter_AddRefs(node));
92 NS_ENSURE_SUCCESS(rv, rv);
94 nsXULTemplateResultXML* result =
95 new nsXULTemplateResultXML(mQuery, node, mBindingSet);
96 NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY);
98 ++mPosition;
99 *aResult = result;
100 NS_ADDREF(result);
101 return NS_OK;
105 //----------------------------------------------------------------------
107 // nsXULTemplateQueryProcessorXML
110 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULTemplateQueryProcessorXML)
111 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULTemplateQueryProcessorXML)
112 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mTemplateBuilder)
113 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRequest)
114 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
115 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULTemplateQueryProcessorXML)
116 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mTemplateBuilder)
117 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mRequest)
118 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
119 NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsXULTemplateQueryProcessorXML,
120 nsIXULTemplateQueryProcessor)
121 NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsXULTemplateQueryProcessorXML,
122 nsIXULTemplateQueryProcessor)
123 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULTemplateQueryProcessorXML)
124 NS_INTERFACE_MAP_ENTRY(nsIXULTemplateQueryProcessor)
125 NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
126 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULTemplateQueryProcessor)
127 NS_INTERFACE_MAP_END
130 * Only the first datasource in aDataSource is used, which should be either an
131 * nsIURI of an XML document, or a DOM node. If the former, GetDatasource will
132 * load the document asynchronously and return null in aResult. Once the
133 * document has loaded, the builder's datasource will be set to the XML
134 * document. If the datasource is a DOM node, the node will be returned in
135 * aResult.
137 NS_IMETHODIMP
138 nsXULTemplateQueryProcessorXML::GetDatasource(nsIArray* aDataSources,
139 nsIDOMNode* aRootNode,
140 PRBool aIsTrusted,
141 nsIXULTemplateBuilder* aBuilder,
142 PRBool* aShouldDelayBuilding,
143 nsISupports** aResult)
145 *aResult = nsnull;
146 *aShouldDelayBuilding = PR_FALSE;
148 nsresult rv;
149 PRUint32 length;
151 aDataSources->GetLength(&length);
152 if (length == 0)
153 return NS_OK;
155 // we get only the first item, because the query processor supports only
156 // one document as a datasource
158 nsCOMPtr<nsIDOMNode> node = do_QueryElementAt(aDataSources, 0);
159 if (node) {
160 return CallQueryInterface(node, aResult);
163 nsCOMPtr<nsIURI> uri = do_QueryElementAt(aDataSources, 0);
164 if (!uri)
165 return NS_ERROR_UNEXPECTED;
167 nsCAutoString uriStr;
168 rv = uri->GetSpec(uriStr);
169 NS_ENSURE_SUCCESS(rv, rv);
171 nsCOMPtr<nsIContent> root = do_QueryInterface(aRootNode);
172 if (!root)
173 return NS_ERROR_UNEXPECTED;
175 nsCOMPtr<nsIDocument> doc = root->GetCurrentDoc();
176 if (!doc)
177 return NS_ERROR_UNEXPECTED;
179 nsIPrincipal *docPrincipal = doc->NodePrincipal();
181 PRBool hasHadScriptObject = PR_TRUE;
182 nsIScriptGlobalObject* scriptObject =
183 doc->GetScriptHandlingObject(hasHadScriptObject);
184 NS_ENSURE_STATE(scriptObject || !hasHadScriptObject);
186 nsIScriptContext *context = scriptObject->GetContext();
187 NS_ENSURE_TRUE(context, NS_OK);
189 nsCOMPtr<nsIXMLHttpRequest> req =
190 do_CreateInstance(NS_XMLHTTPREQUEST_CONTRACTID, &rv);
191 NS_ENSURE_SUCCESS(rv, rv);
193 nsCOMPtr<nsPIDOMWindow> owner = do_QueryInterface(scriptObject);
194 req->Init(docPrincipal, context, owner);
196 rv = req->OpenRequest(NS_LITERAL_CSTRING("GET"), uriStr, PR_TRUE,
197 EmptyString(), EmptyString());
198 NS_ENSURE_SUCCESS(rv, rv);
200 nsCOMPtr<nsIDOMEventTarget> target(do_QueryInterface(req));
201 rv = target->AddEventListener(NS_LITERAL_STRING("load"), this, PR_FALSE);
202 NS_ENSURE_SUCCESS(rv, rv);
204 rv = target->AddEventListener(NS_LITERAL_STRING("error"), this, PR_FALSE);
205 NS_ENSURE_SUCCESS(rv, rv);
207 rv = req->Send(nsnull);
208 NS_ENSURE_SUCCESS(rv, rv);
210 mTemplateBuilder = aBuilder;
211 mRequest = req;
213 *aShouldDelayBuilding = PR_TRUE;
214 return NS_OK;
217 NS_IMETHODIMP
218 nsXULTemplateQueryProcessorXML::InitializeForBuilding(nsISupports* aDatasource,
219 nsIXULTemplateBuilder* aBuilder,
220 nsIDOMNode* aRootNode)
222 if (mGenerationStarted)
223 return NS_ERROR_UNEXPECTED;
225 // the datasource is either a document or a DOM element
226 nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(aDatasource);
227 if (doc)
228 doc->GetDocumentElement(getter_AddRefs(mRoot));
229 else
230 mRoot = do_QueryInterface(aDatasource);
231 NS_ENSURE_STATE(mRoot);
233 mEvaluator = do_CreateInstance("@mozilla.org/dom/xpath-evaluator;1");
234 NS_ENSURE_TRUE(mEvaluator, NS_ERROR_OUT_OF_MEMORY);
236 if (!mRuleToBindingsMap.IsInitialized() &&
237 !mRuleToBindingsMap.Init())
238 return NS_ERROR_OUT_OF_MEMORY;
240 return NS_OK;
243 NS_IMETHODIMP
244 nsXULTemplateQueryProcessorXML::Done()
246 mGenerationStarted = PR_FALSE;
248 if (mRuleToBindingsMap.IsInitialized())
249 mRuleToBindingsMap.Clear();
251 return NS_OK;
254 NS_IMETHODIMP
255 nsXULTemplateQueryProcessorXML::CompileQuery(nsIXULTemplateBuilder* aBuilder,
256 nsIDOMNode* aQueryNode,
257 nsIAtom* aRefVariable,
258 nsIAtom* aMemberVariable,
259 nsISupports** _retval)
261 nsresult rv = NS_OK;
263 *_retval = nsnull;
265 nsCOMPtr<nsIContent> content = do_QueryInterface(aQueryNode);
267 nsAutoString expr;
268 content->GetAttr(kNameSpaceID_None, nsGkAtoms::expr, expr);
270 // if an expression is not specified, then the default is to
271 // just take all of the children
272 if (expr.IsEmpty())
273 expr.AssignLiteral("*");
275 nsCOMPtr<nsIDOMXPathExpression> compiledexpr;
276 rv = CreateExpression(expr, aQueryNode, getter_AddRefs(compiledexpr));
277 NS_ENSURE_SUCCESS(rv, rv);
279 nsRefPtr<nsXMLQuery> query =
280 new nsXMLQuery(this, aMemberVariable, compiledexpr);
281 NS_ENSURE_TRUE(query, NS_ERROR_OUT_OF_MEMORY);
283 PRUint32 count = content->GetChildCount();
284 for (PRUint32 i = 0; i < count; ++i) {
285 nsIContent *condition = content->GetChildAt(i);
286 if (condition->NodeInfo()->Equals(nsGkAtoms::assign,
287 kNameSpaceID_XUL)) {
288 nsAutoString var;
289 condition->GetAttr(kNameSpaceID_None, nsGkAtoms::var, var);
291 nsAutoString expr;
292 condition->GetAttr(kNameSpaceID_None, nsGkAtoms::expr, expr);
294 // ignore assignments without a variable or an expression
295 if (!var.IsEmpty() && !expr.IsEmpty()) {
296 nsCOMPtr<nsIDOMNode> conditionNode =
297 do_QueryInterface(condition);
298 rv = CreateExpression(expr, conditionNode,
299 getter_AddRefs(compiledexpr));
300 NS_ENSURE_SUCCESS(rv, rv);
302 nsCOMPtr<nsIAtom> varatom = do_GetAtom(var);
304 rv = query->AddBinding(varatom, compiledexpr);
305 NS_ENSURE_SUCCESS(rv, rv);
310 *_retval = query;
311 NS_ADDREF(*_retval);
313 return rv;
316 NS_IMETHODIMP
317 nsXULTemplateQueryProcessorXML::GenerateResults(nsISupports* aDatasource,
318 nsIXULTemplateResult* aRef,
319 nsISupports* aQuery,
320 nsISimpleEnumerator** aResults)
322 if (!aQuery)
323 return NS_ERROR_INVALID_ARG;
325 mGenerationStarted = PR_TRUE;
327 nsCOMPtr<nsXMLQuery> xmlquery = do_QueryInterface(aQuery);
328 if (!xmlquery)
329 return NS_ERROR_INVALID_ARG;
331 nsCOMPtr<nsIDOMNode> context;
332 if (aRef)
333 aRef->GetBindingObjectFor(xmlquery->GetMemberVariable(),
334 getter_AddRefs(context));
335 if (!context)
336 context = mRoot;
338 nsIDOMXPathExpression* expr = xmlquery->GetResultsExpression();
339 if (!expr)
340 return NS_ERROR_FAILURE;
342 nsCOMPtr<nsISupports> exprsupportsresults;
343 nsresult rv = expr->Evaluate(context,
344 nsIDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE,
345 nsnull, getter_AddRefs(exprsupportsresults));
346 NS_ENSURE_SUCCESS(rv, rv);
348 nsCOMPtr<nsIDOMXPathResult> exprresults =
349 do_QueryInterface(exprsupportsresults);
351 nsXULTemplateResultSetXML* results =
352 new nsXULTemplateResultSetXML(xmlquery, exprresults,
353 xmlquery->GetBindingSet());
354 NS_ENSURE_TRUE(results, NS_ERROR_OUT_OF_MEMORY);
356 *aResults = results;
357 NS_ADDREF(*aResults);
359 return NS_OK;
362 NS_IMETHODIMP
363 nsXULTemplateQueryProcessorXML::AddBinding(nsIDOMNode* aRuleNode,
364 nsIAtom* aVar,
365 nsIAtom* aRef,
366 const nsAString& aExpr)
368 if (mGenerationStarted)
369 return NS_ERROR_FAILURE;
371 nsRefPtr<nsXMLBindingSet> bindings = mRuleToBindingsMap.GetWeak(aRuleNode);
372 if (!bindings) {
373 bindings = new nsXMLBindingSet();
374 if (!bindings || !mRuleToBindingsMap.Put(aRuleNode, bindings))
375 return NS_ERROR_OUT_OF_MEMORY;
378 nsCOMPtr<nsIDOMXPathExpression> compiledexpr;
379 nsresult rv =
380 CreateExpression(aExpr, aRuleNode, getter_AddRefs(compiledexpr));
381 NS_ENSURE_SUCCESS(rv, rv);
383 // aRef isn't currently used for XML query processors
384 return bindings->AddBinding(aVar, compiledexpr);
387 NS_IMETHODIMP
388 nsXULTemplateQueryProcessorXML::TranslateRef(nsISupports* aDatasource,
389 const nsAString& aRefString,
390 nsIXULTemplateResult** aRef)
392 *aRef = nsnull;
394 // the datasource is either a document or a DOM element
395 nsCOMPtr<nsIDOMElement> rootElement;
396 nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(aDatasource);
397 if (doc)
398 doc->GetDocumentElement(getter_AddRefs(rootElement));
399 else
400 rootElement = do_QueryInterface(aDatasource);
402 // if no root element, just return. The document may not have loaded yet
403 if (!rootElement)
404 return NS_OK;
406 nsXULTemplateResultXML* result =
407 new nsXULTemplateResultXML(nsnull, rootElement, nsnull);
408 NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY);
410 *aRef = result;
411 NS_ADDREF(*aRef);
413 return NS_OK;
417 NS_IMETHODIMP
418 nsXULTemplateQueryProcessorXML::CompareResults(nsIXULTemplateResult* aLeft,
419 nsIXULTemplateResult* aRight,
420 nsIAtom* aVar,
421 PRInt32* aResult)
423 *aResult = 0;
424 if (!aVar)
425 return NS_OK;
427 // XXXndeakin - bug 379745
428 // it would be good for this to handle other types such as integers,
429 // so that sorting can be optimized for different types.
431 nsAutoString leftVal;
432 if (aLeft)
433 aLeft->GetBindingFor(aVar, leftVal);
435 nsAutoString rightVal;
436 if (aRight)
437 aRight->GetBindingFor(aVar, rightVal);
439 // currently templates always sort case-insensitive
440 *aResult = ::Compare(leftVal, rightVal,
441 nsCaseInsensitiveStringComparator());
442 return NS_OK;
445 nsXMLBindingSet*
446 nsXULTemplateQueryProcessorXML::GetOptionalBindingsForRule(nsIDOMNode* aRuleNode)
448 return mRuleToBindingsMap.GetWeak(aRuleNode);
451 nsresult
452 nsXULTemplateQueryProcessorXML::CreateExpression(const nsAString& aExpr,
453 nsIDOMNode* aNode,
454 nsIDOMXPathExpression** aCompiledExpr)
456 nsCOMPtr<nsIDOMXPathNSResolver> nsResolver;
458 nsCOMPtr<nsIDOMDocument> doc;
459 aNode->GetOwnerDocument(getter_AddRefs(doc));
461 nsCOMPtr<nsIDOMXPathEvaluator> eval = do_QueryInterface(doc);
462 if (eval) {
463 nsresult rv =
464 eval->CreateNSResolver(aNode, getter_AddRefs(nsResolver));
465 NS_ENSURE_SUCCESS(rv, rv);
468 return mEvaluator->CreateExpression(aExpr, nsResolver, aCompiledExpr);
471 NS_IMETHODIMP
472 nsXULTemplateQueryProcessorXML::HandleEvent(nsIDOMEvent* aEvent)
474 NS_PRECONDITION(aEvent, "aEvent null");
475 nsAutoString eventType;
476 aEvent->GetType(eventType);
478 if (eventType.EqualsLiteral("load") && mTemplateBuilder) {
479 NS_ASSERTION(mRequest, "request was not set");
480 nsCOMPtr<nsIDOMDocument> doc;
481 if (NS_SUCCEEDED(mRequest->GetResponseXML(getter_AddRefs(doc))))
482 mTemplateBuilder->SetDatasource(doc);
484 // to avoid leak. we don't need it after...
485 mTemplateBuilder = nsnull;
486 mRequest = nsnull;
488 else if (eventType.EqualsLiteral("error")) {
489 mTemplateBuilder = nsnull;
490 mRequest = nsnull;
493 return NS_OK;