diff options
Diffstat (limited to 'dom/xul/templates/nsXULTemplateQueryProcessorXML.cpp')
-rw-r--r-- | dom/xul/templates/nsXULTemplateQueryProcessorXML.cpp | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/dom/xul/templates/nsXULTemplateQueryProcessorXML.cpp b/dom/xul/templates/nsXULTemplateQueryProcessorXML.cpp new file mode 100644 index 000000000..1c6fed252 --- /dev/null +++ b/dom/xul/templates/nsXULTemplateQueryProcessorXML.cpp @@ -0,0 +1,449 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsCOMPtr.h" +#include "nsAutoPtr.h" +#include "nsIDOMDocument.h" +#include "nsIDOMNode.h" +#include "nsIDOMElement.h" +#include "nsIDOMEvent.h" +#include "nsIDocument.h" +#include "nsIContent.h" +#include "nsComponentManagerUtils.h" +#include "nsGkAtoms.h" +#include "nsIURI.h" +#include "nsIArray.h" +#include "nsIScriptContext.h" +#include "nsArrayUtils.h" +#include "nsPIDOMWindow.h" +#include "nsXULContentUtils.h" +#include "mozilla/dom/XPathEvaluator.h" +#include "nsXULTemplateQueryProcessorXML.h" +#include "nsXULTemplateResultXML.h" +#include "nsXULSortService.h" +#include "mozilla/dom/Element.h" +#include "mozilla/dom/XMLHttpRequest.h" + +using namespace mozilla; +using namespace mozilla::dom; + +NS_IMPL_ISUPPORTS(nsXMLQuery, nsXMLQuery) + +//---------------------------------------------------------------------- +// +// nsXULTemplateResultSetXML +// + +NS_IMPL_ISUPPORTS(nsXULTemplateResultSetXML, nsISimpleEnumerator) + +NS_IMETHODIMP +nsXULTemplateResultSetXML::HasMoreElements(bool *aResult) +{ + // if GetSnapshotLength failed, then the return type was not a set of + // nodes, so just return false in this case. + ErrorResult rv; + uint32_t length = mResults->GetSnapshotLength(rv); + if (NS_WARN_IF(rv.Failed())) { + rv.SuppressException(); + *aResult = false; + return NS_OK; + } + + *aResult = mPosition < length; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultSetXML::GetNext(nsISupports **aResult) +{ + ErrorResult rv; + nsINode* node = mResults->SnapshotItem(mPosition, rv); + if (rv.Failed()) { + return rv.StealNSResult(); + } + + nsXULTemplateResultXML* result = + new nsXULTemplateResultXML(mQuery, node->AsContent(), mBindingSet); + + ++mPosition; + *aResult = result; + NS_ADDREF(result); + return NS_OK; +} + + +//---------------------------------------------------------------------- +// +// nsXULTemplateQueryProcessorXML +// + +NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULTemplateQueryProcessorXML) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULTemplateQueryProcessorXML) + tmp->mRuleToBindingsMap.Clear(); + NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mEvaluator) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateBuilder) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mRequest) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULTemplateQueryProcessorXML) + for (auto it = tmp->mRuleToBindingsMap.Iter(); !it.Done(); it.Next()) { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mRuleToBindingsMap key"); + cb.NoteXPCOMChild(it.Key()); + } + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvaluator) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateBuilder) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRequest) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULTemplateQueryProcessorXML) +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULTemplateQueryProcessorXML) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULTemplateQueryProcessorXML) + NS_INTERFACE_MAP_ENTRY(nsIXULTemplateQueryProcessor) + NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULTemplateQueryProcessor) +NS_INTERFACE_MAP_END + +/* + * Only the first datasource in aDataSource is used, which should be either an + * nsIURI of an XML document, or a DOM node. If the former, GetDatasource will + * load the document asynchronously and return null in aResult. Once the + * document has loaded, the builder's datasource will be set to the XML + * document. If the datasource is a DOM node, the node will be returned in + * aResult. + */ +NS_IMETHODIMP +nsXULTemplateQueryProcessorXML::GetDatasource(nsIArray* aDataSources, + nsIDOMNode* aRootNode, + bool aIsTrusted, + nsIXULTemplateBuilder* aBuilder, + bool* aShouldDelayBuilding, + nsISupports** aResult) +{ + *aResult = nullptr; + *aShouldDelayBuilding = false; + + nsresult rv; + uint32_t length; + + aDataSources->GetLength(&length); + if (length == 0) + return NS_OK; + + // we get only the first item, because the query processor supports only + // one document as a datasource + + nsCOMPtr<nsIDOMNode> node = do_QueryElementAt(aDataSources, 0); + if (node) { + return CallQueryInterface(node, aResult); + } + + nsCOMPtr<nsIURI> uri = do_QueryElementAt(aDataSources, 0); + if (!uri) + return NS_ERROR_UNEXPECTED; + + nsAutoCString uriStr; + rv = uri->GetSpec(uriStr); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIContent> root = do_QueryInterface(aRootNode); + if (!root) + return NS_ERROR_UNEXPECTED; + + nsCOMPtr<nsIDocument> doc = root->GetUncomposedDoc(); + if (!doc) + return NS_ERROR_UNEXPECTED; + + nsIPrincipal *docPrincipal = doc->NodePrincipal(); + + bool hasHadScriptObject = true; + nsIScriptGlobalObject* scriptObject = + doc->GetScriptHandlingObject(hasHadScriptObject); + NS_ENSURE_STATE(scriptObject); + + nsCOMPtr<nsIXMLHttpRequest> req = + do_CreateInstance(NS_XMLHTTPREQUEST_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = req->Init(docPrincipal, scriptObject, nullptr, nullptr); + NS_ENSURE_SUCCESS(rv, rv); + + rv = req->Open(NS_LITERAL_CSTRING("GET"), uriStr, true, + EmptyString(), EmptyString()); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<EventTarget> target(do_QueryInterface(req)); + rv = target->AddEventListener(NS_LITERAL_STRING("load"), this, false); + NS_ENSURE_SUCCESS(rv, rv); + + rv = target->AddEventListener(NS_LITERAL_STRING("error"), this, false); + NS_ENSURE_SUCCESS(rv, rv); + + rv = req->Send(nullptr); + NS_ENSURE_SUCCESS(rv, rv); + + mTemplateBuilder = aBuilder; + mRequest = req; + + *aShouldDelayBuilding = true; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorXML::InitializeForBuilding(nsISupports* aDatasource, + nsIXULTemplateBuilder* aBuilder, + nsIDOMNode* aRootNode) +{ + if (mGenerationStarted) + return NS_ERROR_UNEXPECTED; + + // the datasource is either a document or a DOM element + nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDatasource); + if (doc) + mRoot = doc->GetDocumentElement(); + else + mRoot = do_QueryInterface(aDatasource); + NS_ENSURE_STATE(mRoot); + + mEvaluator = new XPathEvaluator(); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorXML::Done() +{ + mGenerationStarted = false; + + mRuleToBindingsMap.Clear(); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorXML::CompileQuery(nsIXULTemplateBuilder* aBuilder, + nsIDOMNode* aQueryNode, + nsIAtom* aRefVariable, + nsIAtom* aMemberVariable, + nsISupports** _retval) +{ + *_retval = nullptr; + + nsCOMPtr<nsIContent> content = do_QueryInterface(aQueryNode); + + nsAutoString expr; + content->GetAttr(kNameSpaceID_None, nsGkAtoms::expr, expr); + + // if an expression is not specified, then the default is to + // just take all of the children + if (expr.IsEmpty()) + expr.Assign('*'); + + ErrorResult rv; + nsAutoPtr<XPathExpression> compiledexpr; + compiledexpr = CreateExpression(expr, content, rv); + if (rv.Failed()) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BAD_XPATH); + return rv.StealNSResult(); + } + + RefPtr<nsXMLQuery> query = + new nsXMLQuery(this, aMemberVariable, Move(compiledexpr)); + + for (nsIContent* condition = content->GetFirstChild(); + condition; + condition = condition->GetNextSibling()) { + + if (condition->NodeInfo()->Equals(nsGkAtoms::assign, + kNameSpaceID_XUL)) { + nsAutoString var; + condition->GetAttr(kNameSpaceID_None, nsGkAtoms::var, var); + + nsAutoString expr; + condition->GetAttr(kNameSpaceID_None, nsGkAtoms::expr, expr); + + // ignore assignments without a variable or an expression + if (!var.IsEmpty() && !expr.IsEmpty()) { + compiledexpr = CreateExpression(expr, condition, rv); + if (rv.Failed()) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BAD_ASSIGN_XPATH); + return rv.StealNSResult(); + } + + nsCOMPtr<nsIAtom> varatom = NS_Atomize(var); + + query->AddBinding(varatom, Move(compiledexpr)); + } + } + } + + query.forget(_retval); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorXML::GenerateResults(nsISupports* aDatasource, + nsIXULTemplateResult* aRef, + nsISupports* aQuery, + nsISimpleEnumerator** aResults) +{ + if (!aQuery) + return NS_ERROR_INVALID_ARG; + + mGenerationStarted = true; + + nsCOMPtr<nsXMLQuery> xmlquery = do_QueryInterface(aQuery); + if (!xmlquery) + return NS_ERROR_INVALID_ARG; + + nsCOMPtr<nsISupports> supports; + nsCOMPtr<nsINode> context; + if (aRef) + aRef->GetBindingObjectFor(xmlquery->GetMemberVariable(), + getter_AddRefs(supports)); + context = do_QueryInterface(supports); + if (!context) + context = mRoot; + + XPathExpression* expr = xmlquery->GetResultsExpression(); + if (!expr) + return NS_ERROR_FAILURE; + + ErrorResult rv; + RefPtr<XPathResult> exprresults = + expr->Evaluate(*context, XPathResult::ORDERED_NODE_SNAPSHOT_TYPE, + nullptr, rv); + if (rv.Failed()) { + return rv.StealNSResult(); + } + + RefPtr<nsXULTemplateResultSetXML> results = + new nsXULTemplateResultSetXML(xmlquery, exprresults.forget(), + xmlquery->GetBindingSet()); + + results.forget(aResults); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorXML::AddBinding(nsIDOMNode* aRuleNode, + nsIAtom* aVar, + nsIAtom* aRef, + const nsAString& aExpr) +{ + if (mGenerationStarted) + return NS_ERROR_FAILURE; + + RefPtr<nsXMLBindingSet> bindings = mRuleToBindingsMap.GetWeak(aRuleNode); + if (!bindings) { + bindings = new nsXMLBindingSet(); + mRuleToBindingsMap.Put(aRuleNode, bindings); + } + + nsCOMPtr<nsINode> ruleNode = do_QueryInterface(aRuleNode); + + ErrorResult rv; + nsAutoPtr<XPathExpression> compiledexpr; + compiledexpr = CreateExpression(aExpr, ruleNode, rv); + if (rv.Failed()) { + rv.SuppressException(); + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BAD_BINDING_XPATH); + return NS_OK; + } + + // aRef isn't currently used for XML query processors + bindings->AddBinding(aVar, Move(compiledexpr)); + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorXML::TranslateRef(nsISupports* aDatasource, + const nsAString& aRefString, + nsIXULTemplateResult** aRef) +{ + *aRef = nullptr; + + // the datasource is either a document or a DOM element + nsCOMPtr<Element> rootElement; + nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDatasource); + if (doc) + rootElement = doc->GetRootElement(); + else + rootElement = do_QueryInterface(aDatasource); + + // if no root element, just return. The document may not have loaded yet + if (!rootElement) + return NS_OK; + + RefPtr<nsXULTemplateResultXML> result = new nsXULTemplateResultXML(nullptr, rootElement, nullptr); + result.forget(aRef); + + return NS_OK; +} + + +NS_IMETHODIMP +nsXULTemplateQueryProcessorXML::CompareResults(nsIXULTemplateResult* aLeft, + nsIXULTemplateResult* aRight, + nsIAtom* aVar, + uint32_t aSortHints, + int32_t* aResult) +{ + *aResult = 0; + if (!aVar) + return NS_OK; + + nsAutoString leftVal; + if (aLeft) + aLeft->GetBindingFor(aVar, leftVal); + + nsAutoString rightVal; + if (aRight) + aRight->GetBindingFor(aVar, rightVal); + + *aResult = XULSortServiceImpl::CompareValues(leftVal, rightVal, aSortHints); + return NS_OK; +} + +nsXMLBindingSet* +nsXULTemplateQueryProcessorXML::GetOptionalBindingsForRule(nsIDOMNode* aRuleNode) +{ + return mRuleToBindingsMap.GetWeak(aRuleNode); +} + +XPathExpression* +nsXULTemplateQueryProcessorXML::CreateExpression(const nsAString& aExpr, + nsINode* aNode, + ErrorResult& aRv) +{ + return mEvaluator->CreateExpression(aExpr, aNode, aRv); +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorXML::HandleEvent(nsIDOMEvent* aEvent) +{ + NS_PRECONDITION(aEvent, "aEvent null"); + nsAutoString eventType; + aEvent->GetType(eventType); + + if (eventType.EqualsLiteral("load") && mTemplateBuilder) { + NS_ASSERTION(mRequest, "request was not set"); + nsCOMPtr<nsIDOMDocument> doc; + if (NS_SUCCEEDED(mRequest->GetResponseXML(getter_AddRefs(doc)))) + mTemplateBuilder->SetDatasource(doc); + + // to avoid leak. we don't need it after... + mTemplateBuilder = nullptr; + mRequest = nullptr; + } + else if (eventType.EqualsLiteral("error")) { + mTemplateBuilder = nullptr; + mRequest = nullptr; + } + + return NS_OK; +} |