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
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.
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 ***** */
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"
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
)
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.
79 if (NS_SUCCEEDED(mResults
->GetSnapshotLength(&length
)))
80 *aResult
= (mPosition
< length
);
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
);
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
)
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
138 nsXULTemplateQueryProcessorXML::GetDatasource(nsIArray
* aDataSources
,
139 nsIDOMNode
* aRootNode
,
141 nsIXULTemplateBuilder
* aBuilder
,
142 PRBool
* aShouldDelayBuilding
,
143 nsISupports
** aResult
)
146 *aShouldDelayBuilding
= PR_FALSE
;
151 aDataSources
->GetLength(&length
);
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);
160 return CallQueryInterface(node
, aResult
);
163 nsCOMPtr
<nsIURI
> uri
= do_QueryElementAt(aDataSources
, 0);
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
);
173 return NS_ERROR_UNEXPECTED
;
175 nsCOMPtr
<nsIDocument
> doc
= root
->GetCurrentDoc();
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
;
213 *aShouldDelayBuilding
= PR_TRUE
;
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
);
228 doc
->GetDocumentElement(getter_AddRefs(mRoot
));
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
;
244 nsXULTemplateQueryProcessorXML::Done()
246 mGenerationStarted
= PR_FALSE
;
248 if (mRuleToBindingsMap
.IsInitialized())
249 mRuleToBindingsMap
.Clear();
255 nsXULTemplateQueryProcessorXML::CompileQuery(nsIXULTemplateBuilder
* aBuilder
,
256 nsIDOMNode
* aQueryNode
,
257 nsIAtom
* aRefVariable
,
258 nsIAtom
* aMemberVariable
,
259 nsISupports
** _retval
)
265 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(aQueryNode
);
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
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
,
289 condition
->GetAttr(kNameSpaceID_None
, nsGkAtoms::var
, var
);
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
);
317 nsXULTemplateQueryProcessorXML::GenerateResults(nsISupports
* aDatasource
,
318 nsIXULTemplateResult
* aRef
,
320 nsISimpleEnumerator
** aResults
)
323 return NS_ERROR_INVALID_ARG
;
325 mGenerationStarted
= PR_TRUE
;
327 nsCOMPtr
<nsXMLQuery
> xmlquery
= do_QueryInterface(aQuery
);
329 return NS_ERROR_INVALID_ARG
;
331 nsCOMPtr
<nsIDOMNode
> context
;
333 aRef
->GetBindingObjectFor(xmlquery
->GetMemberVariable(),
334 getter_AddRefs(context
));
338 nsIDOMXPathExpression
* expr
= xmlquery
->GetResultsExpression();
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
);
357 NS_ADDREF(*aResults
);
363 nsXULTemplateQueryProcessorXML::AddBinding(nsIDOMNode
* aRuleNode
,
366 const nsAString
& aExpr
)
368 if (mGenerationStarted
)
369 return NS_ERROR_FAILURE
;
371 nsRefPtr
<nsXMLBindingSet
> bindings
= mRuleToBindingsMap
.GetWeak(aRuleNode
);
373 bindings
= new nsXMLBindingSet();
374 if (!bindings
|| !mRuleToBindingsMap
.Put(aRuleNode
, bindings
))
375 return NS_ERROR_OUT_OF_MEMORY
;
378 nsCOMPtr
<nsIDOMXPathExpression
> compiledexpr
;
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
);
388 nsXULTemplateQueryProcessorXML::TranslateRef(nsISupports
* aDatasource
,
389 const nsAString
& aRefString
,
390 nsIXULTemplateResult
** aRef
)
394 // the datasource is either a document or a DOM element
395 nsCOMPtr
<nsIDOMElement
> rootElement
;
396 nsCOMPtr
<nsIDOMDocument
> doc
= do_QueryInterface(aDatasource
);
398 doc
->GetDocumentElement(getter_AddRefs(rootElement
));
400 rootElement
= do_QueryInterface(aDatasource
);
402 // if no root element, just return. The document may not have loaded yet
406 nsXULTemplateResultXML
* result
=
407 new nsXULTemplateResultXML(nsnull
, rootElement
, nsnull
);
408 NS_ENSURE_TRUE(result
, NS_ERROR_OUT_OF_MEMORY
);
418 nsXULTemplateQueryProcessorXML::CompareResults(nsIXULTemplateResult
* aLeft
,
419 nsIXULTemplateResult
* aRight
,
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
;
433 aLeft
->GetBindingFor(aVar
, leftVal
);
435 nsAutoString rightVal
;
437 aRight
->GetBindingFor(aVar
, rightVal
);
439 // currently templates always sort case-insensitive
440 *aResult
= ::Compare(leftVal
, rightVal
,
441 nsCaseInsensitiveStringComparator());
446 nsXULTemplateQueryProcessorXML::GetOptionalBindingsForRule(nsIDOMNode
* aRuleNode
)
448 return mRuleToBindingsMap
.GetWeak(aRuleNode
);
452 nsXULTemplateQueryProcessorXML::CreateExpression(const nsAString
& aExpr
,
454 nsIDOMXPathExpression
** aCompiledExpr
)
456 nsCOMPtr
<nsIDOMXPathNSResolver
> nsResolver
;
458 nsCOMPtr
<nsIDOMDocument
> doc
;
459 aNode
->GetOwnerDocument(getter_AddRefs(doc
));
461 nsCOMPtr
<nsIDOMXPathEvaluator
> eval
= do_QueryInterface(doc
);
464 eval
->CreateNSResolver(aNode
, getter_AddRefs(nsResolver
));
465 NS_ENSURE_SUCCESS(rv
, rv
);
468 return mEvaluator
->CreateExpression(aExpr
, nsResolver
, aCompiledExpr
);
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
;
488 else if (eventType
.EqualsLiteral("error")) {
489 mTemplateBuilder
= nsnull
;