diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h index fe1b9af5f6dc..81d7a905c393 100755 --- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -103,6 +103,7 @@ GK_ATOM(applyTemplates, "apply-templates") GK_ATOM(archive, "archive") GK_ATOM(area, "area") GK_ATOM(ascending, "ascending") +GK_ATOM(assign, "assign") GK_ATOM(attribute, "attribute") GK_ATOM(attributeSet, "attribute-set") GK_ATOM(aural, "aural") @@ -320,6 +321,7 @@ GK_ATOM(event, "event") GK_ATOM(events, "events") GK_ATOM(excludeResultPrefixes, "exclude-result-prefixes") GK_ATOM(excludes, "excludes") +GK_ATOM(expr, "expr") GK_ATOM(extends, "extends") GK_ATOM(extensionElementPrefixes, "extension-element-prefixes") GK_ATOM(face, "face") diff --git a/content/xul/templates/src/Makefile.in b/content/xul/templates/src/Makefile.in index cffb6e4a7247..e88e5d1becee 100644 --- a/content/xul/templates/src/Makefile.in +++ b/content/xul/templates/src/Makefile.in @@ -87,6 +87,9 @@ CPPSRCS = \ nsXULTemplateQueryProcessorRDF.cpp \ nsXULTemplateResultRDF.cpp \ nsXULTemplateResultSetRDF.cpp \ + nsXMLBinding.cpp \ + nsXULTemplateQueryProcessorXML.cpp \ + nsXULTemplateResultXML.cpp \ $(NULL) # we don't want the shared lib, but we want to force the creation of a static lib. diff --git a/content/xul/templates/src/nsTemplateMatch.cpp b/content/xul/templates/src/nsTemplateMatch.cpp index 89aac5e58527..3919742ac743 100644 --- a/content/xul/templates/src/nsTemplateMatch.cpp +++ b/content/xul/templates/src/nsTemplateMatch.cpp @@ -68,10 +68,8 @@ nsTemplateMatch::RuleMatched(nsTemplateQuerySet* aQuerySet, nsCOMPtr rulenode; aRule->GetRuleNode(getter_AddRefs(rulenode)); - if (rulenode) { - nsCOMPtr querynode = do_QueryInterface(aQuerySet->mQueryNode); - return aResult->RuleMatched(querynode, rulenode); - } + if (rulenode) + return aResult->RuleMatched(aQuerySet->mCompiledQuery, rulenode); return NS_OK; } diff --git a/content/xul/templates/src/nsXMLBinding.cpp b/content/xul/templates/src/nsXMLBinding.cpp new file mode 100644 index 000000000000..9f34a6ac4a35 --- /dev/null +++ b/content/xul/templates/src/nsXMLBinding.cpp @@ -0,0 +1,152 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Neil Deakin + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsXULTemplateQueryProcessorXML.h" +#include "nsXULTemplateResultXML.h" +#include "nsXMLBinding.h" + +NS_IMPL_ADDREF(nsXMLBindingSet); +NS_IMPL_RELEASE(nsXMLBindingSet); + +nsresult +nsXMLBindingSet::AddBinding(nsIAtom* aVar, nsIDOMXPathExpression* aExpr) +{ + nsAutoPtr newbinding(new nsXMLBinding(aVar, aExpr)); + NS_ENSURE_TRUE(newbinding, NS_ERROR_OUT_OF_MEMORY); + + if (mFirst) { + nsXMLBinding* binding = mFirst; + + while (binding) { + // if the target variable is already used in a binding, ignore it + // since it won't be useful for anything + if (binding->mVar == aVar) + return NS_OK; + + // add the binding at the end of the list + if (!binding->mNext) { + binding->mNext = newbinding; + break; + } + + binding = binding->mNext; + } + } + else { + mFirst = newbinding; + } + + return NS_OK; +} + +PRInt32 +nsXMLBindingSet::LookupTargetIndex(nsIAtom* aTargetVariable, + nsXMLBinding** aBinding) +{ + PRInt32 idx = 0; + nsXMLBinding* binding = mFirst; + + while (binding) { + if (binding->mVar == aTargetVariable) { + *aBinding = binding; + return idx; + } + idx++; + binding = binding->mNext; + } + + *aBinding = nsnull; + return -1; +} + +void +nsXMLBindingValues::GetAssignmentFor(nsXULTemplateResultXML* aResult, + nsXMLBinding* aBinding, + PRInt32 aIndex, + PRUint16 aType, + nsIDOMXPathResult** aValue) +{ + *aValue = mValues.SafeObjectAt(aIndex); + + if (!*aValue) { + nsCOMPtr contextNode; + aResult->GetNode(getter_AddRefs(contextNode)); + if (contextNode) { + nsCOMPtr resultsupports; + aBinding->mExpr->Evaluate(contextNode, aType, + nsnull, getter_AddRefs(resultsupports)); + + nsCOMPtr result = do_QueryInterface(resultsupports); + if (result && mValues.ReplaceObjectAt(result, aIndex)) + *aValue = result; + } + } + + NS_IF_ADDREF(*aValue); +} + +void +nsXMLBindingValues::GetNodeAssignmentFor(nsXULTemplateResultXML* aResult, + nsXMLBinding* aBinding, + PRInt32 aIndex, + nsIDOMNode** aNode) +{ + nsCOMPtr result; + GetAssignmentFor(aResult, aBinding, aIndex, + nsIDOMXPathResult::FIRST_ORDERED_NODE_TYPE, + getter_AddRefs(result)); + + if (result) + result->GetSingleNodeValue(aNode); + else + *aNode = nsnull; +} + +void +nsXMLBindingValues::GetStringAssignmentFor(nsXULTemplateResultXML* aResult, + nsXMLBinding* aBinding, + PRInt32 aIndex, + nsAString& aValue) +{ + nsCOMPtr result; + GetAssignmentFor(aResult, aBinding, aIndex, + nsIDOMXPathResult::STRING_TYPE, getter_AddRefs(result)); + + if (result) + result->GetStringValue(aValue); + else + aValue.Truncate(); +} diff --git a/content/xul/templates/src/nsXMLBinding.h b/content/xul/templates/src/nsXMLBinding.h new file mode 100644 index 000000000000..e0672b885f75 --- /dev/null +++ b/content/xul/templates/src/nsXMLBinding.h @@ -0,0 +1,170 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Neil Deakin + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsXMLBinding_h__ +#define nsXMLBinding_h__ + +#include "nsAutoPtr.h" +#include "nsIAtom.h" + +class nsXULTemplateResultXML; +class nsXMLBindingValues; + +/** + * Classes related to storing bindings for XML handling. + */ + +/** + * a description + */ +struct nsXMLBinding { + nsCOMPtr mVar; + nsCOMPtr mExpr; + + nsAutoPtr mNext; + + nsXMLBinding(nsIAtom* aVar, nsIDOMXPathExpression* aExpr) + : mVar(aVar), mExpr(aExpr), mNext(nsnull) + { + MOZ_COUNT_CTOR(nsXMLBinding); + } + + ~nsXMLBinding() + { + MOZ_COUNT_DTOR(nsXMLBinding); + } +}; + +/** + * a collection of descriptors. This object is refcounted by + * nsXMLBindingValues objects and the query processor. + */ +class nsXMLBindingSet +{ +public: + + // results hold a reference to a binding set in their + // nsXMLBindingValues fields + nsAutoRefCnt mRefCnt; + + // pointer to the first binding in a linked list + nsAutoPtr mFirst; + +public: + + nsrefcnt AddRef(); + nsrefcnt Release(); + NS_DECL_OWNINGTHREAD + + /** + * Add a binding to the set + */ + nsresult + AddBinding(nsIAtom* aVar, nsIDOMXPathExpression* aExpr); + + /** + * The nsXMLBindingValues class stores an array of values, one for each + * target symbol that could be set by the bindings in the set. + * LookupTargetIndex determines the index into the array for a given + * target symbol. + */ + PRInt32 + LookupTargetIndex(nsIAtom* aTargetVariable, nsXMLBinding** aBinding); +}; + +/** + * a set of values of bindings. This object is used once per result. + */ +class nsXMLBindingValues +{ +protected: + + // the binding set + nsRefPtr mBindings; + + /** + * A set of values for variable bindings. To look up a binding value, + * scan through the binding set in mBindings for the right target atom. + * Its index will correspond to the index in this array. + */ + nsCOMArray mValues; + +public: + + nsXMLBindingValues() { MOZ_COUNT_CTOR(nsXMLBindingValues); } + ~nsXMLBindingValues() { MOZ_COUNT_DTOR(nsXMLBindingValues); } + + nsXMLBindingSet* GetBindingSet() { return mBindings; } + + void SetBindingSet(nsXMLBindingSet* aBindings) { mBindings = aBindings; } + + PRInt32 + LookupTargetIndex(nsIAtom* aTargetVariable, nsXMLBinding** aBinding) + { + return mBindings ? + mBindings->LookupTargetIndex(aTargetVariable, aBinding) : -1; + } + + /** + * Retrieve the assignment for a particular variable + * + * aResult the result generated from the template + * aBinding the binding looked up using LookupTargetIndex + * aIndex the index of the assignment to retrieve + * aType the type of result expected + * aValue the value of the assignment + */ + void + GetAssignmentFor(nsXULTemplateResultXML* aResult, + nsXMLBinding* aBinding, + PRInt32 idx, + PRUint16 type, + nsIDOMXPathResult** aValue); + + void + GetNodeAssignmentFor(nsXULTemplateResultXML* aResult, + nsXMLBinding* aBinding, + PRInt32 idx, + nsIDOMNode** aValue); + + void + GetStringAssignmentFor(nsXULTemplateResultXML* aResult, + nsXMLBinding* aBinding, + PRInt32 idx, + nsAString& aValue); +}; + +#endif // nsXMLBinding_h__ diff --git a/content/xul/templates/src/nsXULContentBuilder.cpp b/content/xul/templates/src/nsXULContentBuilder.cpp index 06a09f2eefce..3bbc3ad97e5f 100644 --- a/content/xul/templates/src/nsXULContentBuilder.cpp +++ b/content/xul/templates/src/nsXULContentBuilder.cpp @@ -944,9 +944,12 @@ nsXULContentBuilder::AddPersistentAttributes(nsIContent* aTemplateNode, nsIXULTemplateResult* aResult, nsIContent* aRealNode) { + if (!mRoot) + return NS_OK; + nsCOMPtr resource; nsresult rv = GetResultResource(aResult, getter_AddRefs(resource)); - if (NS_FAILED(rv)) return rv; + NS_ENSURE_SUCCESS(rv, rv); nsAutoString attribute, persist; aTemplateNode->GetAttr(kNameSpaceID_None, nsGkAtoms::persist, persist); @@ -987,11 +990,11 @@ nsXULContentBuilder::AddPersistentAttributes(nsIContent* aTemplateNode, nsCOMPtr property; rv = nsXULContentUtils::GetResource(nameSpaceID, tag, getter_AddRefs(property)); - if (NS_FAILED(rv)) return rv; + NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr target; rv = mDB->GetTarget(resource, property, PR_TRUE, getter_AddRefs(target)); - if (NS_FAILED(rv)) return rv; + NS_ENSURE_SUCCESS(rv, rv); if (! target) continue; @@ -1003,11 +1006,11 @@ nsXULContentBuilder::AddPersistentAttributes(nsIContent* aTemplateNode, const PRUnichar* valueStr; rv = value->GetValueConst(&valueStr); - if (NS_FAILED(rv)) return rv; + NS_ENSURE_SUCCESS(rv, rv); rv = aRealNode->SetAttr(nameSpaceID, tag, nsDependentString(valueStr), PR_FALSE); - if (NS_FAILED(rv)) return rv; + NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; @@ -1137,7 +1140,7 @@ nsXULContentBuilder::CreateTemplateAndContainerContents(nsIContent* aElement, mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::ref, ref); if (! ref.IsEmpty()) { - nsresult rv = mQueryProcessor->TranslateRef(mDB, ref, + nsresult rv = mQueryProcessor->TranslateRef(mDataSource, ref, getter_AddRefs(mRootResult)); if (NS_FAILED(rv)) return rv; @@ -1278,7 +1281,7 @@ nsXULContentBuilder::CreateContainerContentsForQuerySet(nsIContent* aElement, return NS_OK; nsCOMPtr results; - nsresult rv = mQueryProcessor->GenerateResults(mDB, aResult, + nsresult rv = mQueryProcessor->GenerateResults(mDataSource, aResult, aQuerySet->mCompiledQuery, getter_AddRefs(results)); if (NS_FAILED(rv) || !results) diff --git a/content/xul/templates/src/nsXULTemplateBuilder.cpp b/content/xul/templates/src/nsXULTemplateBuilder.cpp index f4d044f1675d..9eeb74a86dc7 100644 --- a/content/xul/templates/src/nsXULTemplateBuilder.cpp +++ b/content/xul/templates/src/nsXULTemplateBuilder.cpp @@ -62,6 +62,8 @@ #include "nsIDOMElement.h" #include "nsIDOMNode.h" #include "nsIDOMDocument.h" +#include "nsIDOMXMLDocument.h" +#include "nsIPrivateDOMImplementation.h" #include "nsIDOMXULElement.h" #include "nsIDocument.h" #include "nsBindingManager.h" @@ -99,6 +101,7 @@ #include "nsNetUtil.h" #include "nsXULTemplateBuilder.h" #include "nsXULTemplateQueryProcessorRDF.h" +#include "nsXULTemplateQueryProcessorXML.h" //---------------------------------------------------------------------- @@ -128,10 +131,7 @@ PRLogModuleInfo* gXULTemplateLog; // nsXULTemplateBuilder::nsXULTemplateBuilder(void) - : mDB(nsnull), - mCompDB(nsnull), - mRoot(nsnull), - mQueriesCompiled(PR_FALSE), + : mQueriesCompiled(PR_FALSE), mFlags(0), mTop(nsnull) { @@ -246,10 +246,12 @@ TraverseMatchList(nsISupports* aKey, nsTemplateMatch* aMatch, void* aContext) NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULTemplateBuilder) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULTemplateBuilder) + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDataSource) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDB) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCompDB) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULTemplateBuilder) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDataSource) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDB) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCompDB) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mRoot) @@ -300,7 +302,7 @@ nsXULTemplateBuilder::GetRoot(nsIDOMElement** aResult) NS_IMETHODIMP nsXULTemplateBuilder::GetDatabase(nsIRDFCompositeDataSource** aResult) { - NS_IF_ADDREF(*aResult = mCompDB.get()); + NS_IF_ADDREF(*aResult = mCompDB); return NS_OK; } @@ -364,6 +366,9 @@ nsXULTemplateBuilder::Refresh() { nsresult rv; + if (!mCompDB) + return NS_ERROR_FAILURE; + nsCOMPtr dslist; rv = mCompDB->GetDataSources(getter_AddRefs(dslist)); NS_ENSURE_SUCCESS(rv, rv); @@ -396,36 +401,14 @@ nsXULTemplateBuilder::Init(nsIContent* aElement) if (! doc) return NS_ERROR_UNEXPECTED; - nsresult rv = LoadDataSources(doc); + PRBool shouldDelay; + nsresult rv = LoadDataSources(doc, &shouldDelay); if (NS_SUCCEEDED(rv)) { // Add ourselves as a document observer doc->AddObserver(this); } - // create the query processor. The querytype attribute on the root element - // may be used to create one of a specific type. - - // XXX should non-chrome be restricted to specific names? - - nsAutoString type; - mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::querytype, type); - - if (type.IsEmpty() || type.EqualsLiteral("rdf")) { - mQueryProcessor = new nsXULTemplateQueryProcessorRDF(); - if (! mQueryProcessor) - return NS_ERROR_OUT_OF_MEMORY; - } - else { - nsCAutoString cid(NS_QUERY_PROCESSOR_CONTRACTID_PREFIX); - AppendUTF16toUTF8(type, cid); - mQueryProcessor = do_CreateInstance(cid.get(), &rv); - if (!mQueryProcessor) { - // XXXndeakin should log an error here - return rv; - } - } - return rv; } @@ -1066,8 +1049,10 @@ nsXULTemplateBuilder::AttributeChanged(nsIDocument* aDocument, // Check for a change to the 'datasources' attribute. If so, setup // mDB by parsing the vew value and rebuild. else if (aAttribute == nsGkAtoms::datasources) { - LoadDataSources(aDocument); - Rebuild(); + PRBool shouldDelay; + LoadDataSources(aDocument, &shouldDelay); + if (!shouldDelay) + Rebuild(); } } } @@ -1118,6 +1103,7 @@ nsXULTemplateBuilder::NodeWillBeDestroyed(const nsINode* aNode) if (mQueryProcessor) mQueryProcessor->Done(); + mDataSource = nsnull; mDB = nsnull; mCompDB = nsnull; mRoot = nsnull; @@ -1134,57 +1120,144 @@ nsXULTemplateBuilder::NodeWillBeDestroyed(const nsINode* aNode) // nsresult -nsXULTemplateBuilder::LoadDataSources(nsIDocument* doc) +nsXULTemplateBuilder::LoadDataSources(nsIDocument* aDocument, + PRBool* aShouldDelayBuilding) { NS_PRECONDITION(mRoot != nsnull, "not initialized"); nsresult rv; + PRBool isRDFQuery = PR_FALSE; + + // we'll set these again later, after we create a new composite ds + mDB = nsnull; + mCompDB = nsnull; + mDataSource = nsnull; - if (mDB) { - // we'll set it again later, after we create a new composite ds - mDB = nsnull; + *aShouldDelayBuilding = PR_TRUE; + + nsAutoString datasources; + mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::datasources, datasources); + + nsAutoString querytype; + mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::querytype, querytype); + + // if the datasources begins with '#', it is a reference to a node + // within the same document. + PRBool shouldLoadUrls = PR_TRUE; + if (datasources.CharAt(0) == '#') { + shouldLoadUrls = PR_FALSE; + + if (querytype.IsEmpty()) { + querytype.AssignLiteral("xml"); + } + + nsCOMPtr domdoc = do_QueryInterface(aDocument); + + nsCOMPtr dsnode; + domdoc->GetElementById(Substring(datasources, 1), + getter_AddRefs(dsnode)); + if (dsnode) { + mDataSource = dsnode; + *aShouldDelayBuilding = PR_FALSE; + } + } + + // create the query processor. The querytype attribute on the root element + // may be used to create one of a specific type. + + // XXX should non-chrome be restricted to specific names? + if (querytype.IsEmpty()) + querytype.AssignLiteral("rdf"); + + if (querytype.EqualsLiteral("rdf")) { + isRDFQuery = PR_TRUE; + mQueryProcessor = new nsXULTemplateQueryProcessorRDF(); + NS_ENSURE_TRUE(mQueryProcessor, NS_ERROR_OUT_OF_MEMORY); + } + else if (querytype.EqualsLiteral("xml")) { + mQueryProcessor = new nsXULTemplateQueryProcessorXML(); + NS_ENSURE_TRUE(mQueryProcessor, NS_ERROR_OUT_OF_MEMORY); + } + else { + shouldLoadUrls = PR_FALSE; + + nsCAutoString cid(NS_QUERY_PROCESSOR_CONTRACTID_PREFIX); + AppendUTF16toUTF8(querytype, cid); + mQueryProcessor = do_CreateInstance(cid.get(), &rv); + // XXXndeakin log an error here - bug 321169 + NS_ENSURE_TRUE(mQueryProcessor, rv); } - // create a database for the builder - mCompDB = do_CreateInstance(NS_RDF_DATASOURCE_CONTRACTID_PREFIX "composite-datasource"); - - if (! mCompDB) { - NS_ERROR("unable to construct new composite data source"); - return NS_ERROR_UNEXPECTED; + if (shouldLoadUrls) { + rv = LoadDataSourceUrls(aDocument, datasources, + isRDFQuery, aShouldDelayBuilding); + NS_ENSURE_SUCCESS(rv, rv); } - // check for magical attributes. XXX move to ``flags''? - if (mRoot->AttrValueIs(kNameSpaceID_None, nsGkAtoms::coalesceduplicatearcs, - nsGkAtoms::_false, eCaseMatters)) - mCompDB->SetCoalesceDuplicateArcs(PR_FALSE); - - if (mRoot->AttrValueIs(kNameSpaceID_None, nsGkAtoms::allownegativeassertions, - nsGkAtoms::_false, eCaseMatters)) - mCompDB->SetAllowNegativeAssertions(PR_FALSE); + // Now set the database on the element, so that script writers can + // access it. + nsCOMPtr xuldoc = do_QueryInterface(aDocument); + if (xuldoc) + xuldoc->SetTemplateBuilderFor(mRoot, this); + if (!mRoot->IsNodeOfType(nsINode::eXUL)) { + // Hmm. This must be an HTML element. Try to set it as a + // JS property "by hand". + InitHTMLTemplateRoot(); + } + + return NS_OK; +} + +nsresult +nsXULTemplateBuilder::LoadDataSourceUrls(nsIDocument* aDocument, + const nsAString& aDataSources, + PRBool aIsRDFQuery, + PRBool* aShouldDelayBuilding) +{ // Grab the doc's principal... - nsIPrincipal *docPrincipal = doc->NodePrincipal(); + nsIPrincipal *docPrincipal = aDocument->NodePrincipal(); NS_ASSERTION(docPrincipal == mRoot->NodePrincipal(), "Principal mismatch? Which one to use?"); PRBool isTrusted = PR_FALSE; - rv = IsSystemPrincipal(docPrincipal, &isTrusted); - if (NS_FAILED(rv)) - return rv; + nsresult rv = IsSystemPrincipal(docPrincipal, &isTrusted); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr localstore; if (isTrusted) { - // If we're a privileged (e.g., chrome) document, then add the - // local store as the first data source in the db. Note that - // we _might_ not be able to get a local store if we haven't - // got a profile to read from yet. - nsCOMPtr localstore; rv = gRDFService->GetDataSource("rdf:local-store", getter_AddRefs(localstore)); - if (NS_SUCCEEDED(rv)) { + NS_ENSURE_SUCCESS(rv, rv); + } + + if (aIsRDFQuery) { + // create a database for the builder + mCompDB = do_CreateInstance(NS_RDF_DATASOURCE_CONTRACTID_PREFIX "composite-datasource"); + if (! mCompDB) { + NS_ERROR("unable to construct new composite data source"); + return NS_ERROR_UNEXPECTED; + } + + // check for magical attributes. XXX move to ``flags''? + if (mRoot->AttrValueIs(kNameSpaceID_None, + nsGkAtoms::coalesceduplicatearcs, + nsGkAtoms::_false, eCaseMatters)) + mCompDB->SetCoalesceDuplicateArcs(PR_FALSE); + + if (mRoot->AttrValueIs(kNameSpaceID_None, + nsGkAtoms::allownegativeassertions, + nsGkAtoms::_false, eCaseMatters)) + mCompDB->SetAllowNegativeAssertions(PR_FALSE); + + if (localstore) { + // If we're a privileged (e.g., chrome) document, then add the + // local store as the first data source in the db. Note that + // we _might_ not be able to get a local store if we haven't + // got a profile to read from yet. rv = mCompDB->AddDataSource(localstore); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to add local store to db"); - if (NS_FAILED(rv)) - return rv; + NS_ENSURE_SUCCESS(rv, rv); } } @@ -1193,14 +1266,11 @@ nsXULTemplateBuilder::LoadDataSources(nsIDocument* doc) // // rdf:bookmarks rdf:history http://foo.bar.com/blah.cgi?baz=9 // - nsIURI *docurl = doc->GetDocumentURI(); - - nsAutoString datasources; - mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::datasources, datasources); - + nsIURI *docurl = aDocument->GetDocumentURI(); + + nsAutoString datasources(aDataSources); PRUint32 first = 0; - - while(1) { + while (1) { while (first < datasources.Length() && nsCRT::IsAsciiSpace(datasources.CharAt(first))) ++first; @@ -1223,6 +1293,7 @@ nsXULTemplateBuilder::LoadDataSources(nsIDocument* doc) // protocol) leaves uriStr unaltered. NS_MakeAbsoluteURI(uriStr, uriStr, docurl); + nsCOMPtr principal; if (!isTrusted) { // Our document is untrusted, so check to see if we can // load the datasource that they've asked for. @@ -1231,17 +1302,14 @@ nsXULTemplateBuilder::LoadDataSources(nsIDocument* doc) if (NS_FAILED(rv) || !uri) continue; // Necko will barf if our URI is weird - nsCOMPtr principal; rv = gScriptSecurityManager->GetCodebasePrincipal(uri, getter_AddRefs(principal)); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get codebase principal"); - if (NS_FAILED(rv)) - return rv; + NS_ENSURE_SUCCESS(rv, rv); PRBool same; rv = docPrincipal->Equals(principal, &same); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to test same origin"); - if (NS_FAILED(rv)) - return rv; + NS_ENSURE_SUCCESS(rv, rv); if (! same) continue; @@ -1251,64 +1319,90 @@ nsXULTemplateBuilder::LoadDataSources(nsIDocument* doc) // document. Let it load! } - nsCOMPtr ds; - nsCAutoString uristrC; - uristrC.AssignWithConversion(uriStr); + if (aIsRDFQuery) { + nsCOMPtr ds; + nsCAutoString uristrC; + uristrC.AssignWithConversion(uriStr); - rv = gRDFService->GetDataSource(uristrC.get(), getter_AddRefs(ds)); + rv = gRDFService->GetDataSource(uristrC.get(), getter_AddRefs(ds)); - if (NS_FAILED(rv)) { - // This is only a warning because the data source may not - // be accessible for any number of reasons, including - // security, a bad URL, etc. -#ifdef DEBUG - nsCAutoString msg; - msg.Append("unable to load datasource '"); - msg.AppendWithConversion(uriStr); - msg.Append('\''); - NS_WARNING(msg.get()); -#endif - continue; + if (NS_FAILED(rv)) { + // This is only a warning because the data source may not + // be accessible for any number of reasons, including + // security, a bad URL, etc. + #ifdef DEBUG + nsCAutoString msg; + msg.Append("unable to load datasource '"); + msg.AppendWithConversion(uriStr); + msg.Append('\''); + NS_WARNING(msg.get()); + #endif + continue; + } + + mCompDB->AddDataSource(ds); } + else { + nsAutoString emptyStr; + nsCOMPtr domDocument; + rv = nsContentUtils::CreateDocument(emptyStr, emptyStr, nsnull, + docurl, aDocument->GetBaseURI(), + docPrincipal, + getter_AddRefs(domDocument)); + NS_ENSURE_SUCCESS(rv, rv); - mCompDB->AddDataSource(ds); - } + nsCOMPtr target = do_QueryInterface(domDocument); + target->AddEventListener(NS_LITERAL_STRING("load"), this, PR_FALSE); + + nsCOMPtr xmldoc = do_QueryInterface(domDocument); - // check if we were given an inference engine type - nsAutoString infer; - mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::infer, infer); - if (!infer.IsEmpty()) { - nsCString inferContractID(NS_RDF_INFER_DATASOURCE_CONTRACTID_PREFIX); - AppendUTF16toUTF8(infer, inferContractID); - nsCOMPtr inferDB = do_CreateInstance(inferContractID.get()); - - if (inferDB) { - inferDB->SetBaseDataSource(mCompDB); - mDB = do_QueryInterface(inferDB); - } else { - NS_WARNING("failed to construct inference engine specified on template"); + PRBool ok; + xmldoc->Load(uriStr, &ok); + if (ok) { + mDataSource = domDocument; + *aShouldDelayBuilding = PR_TRUE; + } + + // only one XML datasource is supported currently + break; } } + + if (aIsRDFQuery) { + // check if we were given an inference engine type + nsAutoString infer; + mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::infer, infer); + if (!infer.IsEmpty()) { + nsCString inferCID(NS_RDF_INFER_DATASOURCE_CONTRACTID_PREFIX); + AppendUTF16toUTF8(infer, inferCID); + nsCOMPtr inferDB = + do_CreateInstance(inferCID.get()); - if (!mDB) - mDB = mCompDB; - - nsCOMPtr xuldoc = do_QueryInterface(doc); - if (xuldoc) - xuldoc->SetTemplateBuilderFor(mRoot, this); - - // Now set the database on the element, so that script writers can - // access it. - nsXULElement *xulcontent = nsXULElement::FromContent(mRoot); - if (! xulcontent) { - // Hmm. This must be an HTML element. Try to set it as a - // JS property "by hand". - InitHTMLTemplateRoot(); + if (inferDB) { + inferDB->SetBaseDataSource(mCompDB); + mDB = do_QueryInterface(inferDB); + } else { + NS_WARNING("failed to construct inference engine specified on template"); + } + } + + if (!mDB) + mDB = mCompDB; + mDataSource = mDB; + } + else { + mDB = localstore; } return NS_OK; } +NS_IMETHODIMP +nsXULTemplateBuilder::HandleEvent(nsIDOMEvent* aEvent) +{ + return Rebuild(); +} + nsresult nsXULTemplateBuilder::InitHTMLTemplateRoot() { @@ -1348,7 +1442,7 @@ nsXULTemplateBuilder::InitHTMLTemplateRoot() rv = wrapper->GetJSObject(&jselement); NS_ENSURE_SUCCESS(rv, rv); - { + if (mDB) { // database rv = xpc->WrapNative(jscontext, scope, mDB, NS_GET_IID(nsIRDFCompositeDataSource), @@ -1687,7 +1781,8 @@ nsXULTemplateBuilder::CompileQueries() mFlags |= eDontRecurse; nsCOMPtr rootnode = do_QueryInterface(mRoot); - nsresult rv = mQueryProcessor->InitializeForBuilding(mDB, this, rootnode); + nsresult rv = + mQueryProcessor->InitializeForBuilding(mDataSource, this, rootnode); if (NS_FAILED(rv)) return rv; @@ -1989,14 +2084,15 @@ nsXULTemplateBuilder::CompileExtendedQuery(nsIContent* aRuleElement, nsGkAtoms::conditions, getter_AddRefs(conditions)); - if (conditions) { - rv = CompileConditions(rule, conditions); - - // If the rule compilation failed, then we have to bail. - if (NS_FAILED(rv)) { - delete rule; - return rv; - } + // allow the conditions to be placed directly inside the rule + if (!conditions) + conditions = aRuleElement; + + rv = CompileConditions(rule, conditions); + // If the rule compilation failed, then we have to bail. + if (NS_FAILED(rv)) { + delete rule; + return rv; } rv = aQuerySet->AddRule(rule); @@ -2014,11 +2110,12 @@ nsXULTemplateBuilder::CompileExtendedQuery(nsIContent* aRuleElement, nsGkAtoms::bindings, getter_AddRefs(bindings)); - if (bindings) { - rv = CompileBindings(rule, bindings); - if (NS_FAILED(rv)) - return rv; - } + // allow bindings to be placed directly inside rule + if (!bindings) + bindings = aRuleElement; + + rv = CompileBindings(rule, bindings); + NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } diff --git a/content/xul/templates/src/nsXULTemplateBuilder.h b/content/xul/templates/src/nsXULTemplateBuilder.h index 9fbdb9cd7a4a..fcd63353daf0 100644 --- a/content/xul/templates/src/nsXULTemplateBuilder.h +++ b/content/xul/templates/src/nsXULTemplateBuilder.h @@ -53,6 +53,7 @@ #include "nsIRDFObserver.h" #include "nsIRDFService.h" #include "nsIXULTemplateBuilder.h" +#include "nsIDOMEventListener.h" #include "nsFixedSizeAllocator.h" #include "nsVoidArray.h" @@ -77,6 +78,7 @@ class nsIRDFCompositeDataSource; * set of rules. */ class nsXULTemplateBuilder : public nsIXULTemplateBuilder, + public nsIDOMEventListener, public nsStubDocumentObserver { public: @@ -98,7 +100,9 @@ public: // nsIXULTemplateBuilder interface NS_DECL_NSIXULTEMPLATEBUILDER - + + NS_DECL_NSIDOMEVENTLISTENER + // nsIDocumentObserver virtual void AttributeChanged(nsIDocument *aDocument, nsIContent* aContent, PRInt32 aNameSpaceID, nsIAtom* aAttribute, @@ -274,8 +278,30 @@ public: const nsAString& aVariable, void* aClosure); + /** + * Load the datasources for the template. shouldDelayBuilding is an out + * parameter which will be set to true to indicate that content building + * should not be performed yet as the datasource has not yet loaded. If + * false, the datasource has already loaded so building can proceed + * immediately. In the former case, the datasource or query processor + * should either rebuild the template or update results when the + * datasource is loaded as needed. + */ nsresult - LoadDataSources(nsIDocument* aDoc); + LoadDataSources(nsIDocument* aDoc, PRBool* shouldDelayBuilding); + + /** + * Called by LoadDataSources to load a datasource given a uri list + * in aDataSource. The list is a set of uris separated by spaces. + * If aIsRDFQuery is true, then this is for an RDF datasource which + * causes the method to check for additional flags specific to the + * RDF processor. + */ + nsresult + LoadDataSourceUrls(nsIDocument* aDocument, + const nsAString& aDataSources, + PRBool aIsRDFQuery, + PRBool* aShouldDelayBuilding); nsresult InitHTMLTemplateRoot(); @@ -330,6 +356,7 @@ public: nsIRDFResource** aResource); protected: + nsCOMPtr mDataSource; nsCOMPtr mDB; nsCOMPtr mCompDB; diff --git a/content/xul/templates/src/nsXULTemplateQueryProcessorXML.cpp b/content/xul/templates/src/nsXULTemplateQueryProcessorXML.cpp new file mode 100644 index 000000000000..95802a2b439f --- /dev/null +++ b/content/xul/templates/src/nsXULTemplateQueryProcessorXML.cpp @@ -0,0 +1,345 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Neil Deakin + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsCOMPtr.h" +#include "nsAutoPtr.h" +#include "nsIDOMDocument.h" +#include "nsIDOMNode.h" +#include "nsIDOMNodeList.h" +#include "nsIDOMElement.h" +#include "nsIDOMXPathNSResolver.h" +#include "nsINameSpaceManager.h" +#include "nsGkAtoms.h" +#include "nsIServiceManager.h" +#include "nsUnicharUtils.h" + +#include "nsXULTemplateBuilder.h" +#include "nsXULTemplateQueryProcessorXML.h" +#include "nsXULTemplateResultXML.h" + +NS_IMPL_ISUPPORTS1(nsXMLQuery, nsXMLQuery) + +//---------------------------------------------------------------------- +// +// nsXULTemplateResultSetXML +// + +NS_IMPL_ISUPPORTS1(nsXULTemplateResultSetXML, nsISimpleEnumerator) + +NS_IMETHODIMP +nsXULTemplateResultSetXML::HasMoreElements(PRBool *aResult) +{ + // if GetSnapshotLength failed, then the return type was not a set of + // nodes, so just return false in this case. + PRUint32 length; + if (NS_SUCCEEDED(mResults->GetSnapshotLength(&length))) + *aResult = (mPosition < length); + else + *aResult = PR_FALSE; + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultSetXML::GetNext(nsISupports **aResult) +{ + nsCOMPtr node; + nsresult rv = mResults->SnapshotItem(mPosition, getter_AddRefs(node)); + NS_ENSURE_SUCCESS(rv, rv); + + nsXULTemplateResultXML* result = + new nsXULTemplateResultXML(mQuery, node, mBindingSet); + NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY); + + ++mPosition; + *aResult = result; + NS_ADDREF(result); + return NS_OK; +} + + +//---------------------------------------------------------------------- +// +// nsXULTemplateQueryProcessorXML +// + +NS_IMPL_ISUPPORTS1(nsXULTemplateQueryProcessorXML, nsIXULTemplateQueryProcessor) + +NS_IMETHODIMP +nsXULTemplateQueryProcessorXML::InitializeForBuilding(nsISupports* aDatasource, + nsIXULTemplateBuilder* aBuilder, + nsIDOMNode* aRootNode) +{ + // the datasource is either a document or a DOM element + nsCOMPtr doc = do_QueryInterface(aDatasource); + if (doc) + doc->GetDocumentElement(getter_AddRefs(mRoot)); + else + mRoot = do_QueryInterface(aDatasource); + NS_ENSURE_STATE(mRoot); + + mEvaluator = do_CreateInstance("@mozilla.org/dom/xpath-evaluator;1"); + NS_ENSURE_TRUE(mEvaluator, NS_ERROR_OUT_OF_MEMORY); + + if (!mRuleToBindingsMap.IsInitialized() && + !mRuleToBindingsMap.Init()) + return NS_ERROR_OUT_OF_MEMORY; + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorXML::Done() +{ + mGenerationStarted = PR_FALSE; + + if (mRuleToBindingsMap.IsInitialized()) + mRuleToBindingsMap.Clear(); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorXML::CompileQuery(nsIXULTemplateBuilder* aBuilder, + nsIDOMNode* aQueryNode, + nsIAtom* aRefVariable, + nsIAtom* aMemberVariable, + nsISupports** _retval) +{ + nsresult rv = NS_OK; + + *_retval = nsnull; + + nsCOMPtr 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.AssignLiteral("*"); + + nsCOMPtr compiledexpr; + rv = CreateExpression(expr, aQueryNode, getter_AddRefs(compiledexpr)); + NS_ENSURE_SUCCESS(rv, rv); + + nsRefPtr query = + new nsXMLQuery(this, aMemberVariable, compiledexpr); + NS_ENSURE_TRUE(query, NS_ERROR_OUT_OF_MEMORY); + + PRUint32 count = content->GetChildCount(); + for (PRUint32 i = 0; i < count; ++i) { + nsIContent *condition = content->GetChildAt(i); + 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()) { + nsCOMPtr conditionNode = + do_QueryInterface(condition); + rv = CreateExpression(expr, conditionNode, + getter_AddRefs(compiledexpr)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr varatom = do_GetAtom(var); + + rv = query->AddBinding(varatom, compiledexpr); + NS_ENSURE_SUCCESS(rv, rv); + } + } + } + + *_retval = query; + NS_ADDREF(*_retval); + + return rv; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorXML::GenerateResults(nsISupports* aDatasource, + nsIXULTemplateResult* aRef, + nsISupports* aQuery, + nsISimpleEnumerator** aResults) +{ + if (!aQuery) + return NS_ERROR_INVALID_ARG; + + mGenerationStarted = PR_TRUE; + + nsCOMPtr xmlquery = do_QueryInterface(aQuery); + if (!xmlquery) + return NS_ERROR_INVALID_ARG; + + nsCOMPtr context; + aRef->GetBindingObjectFor(xmlquery->GetMemberVariable(), + getter_AddRefs(context)); + if (!context) + context = mRoot; + + nsIDOMXPathExpression* expr = xmlquery->GetResultsExpression(); + if (!expr) + return NS_ERROR_FAILURE; + + nsCOMPtr exprsupportsresults; + nsresult rv = expr->Evaluate(context, + nsIDOMXPathResult::ORDERED_NODE_SNAPSHOT_TYPE, + nsnull, getter_AddRefs(exprsupportsresults)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr exprresults = + do_QueryInterface(exprsupportsresults); + + nsXULTemplateResultSetXML* results = + new nsXULTemplateResultSetXML(xmlquery, exprresults, + xmlquery->GetBindingSet()); + NS_ENSURE_TRUE(results, NS_ERROR_OUT_OF_MEMORY); + + *aResults = results; + NS_ADDREF(*aResults); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorXML::AddBinding(nsIDOMNode* aRuleNode, + nsIAtom* aVar, + nsIAtom* aRef, + const nsAString& aExpr) +{ + if (mGenerationStarted) + return NS_ERROR_FAILURE; + + nsRefPtr bindings = mRuleToBindingsMap.GetWeak(aRuleNode); + if (!bindings) { + bindings = new nsXMLBindingSet(); + if (!bindings || !mRuleToBindingsMap.Put(aRuleNode, bindings)) + return NS_ERROR_OUT_OF_MEMORY; + } + + nsCOMPtr compiledexpr; + nsresult rv = + CreateExpression(aExpr, aRuleNode, getter_AddRefs(compiledexpr)); + NS_ENSURE_SUCCESS(rv, rv); + + // aRef isn't currently used for XML query processors + return bindings->AddBinding(aVar, compiledexpr); +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorXML::TranslateRef(nsISupports* aDatasource, + const nsAString& aRefString, + nsIXULTemplateResult** aRef) +{ + *aRef = nsnull; + + // the datasource is either a document or a DOM element + nsCOMPtr rootElement; + nsCOMPtr doc = do_QueryInterface(aDatasource); + if (doc) + doc->GetDocumentElement(getter_AddRefs(rootElement)); + else + rootElement = do_QueryInterface(aDatasource); + + // if no root element, just return. The document may not have loaded yet + if (!rootElement) + return NS_OK; + + nsXULTemplateResultXML* result = + new nsXULTemplateResultXML(nsnull, rootElement, nsnull); + NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY); + + *aRef = result; + NS_ADDREF(*aRef); + + return NS_OK; +} + + +NS_IMETHODIMP +nsXULTemplateQueryProcessorXML::CompareResults(nsIXULTemplateResult* aLeft, + nsIXULTemplateResult* aRight, + nsIAtom* aVar, + PRInt32* aResult) +{ + *aResult = 0; + + // XXXndeakin - bug 379745 + // it would be good for this to handle other types such as integers, + // so that sorting can be optimized for different types. + + nsAutoString leftVal; + aLeft->GetBindingFor(aVar, leftVal); + + nsAutoString rightVal; + aRight->GetBindingFor(aVar, rightVal); + + // currently templates always sort case-insensitive + *aResult = ::Compare(leftVal, rightVal, + nsCaseInsensitiveStringComparator()); + return NS_OK; +} + +nsXMLBindingSet* +nsXULTemplateQueryProcessorXML::GetOptionalBindingsForRule(nsIDOMNode* aRuleNode) +{ + return mRuleToBindingsMap.GetWeak(aRuleNode); +} + +nsresult +nsXULTemplateQueryProcessorXML::CreateExpression(const nsAString& aExpr, + nsIDOMNode* aNode, + nsIDOMXPathExpression** aCompiledExpr) +{ + nsCOMPtr nsResolver; + + nsCOMPtr doc; + aNode->GetOwnerDocument(getter_AddRefs(doc)); + + nsCOMPtr eval = do_QueryInterface(doc); + if (eval) { + nsresult rv = + eval->CreateNSResolver(aNode, getter_AddRefs(nsResolver)); + NS_ENSURE_SUCCESS(rv, rv); + } + + return mEvaluator->CreateExpression(aExpr, nsResolver, aCompiledExpr); +} diff --git a/content/xul/templates/src/nsXULTemplateQueryProcessorXML.h b/content/xul/templates/src/nsXULTemplateQueryProcessorXML.h new file mode 100644 index 000000000000..912045e7b230 --- /dev/null +++ b/content/xul/templates/src/nsXULTemplateQueryProcessorXML.h @@ -0,0 +1,179 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Neil Deakin + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsXULTemplateQueryProcessorXML_h__ +#define nsXULTemplateQueryProcessorXML_h__ + +#include "nsIXULTemplateQueryProcessor.h" + +#include "nsISimpleEnumerator.h" +#include "nsString.h" +#include "nsCOMArray.h" +#include "nsRefPtrHashtable.h" +#include "nsIDOMElement.h" +#include "nsIDOMXPathExpression.h" +#include "nsIDOMXPathEvaluator.h" +#include "nsIDOMXPathResult.h" +#include "nsXMLBinding.h" + +class nsXULTemplateQueryProcessorXML; + +#define NS_IXMLQUERY_IID \ + {0x0358d692, 0xccce, 0x4a97, \ + { 0xb2, 0x51, 0xba, 0x8f, 0x17, 0x0f, 0x3b, 0x6f }} + +class nsXMLQuery : public nsISupports +{ + public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_IXMLQUERY_IID) + + NS_DECL_ISUPPORTS + + // return a weak reference to the processor the query was created from + nsXULTemplateQueryProcessorXML* Processor() { return mProcessor; } + + // return a weak reference t the member variable for the query + nsIAtom* GetMemberVariable() { return mMemberVariable; } + + // return a weak reference to the expression used to generate results + nsIDOMXPathExpression* GetResultsExpression() { return mResultsExpr; } + + // return a weak reference to the additional required bindings + nsXMLBindingSet* GetBindingSet() { return mRequiredBindings; } + + // add a required binding for the query + nsresult + AddBinding(nsIAtom* aVar, nsIDOMXPathExpression* aExpr) + { + if (!mRequiredBindings) { + mRequiredBindings = new nsXMLBindingSet(); + NS_ENSURE_TRUE(mRequiredBindings, NS_ERROR_OUT_OF_MEMORY); + } + + return mRequiredBindings->AddBinding(aVar, aExpr); + } + + nsXMLQuery(nsXULTemplateQueryProcessorXML* aProcessor, + nsIAtom* aMemberVariable, + nsIDOMXPathExpression* aResultsExpr) + : mProcessor(aProcessor), + mMemberVariable(aMemberVariable), + mResultsExpr(aResultsExpr) + { } + + protected: + nsXULTemplateQueryProcessorXML* mProcessor; + + nsCOMPtr mMemberVariable; + + nsCOMPtr mResultsExpr; + + nsRefPtr mRequiredBindings; +}; + +NS_DEFINE_STATIC_IID_ACCESSOR(nsXMLQuery, NS_IXMLQUERY_IID) + +class nsXULTemplateResultSetXML : public nsISimpleEnumerator +{ +private: + + // reference back to the query + nsCOMPtr mQuery; + + // the binding set created from nodes + nsRefPtr mBindingSet; + + // set of results contained in this enumerator + nsCOMPtr mResults; + + // current position within the list of results + PRUint32 mPosition; + +public: + + // nsISupports interface + NS_DECL_ISUPPORTS + + // nsISimpleEnumerator interface + NS_DECL_NSISIMPLEENUMERATOR + + nsXULTemplateResultSetXML(nsXMLQuery* aQuery, + nsIDOMXPathResult* aResults, + nsXMLBindingSet* aBindingSet) + : mQuery(aQuery), + mBindingSet(aBindingSet), + mResults(aResults), + mPosition(0) + {} +}; + +class nsXULTemplateQueryProcessorXML : public nsIXULTemplateQueryProcessor +{ +public: + + nsXULTemplateQueryProcessorXML() + : mGenerationStarted(PR_FALSE) + {} + + // nsISupports interface + NS_DECL_ISUPPORTS + + // nsIXULTemplateQueryProcessor interface + NS_DECL_NSIXULTEMPLATEQUERYPROCESSOR + + nsXMLBindingSet* + GetOptionalBindingsForRule(nsIDOMNode* aRuleNode); + + // create an XPath expression from aExpr, using aNode for + // resolving namespaces + nsresult + CreateExpression(const nsAString& aExpr, + nsIDOMNode* aNode, + nsIDOMXPathExpression** aCompiledExpr); + +private: + + PRBool mGenerationStarted; + + nsRefPtrHashtable mRuleToBindingsMap; + + nsCOMPtr mRoot; + + nsCOMPtr mEvaluator; +}; + + +#endif // nsXULTemplateQueryProcessorXML_h__ diff --git a/content/xul/templates/src/nsXULTemplateResultXML.cpp b/content/xul/templates/src/nsXULTemplateResultXML.cpp new file mode 100644 index 000000000000..f3339823006d --- /dev/null +++ b/content/xul/templates/src/nsXULTemplateResultXML.cpp @@ -0,0 +1,210 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Neil Deakin + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsIServiceManager.h" +#include "nsIDOMNode.h" +#include "nsIDOMElement.h" +#include "nsIContent.h" + +#include "nsIRDFService.h" + +#include "nsXULTemplateResultXML.h" +#include "nsXMLBinding.h" + +static PRUint32 sTemplateId = 0; + +NS_IMPL_ISUPPORTS1(nsXULTemplateResultXML, nsIXULTemplateResult) + +nsXULTemplateResultXML::nsXULTemplateResultXML(nsXMLQuery* aQuery, + nsIDOMNode* aNode, + nsXMLBindingSet* aBindings) + : mId(++sTemplateId), mQuery(aQuery), mNode(aNode) +{ + if (aBindings) + mRequiredValues.SetBindingSet(aBindings); +} + +NS_IMETHODIMP +nsXULTemplateResultXML::GetIsContainer(PRBool* aIsContainer) +{ + // a node is considered a container if it has children + if (mNode) + mNode->HasChildNodes(aIsContainer); + else + *aIsContainer = PR_FALSE; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultXML::GetIsEmpty(PRBool* aIsEmpty) +{ + // a node is considered empty if it has no elements as children + nsCOMPtr content = do_QueryInterface(mNode); + if (content) { + PRUint32 count = content->GetChildCount(); + for (PRUint32 c = 0; c < count; c++) { + if (content->GetChildAt(c)->IsNodeOfType(nsIContent::eELEMENT)) { + *aIsEmpty = PR_FALSE; + return NS_OK; + } + } + } + + *aIsEmpty = PR_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultXML::GetMayProcessChildren(PRBool* aMayProcessChildren) +{ + *aMayProcessChildren = PR_TRUE; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultXML::GetId(nsAString& aId) +{ + nsAutoString rowid(NS_LITERAL_STRING("row")); + rowid.AppendInt(mId); + aId.Assign(rowid); + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultXML::GetResource(nsIRDFResource** aResource) +{ + *aResource = nsnull; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultXML::GetType(nsAString& aType) +{ + aType.Truncate(); + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultXML::GetBindingFor(nsIAtom* aVar, nsAString& aValue) +{ + // get the position of the atom in the variables table + nsXMLBinding* binding; + + PRInt32 idx = mRequiredValues.LookupTargetIndex(aVar, &binding); + if (idx >= 0) { + mRequiredValues.GetStringAssignmentFor(this, binding, idx, aValue); + return NS_OK; + } + + idx = mOptionalValues.LookupTargetIndex(aVar, &binding); + if (idx >= 0) { + mOptionalValues.GetStringAssignmentFor(this, binding, idx, aValue); + return NS_OK; + } + + // if the variable is not bound, just use the variable name as the name of + // an attribute to retrieve + nsAutoString attr; + aVar->ToString(attr); + + if (attr.Length() > 1) { + nsCOMPtr element = do_QueryInterface(mNode); + if (element) + return element->GetAttribute(Substring(attr, 1), aValue); + } + + aValue.Truncate(); + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultXML::GetBindingObjectFor(nsIAtom* aVar, nsISupports** aValue) +{ + nsXMLBinding* binding; + nsCOMPtr node; + + if (mQuery && aVar == mQuery->GetMemberVariable()) { + node = mNode; + } + else { + PRInt32 idx = mRequiredValues.LookupTargetIndex(aVar, &binding); + if (idx > 0) { + mRequiredValues.GetNodeAssignmentFor(this, binding, idx, + getter_AddRefs(node)); + } + else { + idx = mOptionalValues.LookupTargetIndex(aVar, &binding); + if (idx > 0) { + mOptionalValues.GetNodeAssignmentFor(this, binding, idx, + getter_AddRefs(node)); + } + } + } + + *aValue = node; + NS_IF_ADDREF(*aValue); + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultXML::RuleMatched(nsISupports* aQueryNode, + nsIDOMNode* aRuleNode) +{ + // when a rule matches, set the bindings that must be used. + nsXULTemplateQueryProcessorXML* processor = mQuery ? mQuery->Processor() : + nsnull; + if (processor) { + nsXMLBindingSet* bindings = + processor->GetOptionalBindingsForRule(aRuleNode); + if (bindings) + mOptionalValues.SetBindingSet(bindings); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultXML::HasBeenRemoved() +{ + return NS_OK; +} + +void +nsXULTemplateResultXML::GetNode(nsIDOMNode** aNode) +{ + *aNode = mNode; + NS_IF_ADDREF(*aNode); +} diff --git a/content/xul/templates/src/nsXULTemplateResultXML.h b/content/xul/templates/src/nsXULTemplateResultXML.h new file mode 100644 index 000000000000..9792ade67b22 --- /dev/null +++ b/content/xul/templates/src/nsXULTemplateResultXML.h @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Neil Deakin + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsXULTemplateResultXML_h__ +#define nsXULTemplateResultXML_h__ + +#include "nsCOMPtr.h" +#include "nsIRDFResource.h" +#include "nsXULTemplateQueryProcessorXML.h" +#include "nsIXULTemplateResult.h" + +/** + * An single result of an query + */ +class nsXULTemplateResultXML : public nsIXULTemplateResult +{ +public: + NS_DECL_ISUPPORTS + + NS_DECL_NSIXULTEMPLATERESULT + + nsXULTemplateResultXML(nsXMLQuery* aQuery, + nsIDOMNode* aNode, + nsXMLBindingSet* aBindings); + + ~nsXULTemplateResultXML() {} + + void GetNode(nsIDOMNode** aNode); + +protected: + + // result id + PRUint32 mId; + + // query that generated the result + nsCOMPtr mQuery; + + // context node in datasource + nsCOMPtr mNode; + + // assignments in query + nsXMLBindingValues mRequiredValues; + + // extra assignments made by rules ( tags) + nsXMLBindingValues mOptionalValues; +}; + +#endif // nsXULTemplateResultXML_h__ diff --git a/content/xul/templates/src/nsXULTreeBuilder.cpp b/content/xul/templates/src/nsXULTreeBuilder.cpp index eb3bd0adb67a..f84f8964d78c 100644 --- a/content/xul/templates/src/nsXULTreeBuilder.cpp +++ b/content/xul/templates/src/nsXULTreeBuilder.cpp @@ -1374,7 +1374,8 @@ nsXULTreeBuilder::RebuildAll() mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::ref, ref); if (! ref.IsEmpty()) { - rv = mQueryProcessor->TranslateRef(mDB, ref, getter_AddRefs(mRootResult)); + rv = mQueryProcessor->TranslateRef(mDataSource, ref, + getter_AddRefs(mRootResult)); if (NS_FAILED(rv)) return rv; @@ -1577,7 +1578,7 @@ nsXULTreeBuilder::OpenSubtreeForQuerySet(nsTreeRows::Subtree* aSubtree, PRInt32 count = *aDelta; nsCOMPtr results; - nsresult rv = mQueryProcessor->GenerateResults(mDB, aResult, + nsresult rv = mQueryProcessor->GenerateResults(mDataSource, aResult, aQuerySet->mCompiledQuery, getter_AddRefs(results)); if (NS_FAILED(rv)) @@ -1812,7 +1813,9 @@ nsXULTreeBuilder::Compare(const void* aLeft, const void* aRight, void* aClosure) PRInt32 nsXULTreeBuilder::CompareResults(nsIXULTemplateResult* aLeft, nsIXULTemplateResult* aRight) { - if (mSortDirection == eDirection_Natural) { + // this is an extra check done for RDF queries such that results appear in + // the order they appear in their containing Seq + if (mSortDirection == eDirection_Natural && mDB) { // If the sort order is ``natural'', then see if the container // is an RDF sequence. If so, we'll try to use the ordinal // properties to determine order.