1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/xul/templates/src/nsXULTemplateQueryProcessorXML.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,481 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsCOMPtr.h" 1.10 +#include "nsAutoPtr.h" 1.11 +#include "nsIDOMDocument.h" 1.12 +#include "nsIDOMNode.h" 1.13 +#include "nsIDOMElement.h" 1.14 +#include "nsIDOMEvent.h" 1.15 +#include "nsIDOMXPathNSResolver.h" 1.16 +#include "nsIDocument.h" 1.17 +#include "nsIContent.h" 1.18 +#include "nsComponentManagerUtils.h" 1.19 +#include "nsGkAtoms.h" 1.20 +#include "nsIURI.h" 1.21 +#include "nsIArray.h" 1.22 +#include "nsIScriptContext.h" 1.23 +#include "nsArrayUtils.h" 1.24 +#include "nsPIDOMWindow.h" 1.25 +#include "nsXULContentUtils.h" 1.26 +#include "nsXMLHttpRequest.h" 1.27 + 1.28 +#include "nsXULTemplateQueryProcessorXML.h" 1.29 +#include "nsXULTemplateResultXML.h" 1.30 +#include "nsXULSortService.h" 1.31 + 1.32 +using namespace mozilla::dom; 1.33 + 1.34 +NS_IMPL_ISUPPORTS(nsXMLQuery, nsXMLQuery) 1.35 + 1.36 +//---------------------------------------------------------------------- 1.37 +// 1.38 +// nsXULTemplateResultSetXML 1.39 +// 1.40 + 1.41 +NS_IMPL_ISUPPORTS(nsXULTemplateResultSetXML, nsISimpleEnumerator) 1.42 + 1.43 +NS_IMETHODIMP 1.44 +nsXULTemplateResultSetXML::HasMoreElements(bool *aResult) 1.45 +{ 1.46 + // if GetSnapshotLength failed, then the return type was not a set of 1.47 + // nodes, so just return false in this case. 1.48 + uint32_t length; 1.49 + if (NS_SUCCEEDED(mResults->GetSnapshotLength(&length))) 1.50 + *aResult = (mPosition < length); 1.51 + else 1.52 + *aResult = false; 1.53 + 1.54 + return NS_OK; 1.55 +} 1.56 + 1.57 +NS_IMETHODIMP 1.58 +nsXULTemplateResultSetXML::GetNext(nsISupports **aResult) 1.59 +{ 1.60 + nsCOMPtr<nsIDOMNode> node; 1.61 + nsresult rv = mResults->SnapshotItem(mPosition, getter_AddRefs(node)); 1.62 + NS_ENSURE_SUCCESS(rv, rv); 1.63 + 1.64 + nsXULTemplateResultXML* result = 1.65 + new nsXULTemplateResultXML(mQuery, node, mBindingSet); 1.66 + NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY); 1.67 + 1.68 + ++mPosition; 1.69 + *aResult = result; 1.70 + NS_ADDREF(result); 1.71 + return NS_OK; 1.72 +} 1.73 + 1.74 + 1.75 +//---------------------------------------------------------------------- 1.76 +// 1.77 +// nsXULTemplateQueryProcessorXML 1.78 +// 1.79 + 1.80 +static PLDHashOperator 1.81 +TraverseRuleToBindingsMap(nsISupports* aKey, nsXMLBindingSet* aMatch, void* aContext) 1.82 +{ 1.83 + nsCycleCollectionTraversalCallback *cb = 1.84 + static_cast<nsCycleCollectionTraversalCallback*>(aContext); 1.85 + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mRuleToBindingsMap key"); 1.86 + cb->NoteXPCOMChild(aKey); 1.87 + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mRuleToBindingsMap value"); 1.88 + cb->NoteNativeChild(aMatch, NS_CYCLE_COLLECTION_PARTICIPANT(nsXMLBindingSet)); 1.89 + return PL_DHASH_NEXT; 1.90 +} 1.91 + 1.92 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULTemplateQueryProcessorXML) 1.93 + 1.94 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULTemplateQueryProcessorXML) 1.95 + tmp->mRuleToBindingsMap.Clear(); 1.96 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot) 1.97 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mEvaluator) 1.98 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateBuilder) 1.99 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mRequest) 1.100 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.101 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULTemplateQueryProcessorXML) 1.102 + tmp->mRuleToBindingsMap.EnumerateRead(TraverseRuleToBindingsMap, &cb); 1.103 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot) 1.104 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvaluator) 1.105 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateBuilder) 1.106 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRequest) 1.107 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.108 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULTemplateQueryProcessorXML) 1.109 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULTemplateQueryProcessorXML) 1.110 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULTemplateQueryProcessorXML) 1.111 + NS_INTERFACE_MAP_ENTRY(nsIXULTemplateQueryProcessor) 1.112 + NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) 1.113 + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULTemplateQueryProcessor) 1.114 +NS_INTERFACE_MAP_END 1.115 + 1.116 +/* 1.117 + * Only the first datasource in aDataSource is used, which should be either an 1.118 + * nsIURI of an XML document, or a DOM node. If the former, GetDatasource will 1.119 + * load the document asynchronously and return null in aResult. Once the 1.120 + * document has loaded, the builder's datasource will be set to the XML 1.121 + * document. If the datasource is a DOM node, the node will be returned in 1.122 + * aResult. 1.123 + */ 1.124 +NS_IMETHODIMP 1.125 +nsXULTemplateQueryProcessorXML::GetDatasource(nsIArray* aDataSources, 1.126 + nsIDOMNode* aRootNode, 1.127 + bool aIsTrusted, 1.128 + nsIXULTemplateBuilder* aBuilder, 1.129 + bool* aShouldDelayBuilding, 1.130 + nsISupports** aResult) 1.131 +{ 1.132 + *aResult = nullptr; 1.133 + *aShouldDelayBuilding = false; 1.134 + 1.135 + nsresult rv; 1.136 + uint32_t length; 1.137 + 1.138 + aDataSources->GetLength(&length); 1.139 + if (length == 0) 1.140 + return NS_OK; 1.141 + 1.142 + // we get only the first item, because the query processor supports only 1.143 + // one document as a datasource 1.144 + 1.145 + nsCOMPtr<nsIDOMNode> node = do_QueryElementAt(aDataSources, 0); 1.146 + if (node) { 1.147 + return CallQueryInterface(node, aResult); 1.148 + } 1.149 + 1.150 + nsCOMPtr<nsIURI> uri = do_QueryElementAt(aDataSources, 0); 1.151 + if (!uri) 1.152 + return NS_ERROR_UNEXPECTED; 1.153 + 1.154 + nsAutoCString uriStr; 1.155 + rv = uri->GetSpec(uriStr); 1.156 + NS_ENSURE_SUCCESS(rv, rv); 1.157 + 1.158 + nsCOMPtr<nsIContent> root = do_QueryInterface(aRootNode); 1.159 + if (!root) 1.160 + return NS_ERROR_UNEXPECTED; 1.161 + 1.162 + nsCOMPtr<nsIDocument> doc = root->GetCurrentDoc(); 1.163 + if (!doc) 1.164 + return NS_ERROR_UNEXPECTED; 1.165 + 1.166 + nsIPrincipal *docPrincipal = doc->NodePrincipal(); 1.167 + 1.168 + bool hasHadScriptObject = true; 1.169 + nsIScriptGlobalObject* scriptObject = 1.170 + doc->GetScriptHandlingObject(hasHadScriptObject); 1.171 + NS_ENSURE_STATE(scriptObject); 1.172 + 1.173 + nsIScriptContext *context = scriptObject->GetContext(); 1.174 + NS_ENSURE_TRUE(context, NS_OK); 1.175 + 1.176 + nsCOMPtr<nsIXMLHttpRequest> req = 1.177 + do_CreateInstance(NS_XMLHTTPREQUEST_CONTRACTID, &rv); 1.178 + NS_ENSURE_SUCCESS(rv, rv); 1.179 + 1.180 + rv = req->Init(docPrincipal, context, 1.181 + scriptObject ? scriptObject : doc->GetScopeObject(), 1.182 + nullptr); 1.183 + NS_ENSURE_SUCCESS(rv, rv); 1.184 + 1.185 + rv = req->Open(NS_LITERAL_CSTRING("GET"), uriStr, true, 1.186 + EmptyString(), EmptyString()); 1.187 + NS_ENSURE_SUCCESS(rv, rv); 1.188 + 1.189 + nsCOMPtr<EventTarget> target(do_QueryInterface(req)); 1.190 + rv = target->AddEventListener(NS_LITERAL_STRING("load"), this, false); 1.191 + NS_ENSURE_SUCCESS(rv, rv); 1.192 + 1.193 + rv = target->AddEventListener(NS_LITERAL_STRING("error"), this, false); 1.194 + NS_ENSURE_SUCCESS(rv, rv); 1.195 + 1.196 + rv = req->Send(nullptr); 1.197 + NS_ENSURE_SUCCESS(rv, rv); 1.198 + 1.199 + mTemplateBuilder = aBuilder; 1.200 + mRequest = req; 1.201 + 1.202 + *aShouldDelayBuilding = true; 1.203 + return NS_OK; 1.204 +} 1.205 + 1.206 +NS_IMETHODIMP 1.207 +nsXULTemplateQueryProcessorXML::InitializeForBuilding(nsISupports* aDatasource, 1.208 + nsIXULTemplateBuilder* aBuilder, 1.209 + nsIDOMNode* aRootNode) 1.210 +{ 1.211 + if (mGenerationStarted) 1.212 + return NS_ERROR_UNEXPECTED; 1.213 + 1.214 + // the datasource is either a document or a DOM element 1.215 + nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(aDatasource); 1.216 + if (doc) 1.217 + doc->GetDocumentElement(getter_AddRefs(mRoot)); 1.218 + else 1.219 + mRoot = do_QueryInterface(aDatasource); 1.220 + NS_ENSURE_STATE(mRoot); 1.221 + 1.222 + mEvaluator = do_CreateInstance("@mozilla.org/dom/xpath-evaluator;1"); 1.223 + NS_ENSURE_TRUE(mEvaluator, NS_ERROR_OUT_OF_MEMORY); 1.224 + 1.225 + return NS_OK; 1.226 +} 1.227 + 1.228 +NS_IMETHODIMP 1.229 +nsXULTemplateQueryProcessorXML::Done() 1.230 +{ 1.231 + mGenerationStarted = false; 1.232 + 1.233 + mRuleToBindingsMap.Clear(); 1.234 + 1.235 + return NS_OK; 1.236 +} 1.237 + 1.238 +NS_IMETHODIMP 1.239 +nsXULTemplateQueryProcessorXML::CompileQuery(nsIXULTemplateBuilder* aBuilder, 1.240 + nsIDOMNode* aQueryNode, 1.241 + nsIAtom* aRefVariable, 1.242 + nsIAtom* aMemberVariable, 1.243 + nsISupports** _retval) 1.244 +{ 1.245 + nsresult rv = NS_OK; 1.246 + 1.247 + *_retval = nullptr; 1.248 + 1.249 + nsCOMPtr<nsIContent> content = do_QueryInterface(aQueryNode); 1.250 + 1.251 + nsAutoString expr; 1.252 + content->GetAttr(kNameSpaceID_None, nsGkAtoms::expr, expr); 1.253 + 1.254 + // if an expression is not specified, then the default is to 1.255 + // just take all of the children 1.256 + if (expr.IsEmpty()) 1.257 + expr.AssignLiteral("*"); 1.258 + 1.259 + nsCOMPtr<nsIDOMXPathExpression> compiledexpr; 1.260 + rv = CreateExpression(expr, aQueryNode, getter_AddRefs(compiledexpr)); 1.261 + if (NS_FAILED(rv)) { 1.262 + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BAD_XPATH); 1.263 + return rv; 1.264 + } 1.265 + 1.266 + nsRefPtr<nsXMLQuery> query = 1.267 + new nsXMLQuery(this, aMemberVariable, compiledexpr); 1.268 + NS_ENSURE_TRUE(query, NS_ERROR_OUT_OF_MEMORY); 1.269 + 1.270 + for (nsIContent* condition = content->GetFirstChild(); 1.271 + condition; 1.272 + condition = condition->GetNextSibling()) { 1.273 + 1.274 + if (condition->NodeInfo()->Equals(nsGkAtoms::assign, 1.275 + kNameSpaceID_XUL)) { 1.276 + nsAutoString var; 1.277 + condition->GetAttr(kNameSpaceID_None, nsGkAtoms::var, var); 1.278 + 1.279 + nsAutoString expr; 1.280 + condition->GetAttr(kNameSpaceID_None, nsGkAtoms::expr, expr); 1.281 + 1.282 + // ignore assignments without a variable or an expression 1.283 + if (!var.IsEmpty() && !expr.IsEmpty()) { 1.284 + nsCOMPtr<nsIDOMNode> conditionNode = 1.285 + do_QueryInterface(condition); 1.286 + rv = CreateExpression(expr, conditionNode, 1.287 + getter_AddRefs(compiledexpr)); 1.288 + if (NS_FAILED(rv)) { 1.289 + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BAD_ASSIGN_XPATH); 1.290 + return rv; 1.291 + } 1.292 + 1.293 + nsCOMPtr<nsIAtom> varatom = do_GetAtom(var); 1.294 + 1.295 + rv = query->AddBinding(varatom, compiledexpr); 1.296 + NS_ENSURE_SUCCESS(rv, rv); 1.297 + } 1.298 + } 1.299 + } 1.300 + 1.301 + *_retval = query; 1.302 + NS_ADDREF(*_retval); 1.303 + 1.304 + return rv; 1.305 +} 1.306 + 1.307 +NS_IMETHODIMP 1.308 +nsXULTemplateQueryProcessorXML::GenerateResults(nsISupports* aDatasource, 1.309 + nsIXULTemplateResult* aRef, 1.310 + nsISupports* aQuery, 1.311 + nsISimpleEnumerator** aResults) 1.312 +{ 1.313 + if (!aQuery) 1.314 + return NS_ERROR_INVALID_ARG; 1.315 + 1.316 + mGenerationStarted = true; 1.317 + 1.318 + nsCOMPtr<nsXMLQuery> xmlquery = do_QueryInterface(aQuery); 1.319 + if (!xmlquery) 1.320 + return NS_ERROR_INVALID_ARG; 1.321 + 1.322 + nsCOMPtr<nsISupports> supports; 1.323 + nsCOMPtr<nsIDOMNode> context; 1.324 + if (aRef) 1.325 + aRef->GetBindingObjectFor(xmlquery->GetMemberVariable(), 1.326 + getter_AddRefs(supports)); 1.327 + context = do_QueryInterface(supports); 1.328 + if (!context) 1.329 + context = mRoot; 1.330 + 1.331 + nsIDOMXPathExpression* expr = xmlquery->GetResultsExpression(); 1.332 + if (!expr) 1.333 + return NS_ERROR_FAILURE; 1.334 + 1.335 + nsCOMPtr<nsISupports> exprsupportsresults; 1.336 + nsresult rv = expr->Evaluate(context, 1.337 + nsIDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE, 1.338 + nullptr, getter_AddRefs(exprsupportsresults)); 1.339 + NS_ENSURE_SUCCESS(rv, rv); 1.340 + 1.341 + nsCOMPtr<nsIDOMXPathResult> exprresults = 1.342 + do_QueryInterface(exprsupportsresults); 1.343 + 1.344 + nsXULTemplateResultSetXML* results = 1.345 + new nsXULTemplateResultSetXML(xmlquery, exprresults, 1.346 + xmlquery->GetBindingSet()); 1.347 + NS_ENSURE_TRUE(results, NS_ERROR_OUT_OF_MEMORY); 1.348 + 1.349 + *aResults = results; 1.350 + NS_ADDREF(*aResults); 1.351 + 1.352 + return NS_OK; 1.353 +} 1.354 + 1.355 +NS_IMETHODIMP 1.356 +nsXULTemplateQueryProcessorXML::AddBinding(nsIDOMNode* aRuleNode, 1.357 + nsIAtom* aVar, 1.358 + nsIAtom* aRef, 1.359 + const nsAString& aExpr) 1.360 +{ 1.361 + if (mGenerationStarted) 1.362 + return NS_ERROR_FAILURE; 1.363 + 1.364 + nsRefPtr<nsXMLBindingSet> bindings = mRuleToBindingsMap.GetWeak(aRuleNode); 1.365 + if (!bindings) { 1.366 + bindings = new nsXMLBindingSet(); 1.367 + mRuleToBindingsMap.Put(aRuleNode, bindings); 1.368 + } 1.369 + 1.370 + nsCOMPtr<nsIDOMXPathExpression> compiledexpr; 1.371 + nsresult rv = 1.372 + CreateExpression(aExpr, aRuleNode, getter_AddRefs(compiledexpr)); 1.373 + if (NS_FAILED(rv)) { 1.374 + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BAD_BINDING_XPATH); 1.375 + return NS_OK; 1.376 + } 1.377 + 1.378 + // aRef isn't currently used for XML query processors 1.379 + return bindings->AddBinding(aVar, compiledexpr); 1.380 +} 1.381 + 1.382 +NS_IMETHODIMP 1.383 +nsXULTemplateQueryProcessorXML::TranslateRef(nsISupports* aDatasource, 1.384 + const nsAString& aRefString, 1.385 + nsIXULTemplateResult** aRef) 1.386 +{ 1.387 + *aRef = nullptr; 1.388 + 1.389 + // the datasource is either a document or a DOM element 1.390 + nsCOMPtr<nsIDOMElement> rootElement; 1.391 + nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(aDatasource); 1.392 + if (doc) 1.393 + doc->GetDocumentElement(getter_AddRefs(rootElement)); 1.394 + else 1.395 + rootElement = do_QueryInterface(aDatasource); 1.396 + 1.397 + // if no root element, just return. The document may not have loaded yet 1.398 + if (!rootElement) 1.399 + return NS_OK; 1.400 + 1.401 + nsXULTemplateResultXML* result = 1.402 + new nsXULTemplateResultXML(nullptr, rootElement, nullptr); 1.403 + NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY); 1.404 + 1.405 + *aRef = result; 1.406 + NS_ADDREF(*aRef); 1.407 + 1.408 + return NS_OK; 1.409 +} 1.410 + 1.411 + 1.412 +NS_IMETHODIMP 1.413 +nsXULTemplateQueryProcessorXML::CompareResults(nsIXULTemplateResult* aLeft, 1.414 + nsIXULTemplateResult* aRight, 1.415 + nsIAtom* aVar, 1.416 + uint32_t aSortHints, 1.417 + int32_t* aResult) 1.418 +{ 1.419 + *aResult = 0; 1.420 + if (!aVar) 1.421 + return NS_OK; 1.422 + 1.423 + nsAutoString leftVal; 1.424 + if (aLeft) 1.425 + aLeft->GetBindingFor(aVar, leftVal); 1.426 + 1.427 + nsAutoString rightVal; 1.428 + if (aRight) 1.429 + aRight->GetBindingFor(aVar, rightVal); 1.430 + 1.431 + *aResult = XULSortServiceImpl::CompareValues(leftVal, rightVal, aSortHints); 1.432 + return NS_OK; 1.433 +} 1.434 + 1.435 +nsXMLBindingSet* 1.436 +nsXULTemplateQueryProcessorXML::GetOptionalBindingsForRule(nsIDOMNode* aRuleNode) 1.437 +{ 1.438 + return mRuleToBindingsMap.GetWeak(aRuleNode); 1.439 +} 1.440 + 1.441 +nsresult 1.442 +nsXULTemplateQueryProcessorXML::CreateExpression(const nsAString& aExpr, 1.443 + nsIDOMNode* aNode, 1.444 + nsIDOMXPathExpression** aCompiledExpr) 1.445 +{ 1.446 + nsCOMPtr<nsIDOMXPathNSResolver> nsResolver; 1.447 + 1.448 + nsCOMPtr<nsIDOMDocument> doc; 1.449 + aNode->GetOwnerDocument(getter_AddRefs(doc)); 1.450 + 1.451 + nsCOMPtr<nsIDOMXPathEvaluator> eval = do_QueryInterface(doc); 1.452 + if (eval) { 1.453 + nsresult rv = 1.454 + eval->CreateNSResolver(aNode, getter_AddRefs(nsResolver)); 1.455 + NS_ENSURE_SUCCESS(rv, rv); 1.456 + } 1.457 + 1.458 + return mEvaluator->CreateExpression(aExpr, nsResolver, aCompiledExpr); 1.459 +} 1.460 + 1.461 +NS_IMETHODIMP 1.462 +nsXULTemplateQueryProcessorXML::HandleEvent(nsIDOMEvent* aEvent) 1.463 +{ 1.464 + NS_PRECONDITION(aEvent, "aEvent null"); 1.465 + nsAutoString eventType; 1.466 + aEvent->GetType(eventType); 1.467 + 1.468 + if (eventType.EqualsLiteral("load") && mTemplateBuilder) { 1.469 + NS_ASSERTION(mRequest, "request was not set"); 1.470 + nsCOMPtr<nsIDOMDocument> doc; 1.471 + if (NS_SUCCEEDED(mRequest->GetResponseXML(getter_AddRefs(doc)))) 1.472 + mTemplateBuilder->SetDatasource(doc); 1.473 + 1.474 + // to avoid leak. we don't need it after... 1.475 + mTemplateBuilder = nullptr; 1.476 + mRequest = nullptr; 1.477 + } 1.478 + else if (eventType.EqualsLiteral("error")) { 1.479 + mTemplateBuilder = nullptr; 1.480 + mRequest = nullptr; 1.481 + } 1.482 + 1.483 + return NS_OK; 1.484 +}