michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsIDOMDocument.h" michael@0: #include "nsIDOMNode.h" michael@0: #include "nsIDOMElement.h" michael@0: #include "nsIDOMEvent.h" michael@0: #include "nsIDOMXPathNSResolver.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIContent.h" michael@0: #include "nsComponentManagerUtils.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsIURI.h" michael@0: #include "nsIArray.h" michael@0: #include "nsIScriptContext.h" michael@0: #include "nsArrayUtils.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsXULContentUtils.h" michael@0: #include "nsXMLHttpRequest.h" michael@0: michael@0: #include "nsXULTemplateQueryProcessorXML.h" michael@0: #include "nsXULTemplateResultXML.h" michael@0: #include "nsXULSortService.h" michael@0: michael@0: using namespace mozilla::dom; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsXMLQuery, nsXMLQuery) michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // michael@0: // nsXULTemplateResultSetXML michael@0: // michael@0: michael@0: NS_IMPL_ISUPPORTS(nsXULTemplateResultSetXML, nsISimpleEnumerator) michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTemplateResultSetXML::HasMoreElements(bool *aResult) michael@0: { michael@0: // if GetSnapshotLength failed, then the return type was not a set of michael@0: // nodes, so just return false in this case. michael@0: uint32_t length; michael@0: if (NS_SUCCEEDED(mResults->GetSnapshotLength(&length))) michael@0: *aResult = (mPosition < length); michael@0: else michael@0: *aResult = false; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTemplateResultSetXML::GetNext(nsISupports **aResult) michael@0: { michael@0: nsCOMPtr node; michael@0: nsresult rv = mResults->SnapshotItem(mPosition, getter_AddRefs(node)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsXULTemplateResultXML* result = michael@0: new nsXULTemplateResultXML(mQuery, node, mBindingSet); michael@0: NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: ++mPosition; michael@0: *aResult = result; michael@0: NS_ADDREF(result); michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: //---------------------------------------------------------------------- michael@0: // michael@0: // nsXULTemplateQueryProcessorXML michael@0: // michael@0: michael@0: static PLDHashOperator michael@0: TraverseRuleToBindingsMap(nsISupports* aKey, nsXMLBindingSet* aMatch, void* aContext) michael@0: { michael@0: nsCycleCollectionTraversalCallback *cb = michael@0: static_cast(aContext); michael@0: NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mRuleToBindingsMap key"); michael@0: cb->NoteXPCOMChild(aKey); michael@0: NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mRuleToBindingsMap value"); michael@0: cb->NoteNativeChild(aMatch, NS_CYCLE_COLLECTION_PARTICIPANT(nsXMLBindingSet)); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULTemplateQueryProcessorXML) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULTemplateQueryProcessorXML) michael@0: tmp->mRuleToBindingsMap.Clear(); michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mEvaluator) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mTemplateBuilder) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mRequest) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULTemplateQueryProcessorXML) michael@0: tmp->mRuleToBindingsMap.EnumerateRead(TraverseRuleToBindingsMap, &cb); michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEvaluator) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTemplateBuilder) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRequest) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULTemplateQueryProcessorXML) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULTemplateQueryProcessorXML) michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULTemplateQueryProcessorXML) michael@0: NS_INTERFACE_MAP_ENTRY(nsIXULTemplateQueryProcessor) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULTemplateQueryProcessor) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: /* michael@0: * Only the first datasource in aDataSource is used, which should be either an michael@0: * nsIURI of an XML document, or a DOM node. If the former, GetDatasource will michael@0: * load the document asynchronously and return null in aResult. Once the michael@0: * document has loaded, the builder's datasource will be set to the XML michael@0: * document. If the datasource is a DOM node, the node will be returned in michael@0: * aResult. michael@0: */ michael@0: NS_IMETHODIMP michael@0: nsXULTemplateQueryProcessorXML::GetDatasource(nsIArray* aDataSources, michael@0: nsIDOMNode* aRootNode, michael@0: bool aIsTrusted, michael@0: nsIXULTemplateBuilder* aBuilder, michael@0: bool* aShouldDelayBuilding, michael@0: nsISupports** aResult) michael@0: { michael@0: *aResult = nullptr; michael@0: *aShouldDelayBuilding = false; michael@0: michael@0: nsresult rv; michael@0: uint32_t length; michael@0: michael@0: aDataSources->GetLength(&length); michael@0: if (length == 0) michael@0: return NS_OK; michael@0: michael@0: // we get only the first item, because the query processor supports only michael@0: // one document as a datasource michael@0: michael@0: nsCOMPtr node = do_QueryElementAt(aDataSources, 0); michael@0: if (node) { michael@0: return CallQueryInterface(node, aResult); michael@0: } michael@0: michael@0: nsCOMPtr uri = do_QueryElementAt(aDataSources, 0); michael@0: if (!uri) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: nsAutoCString uriStr; michael@0: rv = uri->GetSpec(uriStr); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr root = do_QueryInterface(aRootNode); michael@0: if (!root) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: nsCOMPtr doc = root->GetCurrentDoc(); michael@0: if (!doc) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: nsIPrincipal *docPrincipal = doc->NodePrincipal(); michael@0: michael@0: bool hasHadScriptObject = true; michael@0: nsIScriptGlobalObject* scriptObject = michael@0: doc->GetScriptHandlingObject(hasHadScriptObject); michael@0: NS_ENSURE_STATE(scriptObject); michael@0: michael@0: nsIScriptContext *context = scriptObject->GetContext(); michael@0: NS_ENSURE_TRUE(context, NS_OK); michael@0: michael@0: nsCOMPtr req = michael@0: do_CreateInstance(NS_XMLHTTPREQUEST_CONTRACTID, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = req->Init(docPrincipal, context, michael@0: scriptObject ? scriptObject : doc->GetScopeObject(), michael@0: nullptr); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = req->Open(NS_LITERAL_CSTRING("GET"), uriStr, true, michael@0: EmptyString(), EmptyString()); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr target(do_QueryInterface(req)); michael@0: rv = target->AddEventListener(NS_LITERAL_STRING("load"), this, false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = target->AddEventListener(NS_LITERAL_STRING("error"), this, false); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = req->Send(nullptr); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mTemplateBuilder = aBuilder; michael@0: mRequest = req; michael@0: michael@0: *aShouldDelayBuilding = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTemplateQueryProcessorXML::InitializeForBuilding(nsISupports* aDatasource, michael@0: nsIXULTemplateBuilder* aBuilder, michael@0: nsIDOMNode* aRootNode) michael@0: { michael@0: if (mGenerationStarted) michael@0: return NS_ERROR_UNEXPECTED; michael@0: michael@0: // the datasource is either a document or a DOM element michael@0: nsCOMPtr doc = do_QueryInterface(aDatasource); michael@0: if (doc) michael@0: doc->GetDocumentElement(getter_AddRefs(mRoot)); michael@0: else michael@0: mRoot = do_QueryInterface(aDatasource); michael@0: NS_ENSURE_STATE(mRoot); michael@0: michael@0: mEvaluator = do_CreateInstance("@mozilla.org/dom/xpath-evaluator;1"); michael@0: NS_ENSURE_TRUE(mEvaluator, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTemplateQueryProcessorXML::Done() michael@0: { michael@0: mGenerationStarted = false; michael@0: michael@0: mRuleToBindingsMap.Clear(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTemplateQueryProcessorXML::CompileQuery(nsIXULTemplateBuilder* aBuilder, michael@0: nsIDOMNode* aQueryNode, michael@0: nsIAtom* aRefVariable, michael@0: nsIAtom* aMemberVariable, michael@0: nsISupports** _retval) michael@0: { michael@0: nsresult rv = NS_OK; michael@0: michael@0: *_retval = nullptr; michael@0: michael@0: nsCOMPtr content = do_QueryInterface(aQueryNode); michael@0: michael@0: nsAutoString expr; michael@0: content->GetAttr(kNameSpaceID_None, nsGkAtoms::expr, expr); michael@0: michael@0: // if an expression is not specified, then the default is to michael@0: // just take all of the children michael@0: if (expr.IsEmpty()) michael@0: expr.AssignLiteral("*"); michael@0: michael@0: nsCOMPtr compiledexpr; michael@0: rv = CreateExpression(expr, aQueryNode, getter_AddRefs(compiledexpr)); michael@0: if (NS_FAILED(rv)) { michael@0: nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BAD_XPATH); michael@0: return rv; michael@0: } michael@0: michael@0: nsRefPtr query = michael@0: new nsXMLQuery(this, aMemberVariable, compiledexpr); michael@0: NS_ENSURE_TRUE(query, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: for (nsIContent* condition = content->GetFirstChild(); michael@0: condition; michael@0: condition = condition->GetNextSibling()) { michael@0: michael@0: if (condition->NodeInfo()->Equals(nsGkAtoms::assign, michael@0: kNameSpaceID_XUL)) { michael@0: nsAutoString var; michael@0: condition->GetAttr(kNameSpaceID_None, nsGkAtoms::var, var); michael@0: michael@0: nsAutoString expr; michael@0: condition->GetAttr(kNameSpaceID_None, nsGkAtoms::expr, expr); michael@0: michael@0: // ignore assignments without a variable or an expression michael@0: if (!var.IsEmpty() && !expr.IsEmpty()) { michael@0: nsCOMPtr conditionNode = michael@0: do_QueryInterface(condition); michael@0: rv = CreateExpression(expr, conditionNode, michael@0: getter_AddRefs(compiledexpr)); michael@0: if (NS_FAILED(rv)) { michael@0: nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BAD_ASSIGN_XPATH); michael@0: return rv; michael@0: } michael@0: michael@0: nsCOMPtr varatom = do_GetAtom(var); michael@0: michael@0: rv = query->AddBinding(varatom, compiledexpr); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: } michael@0: } michael@0: michael@0: *_retval = query; michael@0: NS_ADDREF(*_retval); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTemplateQueryProcessorXML::GenerateResults(nsISupports* aDatasource, michael@0: nsIXULTemplateResult* aRef, michael@0: nsISupports* aQuery, michael@0: nsISimpleEnumerator** aResults) michael@0: { michael@0: if (!aQuery) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: mGenerationStarted = true; michael@0: michael@0: nsCOMPtr xmlquery = do_QueryInterface(aQuery); michael@0: if (!xmlquery) michael@0: return NS_ERROR_INVALID_ARG; michael@0: michael@0: nsCOMPtr supports; michael@0: nsCOMPtr context; michael@0: if (aRef) michael@0: aRef->GetBindingObjectFor(xmlquery->GetMemberVariable(), michael@0: getter_AddRefs(supports)); michael@0: context = do_QueryInterface(supports); michael@0: if (!context) michael@0: context = mRoot; michael@0: michael@0: nsIDOMXPathExpression* expr = xmlquery->GetResultsExpression(); michael@0: if (!expr) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsCOMPtr exprsupportsresults; michael@0: nsresult rv = expr->Evaluate(context, michael@0: nsIDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE, michael@0: nullptr, getter_AddRefs(exprsupportsresults)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr exprresults = michael@0: do_QueryInterface(exprsupportsresults); michael@0: michael@0: nsXULTemplateResultSetXML* results = michael@0: new nsXULTemplateResultSetXML(xmlquery, exprresults, michael@0: xmlquery->GetBindingSet()); michael@0: NS_ENSURE_TRUE(results, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: *aResults = results; michael@0: NS_ADDREF(*aResults); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTemplateQueryProcessorXML::AddBinding(nsIDOMNode* aRuleNode, michael@0: nsIAtom* aVar, michael@0: nsIAtom* aRef, michael@0: const nsAString& aExpr) michael@0: { michael@0: if (mGenerationStarted) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsRefPtr bindings = mRuleToBindingsMap.GetWeak(aRuleNode); michael@0: if (!bindings) { michael@0: bindings = new nsXMLBindingSet(); michael@0: mRuleToBindingsMap.Put(aRuleNode, bindings); michael@0: } michael@0: michael@0: nsCOMPtr compiledexpr; michael@0: nsresult rv = michael@0: CreateExpression(aExpr, aRuleNode, getter_AddRefs(compiledexpr)); michael@0: if (NS_FAILED(rv)) { michael@0: nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BAD_BINDING_XPATH); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // aRef isn't currently used for XML query processors michael@0: return bindings->AddBinding(aVar, compiledexpr); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTemplateQueryProcessorXML::TranslateRef(nsISupports* aDatasource, michael@0: const nsAString& aRefString, michael@0: nsIXULTemplateResult** aRef) michael@0: { michael@0: *aRef = nullptr; michael@0: michael@0: // the datasource is either a document or a DOM element michael@0: nsCOMPtr rootElement; michael@0: nsCOMPtr doc = do_QueryInterface(aDatasource); michael@0: if (doc) michael@0: doc->GetDocumentElement(getter_AddRefs(rootElement)); michael@0: else michael@0: rootElement = do_QueryInterface(aDatasource); michael@0: michael@0: // if no root element, just return. The document may not have loaded yet michael@0: if (!rootElement) michael@0: return NS_OK; michael@0: michael@0: nsXULTemplateResultXML* result = michael@0: new nsXULTemplateResultXML(nullptr, rootElement, nullptr); michael@0: NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: *aRef = result; michael@0: NS_ADDREF(*aRef); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTemplateQueryProcessorXML::CompareResults(nsIXULTemplateResult* aLeft, michael@0: nsIXULTemplateResult* aRight, michael@0: nsIAtom* aVar, michael@0: uint32_t aSortHints, michael@0: int32_t* aResult) michael@0: { michael@0: *aResult = 0; michael@0: if (!aVar) michael@0: return NS_OK; michael@0: michael@0: nsAutoString leftVal; michael@0: if (aLeft) michael@0: aLeft->GetBindingFor(aVar, leftVal); michael@0: michael@0: nsAutoString rightVal; michael@0: if (aRight) michael@0: aRight->GetBindingFor(aVar, rightVal); michael@0: michael@0: *aResult = XULSortServiceImpl::CompareValues(leftVal, rightVal, aSortHints); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsXMLBindingSet* michael@0: nsXULTemplateQueryProcessorXML::GetOptionalBindingsForRule(nsIDOMNode* aRuleNode) michael@0: { michael@0: return mRuleToBindingsMap.GetWeak(aRuleNode); michael@0: } michael@0: michael@0: nsresult michael@0: nsXULTemplateQueryProcessorXML::CreateExpression(const nsAString& aExpr, michael@0: nsIDOMNode* aNode, michael@0: nsIDOMXPathExpression** aCompiledExpr) michael@0: { michael@0: nsCOMPtr nsResolver; michael@0: michael@0: nsCOMPtr doc; michael@0: aNode->GetOwnerDocument(getter_AddRefs(doc)); michael@0: michael@0: nsCOMPtr eval = do_QueryInterface(doc); michael@0: if (eval) { michael@0: nsresult rv = michael@0: eval->CreateNSResolver(aNode, getter_AddRefs(nsResolver)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return mEvaluator->CreateExpression(aExpr, nsResolver, aCompiledExpr); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsXULTemplateQueryProcessorXML::HandleEvent(nsIDOMEvent* aEvent) michael@0: { michael@0: NS_PRECONDITION(aEvent, "aEvent null"); michael@0: nsAutoString eventType; michael@0: aEvent->GetType(eventType); michael@0: michael@0: if (eventType.EqualsLiteral("load") && mTemplateBuilder) { michael@0: NS_ASSERTION(mRequest, "request was not set"); michael@0: nsCOMPtr doc; michael@0: if (NS_SUCCEEDED(mRequest->GetResponseXML(getter_AddRefs(doc)))) michael@0: mTemplateBuilder->SetDatasource(doc); michael@0: michael@0: // to avoid leak. we don't need it after... michael@0: mTemplateBuilder = nullptr; michael@0: mRequest = nullptr; michael@0: } michael@0: else if (eventType.EqualsLiteral("error")) { michael@0: mTemplateBuilder = nullptr; michael@0: mRequest = nullptr; michael@0: } michael@0: michael@0: return NS_OK; michael@0: }