summaryrefslogtreecommitdiffstats
path: root/dom/xul/templates/nsXULTemplateQueryProcessorRDF.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /dom/xul/templates/nsXULTemplateQueryProcessorRDF.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/xul/templates/nsXULTemplateQueryProcessorRDF.cpp')
-rw-r--r--dom/xul/templates/nsXULTemplateQueryProcessorRDF.cpp1825
1 files changed, 1825 insertions, 0 deletions
diff --git a/dom/xul/templates/nsXULTemplateQueryProcessorRDF.cpp b/dom/xul/templates/nsXULTemplateQueryProcessorRDF.cpp
new file mode 100644
index 000000000..732e545d0
--- /dev/null
+++ b/dom/xul/templates/nsXULTemplateQueryProcessorRDF.cpp
@@ -0,0 +1,1825 @@
+/* -*- 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 "nsICollation.h"
+#include "nsIDOMNode.h"
+#include "nsIRDFNode.h"
+#include "nsIRDFObserver.h"
+#include "nsIRDFRemoteDataSource.h"
+#include "nsIRDFInferDataSource.h"
+#include "nsIRDFService.h"
+#include "nsRDFCID.h"
+#include "nsIServiceManager.h"
+#include "nsNameSpaceManager.h"
+#include "nsGkAtoms.h"
+#include "nsIDOMDocument.h"
+#include "nsAttrName.h"
+#include "rdf.h"
+#include "nsArrayUtils.h"
+#include "nsIURI.h"
+
+#include "nsContentTestNode.h"
+#include "nsRDFConInstanceTestNode.h"
+#include "nsRDFConMemberTestNode.h"
+#include "nsRDFPropertyTestNode.h"
+#include "nsInstantiationNode.h"
+#include "nsRDFTestNode.h"
+#include "nsXULContentUtils.h"
+#include "nsXULTemplateBuilder.h"
+#include "nsXULTemplateResultRDF.h"
+#include "nsXULTemplateResultSetRDF.h"
+#include "nsXULTemplateQueryProcessorRDF.h"
+#include "nsXULSortService.h"
+#include "nsIDocument.h"
+
+//----------------------------------------------------------------------
+
+#define PARSE_TYPE_INTEGER "Integer"
+
+nsrefcnt nsXULTemplateQueryProcessorRDF::gRefCnt = 0;
+nsIRDFService* nsXULTemplateQueryProcessorRDF::gRDFService;
+nsIRDFContainerUtils* nsXULTemplateQueryProcessorRDF::gRDFContainerUtils;
+nsIRDFResource* nsXULTemplateQueryProcessorRDF::kNC_BookmarkSeparator;
+nsIRDFResource* nsXULTemplateQueryProcessorRDF::kRDF_type;
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULTemplateQueryProcessorRDF)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULTemplateQueryProcessorRDF)
+ tmp->Done();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULTemplateQueryProcessorRDF)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDB)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLastRef)
+
+ for (auto it = tmp->mBindingDependencies.Iter(); !it.Done(); it.Next()) {
+ nsXULTemplateQueryProcessorRDF::ResultArray* array = it.UserData();
+ int32_t count = array->Length();
+ for (int32_t i = 0; i < count; ++i) {
+ cb.NoteXPCOMChild(array->ElementAt(i));
+ }
+ }
+
+ for (auto it = tmp->mMemoryElementToResultMap.Iter();
+ !it.Done();
+ it.Next()) {
+ nsCOMArray<nsXULTemplateResultRDF>* array = it.UserData();
+ int32_t count = array->Count();
+ for (int32_t i = 0; i < count; ++i) {
+ cb.NoteXPCOMChild(array->ObjectAt(i));
+ }
+ }
+
+ for (auto it = tmp->mRuleToBindingsMap.Iter(); !it.Done(); it.Next()) {
+ cb.NoteXPCOMChild(it.Key());
+ }
+
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueries)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULTemplateQueryProcessorRDF)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULTemplateQueryProcessorRDF)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULTemplateQueryProcessorRDF)
+ NS_INTERFACE_MAP_ENTRY(nsIXULTemplateQueryProcessor)
+ NS_INTERFACE_MAP_ENTRY(nsIRDFObserver)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULTemplateQueryProcessor)
+NS_INTERFACE_MAP_END
+
+nsXULTemplateQueryProcessorRDF::nsXULTemplateQueryProcessorRDF(void)
+ : mDB(nullptr),
+ mBuilder(nullptr),
+ mQueryProcessorRDFInited(false),
+ mGenerationStarted(false),
+ mUpdateBatchNest(0),
+ mSimpleRuleMemberTest(nullptr)
+{
+ gRefCnt++;
+}
+
+nsXULTemplateQueryProcessorRDF::~nsXULTemplateQueryProcessorRDF(void)
+{
+ if (--gRefCnt == 0) {
+ NS_IF_RELEASE(gRDFService);
+ NS_IF_RELEASE(gRDFContainerUtils);
+ NS_IF_RELEASE(kNC_BookmarkSeparator);
+ NS_IF_RELEASE(kRDF_type);
+ }
+}
+
+nsresult
+nsXULTemplateQueryProcessorRDF::InitGlobals()
+{
+ nsresult rv;
+
+ // Initialize the global shared reference to the service
+ // manager and get some shared resource objects.
+ if (!gRDFService) {
+ NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
+ rv = CallGetService(kRDFServiceCID, &gRDFService);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ if (!gRDFContainerUtils) {
+ NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
+ rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ if (!kNC_BookmarkSeparator) {
+ gRDFService->GetResource(
+ NS_LITERAL_CSTRING(NC_NAMESPACE_URI "BookmarkSeparator"),
+ &kNC_BookmarkSeparator);
+ }
+
+ if (!kRDF_type) {
+ gRDFService->GetResource(
+ NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "type"),
+ &kRDF_type);
+ }
+
+ return NS_OK;
+}
+
+//----------------------------------------------------------------------
+//
+// nsIXULTemplateQueryProcessor interface
+//
+
+NS_IMETHODIMP
+nsXULTemplateQueryProcessorRDF::GetDatasource(nsIArray* aDataSources,
+ nsIDOMNode* aRootNode,
+ bool aIsTrusted,
+ nsIXULTemplateBuilder* aBuilder,
+ bool* aShouldDelayBuilding,
+ nsISupports** aResult)
+{
+ nsCOMPtr<nsIRDFCompositeDataSource> compDB;
+ nsCOMPtr<nsIContent> root = do_QueryInterface(aRootNode);
+ nsresult rv;
+
+ *aResult = nullptr;
+ *aShouldDelayBuilding = false;
+
+ NS_ENSURE_TRUE(root, NS_ERROR_UNEXPECTED);
+
+ // make sure the RDF service is set up
+ rv = InitGlobals();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // create a database for the builder
+ compDB = do_CreateInstance(NS_RDF_DATASOURCE_CONTRACTID_PREFIX
+ "composite-datasource");
+ if (!compDB) {
+ NS_ERROR("unable to construct new composite data source");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // check for magical attributes. XXX move to ``flags''?
+ if (root->AttrValueIs(kNameSpaceID_None,
+ nsGkAtoms::coalesceduplicatearcs,
+ nsGkAtoms::_false, eCaseMatters))
+ compDB->SetCoalesceDuplicateArcs(false);
+
+ if (root->AttrValueIs(kNameSpaceID_None,
+ nsGkAtoms::allownegativeassertions,
+ nsGkAtoms::_false, eCaseMatters))
+ compDB->SetAllowNegativeAssertions(false);
+
+ if (aIsTrusted) {
+ // 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<nsIRDFDataSource> localstore;
+ rv = gRDFService->GetDataSource("rdf:local-store",
+ getter_AddRefs(localstore));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = compDB->AddDataSource(localstore);
+ NS_ASSERTION(NS_SUCCEEDED(rv), "unable to add local store to db");
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ uint32_t length, index;
+ rv = aDataSources->GetLength(&length);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ for (index = 0; index < length; index++) {
+
+ nsCOMPtr<nsIURI> uri = do_QueryElementAt(aDataSources, index);
+ if (!uri) // we ignore other datasources than uri
+ continue;
+
+ nsCOMPtr<nsIRDFDataSource> ds;
+ nsAutoCString uristrC;
+ uri->GetSpec(uristrC);
+
+ 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
+ nsAutoCString msg;
+ msg.AppendLiteral("unable to load datasource '");
+ msg.Append(uristrC);
+ msg.Append('\'');
+ NS_WARNING(msg.get());
+ #endif
+ continue;
+ }
+
+ compDB->AddDataSource(ds);
+ }
+
+
+ // check if we were given an inference engine type
+ nsAutoString infer;
+ nsCOMPtr<nsIRDFDataSource> db;
+ root->GetAttr(kNameSpaceID_None, nsGkAtoms::infer, infer);
+ if (!infer.IsEmpty()) {
+ nsCString inferCID(NS_RDF_INFER_DATASOURCE_CONTRACTID_PREFIX);
+ AppendUTF16toUTF8(infer, inferCID);
+ nsCOMPtr<nsIRDFInferDataSource> inferDB =
+ do_CreateInstance(inferCID.get());
+
+ if (inferDB) {
+ inferDB->SetBaseDataSource(compDB);
+ db = do_QueryInterface(inferDB);
+ }
+ else {
+ NS_WARNING("failed to construct inference engine specified on template");
+ }
+ }
+
+ if (!db)
+ db = compDB;
+
+ return CallQueryInterface(db, aResult);
+}
+
+NS_IMETHODIMP
+nsXULTemplateQueryProcessorRDF::InitializeForBuilding(nsISupports* aDatasource,
+ nsIXULTemplateBuilder* aBuilder,
+ nsIDOMNode* aRootNode)
+{
+ if (!mQueryProcessorRDFInited) {
+ nsresult rv = InitGlobals();
+ if (NS_FAILED(rv))
+ return rv;
+
+ mQueryProcessorRDFInited = true;
+ }
+
+ // don't do anything if generation has already been done
+ if (mGenerationStarted)
+ return NS_ERROR_UNEXPECTED;
+
+ mDB = do_QueryInterface(aDatasource);
+ mBuilder = aBuilder;
+
+ ComputeContainmentProperties(aRootNode);
+
+ // Add ourselves as a datasource observer
+ if (mDB)
+ mDB->AddObserver(this);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXULTemplateQueryProcessorRDF::Done()
+{
+ if (!mQueryProcessorRDFInited)
+ return NS_OK;
+
+ if (mDB)
+ mDB->RemoveObserver(this);
+
+ mDB = nullptr;
+ mBuilder = nullptr;
+ mRefVariable = nullptr;
+ mLastRef = nullptr;
+
+ mGenerationStarted = false;
+ mUpdateBatchNest = 0;
+
+ mContainmentProperties.Clear();
+
+ for (ReteNodeSet::Iterator node = mAllTests.First();
+ node != mAllTests.Last(); ++node)
+ delete *node;
+
+ mAllTests.Clear();
+ mRDFTests.Clear();
+ mQueries.Clear();
+
+ mSimpleRuleMemberTest = nullptr;
+
+ mBindingDependencies.Clear();
+
+ mMemoryElementToResultMap.Clear();
+
+ mRuleToBindingsMap.Clear();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXULTemplateQueryProcessorRDF::CompileQuery(nsIXULTemplateBuilder* aBuilder,
+ nsIDOMNode* aQueryNode,
+ nsIAtom* aRefVariable,
+ nsIAtom* aMemberVariable,
+ nsISupports** _retval)
+{
+ RefPtr<nsRDFQuery> query = new nsRDFQuery(this);
+ if (!query)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ query->mRefVariable = aRefVariable;
+ if (!mRefVariable)
+ mRefVariable = aRefVariable;
+
+ if (!aMemberVariable)
+ query->mMemberVariable = NS_Atomize("?");
+ else
+ query->mMemberVariable = aMemberVariable;
+
+ nsresult rv;
+ TestNode *lastnode = nullptr;
+
+ nsCOMPtr<nsIContent> content = do_QueryInterface(aQueryNode);
+
+ if (content->NodeInfo()->Equals(nsGkAtoms::_template, kNameSpaceID_XUL)) {
+ // simplified syntax with no rules
+
+ query->SetSimple();
+ NS_ASSERTION(!mSimpleRuleMemberTest,
+ "CompileQuery called twice with the same template");
+ if (!mSimpleRuleMemberTest)
+ rv = CompileSimpleQuery(query, content, &lastnode);
+ else
+ rv = NS_ERROR_FAILURE;
+ }
+ else if (content->NodeInfo()->Equals(nsGkAtoms::rule, kNameSpaceID_XUL)) {
+ // simplified syntax with at least one rule
+ query->SetSimple();
+ rv = CompileSimpleQuery(query, content, &lastnode);
+ }
+ else {
+ rv = CompileExtendedQuery(query, content, &lastnode);
+ }
+
+ if (NS_FAILED(rv))
+ return rv;
+
+ query->SetQueryNode(aQueryNode);
+
+ nsInstantiationNode* instnode = new nsInstantiationNode(this, query);
+
+ // this and other functions always add nodes to mAllTests first. That
+ // way if something fails, the node will just sit harmlessly in mAllTests
+ // where it can be deleted later.
+ rv = mAllTests.Add(instnode);
+ if (NS_FAILED(rv)) {
+ delete instnode;
+ return rv;
+ }
+
+ rv = lastnode->AddChild(instnode);
+ if (NS_FAILED(rv))
+ return rv;
+
+ mQueries.AppendElement(query);
+
+ query.forget(_retval);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXULTemplateQueryProcessorRDF::GenerateResults(nsISupports* aDatasource,
+ nsIXULTemplateResult* aRef,
+ nsISupports* aQuery,
+ nsISimpleEnumerator** aResults)
+{
+ nsCOMPtr<nsITemplateRDFQuery> rdfquery = do_QueryInterface(aQuery);
+ if (! rdfquery)
+ return NS_ERROR_INVALID_ARG;
+
+ mGenerationStarted = true;
+
+ // should be safe to cast here since the query is a
+ // non-scriptable nsITemplateRDFQuery
+ nsRDFQuery* query = static_cast<nsRDFQuery *>(aQuery);
+
+ *aResults = nullptr;
+
+ nsCOMPtr<nsISimpleEnumerator> results;
+
+ if (aRef) {
+ // make sure that cached results were generated for this ref, and if not,
+ // regenerate them. Otherwise, things will go wrong for templates bound to
+ // an HTML element as they are not generated lazily.
+ if (aRef == mLastRef) {
+ query->UseCachedResults(getter_AddRefs(results));
+ }
+ else {
+ // clear the cached results
+ int32_t count = mQueries.Length();
+ for (int32_t r = 0; r < count; r++) {
+ mQueries[r]->ClearCachedResults();
+ }
+ }
+
+ if (! results) {
+ if (! query->mRefVariable)
+ query->mRefVariable = NS_Atomize("?uri");
+
+ nsCOMPtr<nsIRDFResource> refResource;
+ aRef->GetResource(getter_AddRefs(refResource));
+ if (! refResource)
+ return NS_ERROR_FAILURE;
+
+ // Propagate the assignments through the network
+ TestNode* root = query->GetRoot();
+
+ if (query->IsSimple() && mSimpleRuleMemberTest) {
+ // get the top node in the simple rule tree
+ root = mSimpleRuleMemberTest->GetParent();
+ mLastRef = aRef;
+ }
+
+ if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) {
+ nsAutoString id;
+ aRef->GetId(id);
+
+ nsAutoString rvar;
+ query->mRefVariable->ToString(rvar);
+ nsAutoString mvar;
+ query->mMemberVariable->ToString(mvar);
+
+ MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
+ ("QueryProcessor::GenerateResults using ref %s and vars [ ref: %s member: %s]",
+ NS_ConvertUTF16toUTF8(id).get(),
+ NS_ConvertUTF16toUTF8(rvar).get(),
+ NS_ConvertUTF16toUTF8(mvar).get()));
+ }
+
+ if (root) {
+ // the seed is the initial instantiation, which has a single
+ // assignment holding the reference point
+ Instantiation seed;
+ seed.AddAssignment(query->mRefVariable, refResource);
+
+ InstantiationSet* instantiations = new InstantiationSet();
+ instantiations->Append(seed);
+
+ // if the propagation caused a match, then the results will be
+ // cached in the query, retrieved below by calling
+ // UseCachedResults. The matching result set owns the
+ // instantiations and will delete them when results have been
+ // iterated over. If the propagation did not match, the
+ // instantiations need to be deleted.
+ bool owned = false;
+ nsresult rv = root->Propagate(*instantiations, false, owned);
+ if (! owned)
+ delete instantiations;
+ if (NS_FAILED(rv))
+ return rv;
+
+ query->UseCachedResults(getter_AddRefs(results));
+ }
+ }
+ }
+
+ if (! results) {
+ // no results were found so create an empty set
+ results = new nsXULTemplateResultSetRDF(this, query, nullptr);
+ }
+
+ results.swap(*aResults);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXULTemplateQueryProcessorRDF::AddBinding(nsIDOMNode* aRuleNode,
+ nsIAtom* aVar,
+ nsIAtom* aRef,
+ const nsAString& aExpr)
+{
+ // add a <binding> to a rule. When a result is matched, the bindings are
+ // examined to add additional variable assignments
+
+ // bindings can't be added once result generation has started, otherwise
+ // the array sizes will get out of sync
+ if (mGenerationStarted)
+ return NS_ERROR_UNEXPECTED;
+
+ nsCOMPtr<nsIRDFResource> property;
+ nsresult rv = gRDFService->GetUnicodeResource(aExpr, getter_AddRefs(property));
+ if (NS_FAILED(rv))
+ return rv;
+
+ RefPtr<RDFBindingSet> bindings = mRuleToBindingsMap.GetWeak(aRuleNode);
+ if (!bindings) {
+ bindings = new RDFBindingSet();
+ mRuleToBindingsMap.Put(aRuleNode, bindings);
+ }
+
+ return bindings->AddBinding(aVar, aRef, property);
+}
+
+NS_IMETHODIMP
+nsXULTemplateQueryProcessorRDF::TranslateRef(nsISupports* aDatasource,
+ const nsAString& aRefString,
+ nsIXULTemplateResult** aRef)
+{
+ // make sure the RDF service is set up
+ nsresult rv = InitGlobals();
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIRDFResource> uri;
+ gRDFService->GetUnicodeResource(aRefString, getter_AddRefs(uri));
+
+ RefPtr<nsXULTemplateResultRDF> refresult = new nsXULTemplateResultRDF(uri);
+ if (! refresult)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ refresult.forget(aRef);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXULTemplateQueryProcessorRDF::CompareResults(nsIXULTemplateResult* aLeft,
+ nsIXULTemplateResult* aRight,
+ nsIAtom* aVar,
+ uint32_t aSortHints,
+ int32_t* aResult)
+{
+ NS_ENSURE_ARG_POINTER(aLeft);
+ NS_ENSURE_ARG_POINTER(aRight);
+
+ *aResult = 0;
+
+ // for natural order sorting, use the index in the RDF container for the
+ // order. If there is no container, just sort them arbitrarily.
+ if (!aVar) {
+ // if a result has a negative index, just sort it first
+ int32_t leftindex = GetContainerIndexOf(aLeft);
+ int32_t rightindex = GetContainerIndexOf(aRight);
+ *aResult = leftindex == rightindex ? 0 :
+ leftindex > rightindex ? 1 :
+ -1;
+ return NS_OK;
+ }
+
+ nsDependentAtomString sortkey(aVar);
+
+ nsCOMPtr<nsISupports> leftNode, rightNode;
+
+ if (!sortkey.IsEmpty() && sortkey[0] != '?' &&
+ !StringBeginsWith(sortkey, NS_LITERAL_STRING("rdf:")) &&
+ mDB) {
+ // if the sort key is not a template variable, it should be an RDF
+ // predicate. Get the targets and compare those instead.
+ nsCOMPtr<nsIRDFResource> predicate;
+ nsresult rv = gRDFService->GetUnicodeResource(sortkey, getter_AddRefs(predicate));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // create a predicate with '?sort=true' appended. This special
+ // predicate may be used to have a different sort value than the
+ // displayed value
+ sortkey.AppendLiteral("?sort=true");
+
+ nsCOMPtr<nsIRDFResource> sortPredicate;
+ rv = gRDFService->GetUnicodeResource(sortkey, getter_AddRefs(sortPredicate));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = GetSortValue(aLeft, predicate, sortPredicate, getter_AddRefs(leftNode));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = GetSortValue(aRight, predicate, sortPredicate, getter_AddRefs(rightNode));
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ else {
+ // get the values for the sort key from the results
+ aLeft->GetBindingObjectFor(aVar, getter_AddRefs(leftNode));
+ aRight->GetBindingObjectFor(aVar, getter_AddRefs(rightNode));
+ }
+
+ {
+ // Literals?
+ nsCOMPtr<nsIRDFLiteral> l = do_QueryInterface(leftNode);
+ if (l) {
+ nsCOMPtr<nsIRDFLiteral> r = do_QueryInterface(rightNode);
+ if (r) {
+ const char16_t *lstr, *rstr;
+ l->GetValueConst(&lstr);
+ r->GetValueConst(&rstr);
+
+ *aResult = XULSortServiceImpl::CompareValues(
+ nsDependentString(lstr),
+ nsDependentString(rstr), aSortHints);
+ }
+
+ return NS_OK;
+ }
+ }
+
+ {
+ // Dates?
+ nsCOMPtr<nsIRDFDate> l = do_QueryInterface(leftNode);
+ if (l) {
+ nsCOMPtr<nsIRDFDate> r = do_QueryInterface(rightNode);
+ if (r) {
+ PRTime ldate, rdate;
+ l->GetValue(&ldate);
+ r->GetValue(&rdate);
+
+ int64_t delta = ldate - rdate;
+ if (delta == 0)
+ *aResult = 0;
+ else if (delta >= 0)
+ *aResult = 1;
+ else
+ *aResult = -1;
+ }
+
+ return NS_OK;
+ }
+ }
+
+ {
+ // Integers?
+ nsCOMPtr<nsIRDFInt> l = do_QueryInterface(leftNode);
+ if (l) {
+ nsCOMPtr<nsIRDFInt> r = do_QueryInterface(rightNode);
+ if (r) {
+ int32_t lval, rval;
+ l->GetValue(&lval);
+ r->GetValue(&rval);
+
+ *aResult = lval - rval;
+ }
+
+ return NS_OK;
+ }
+ }
+
+ nsICollation* collation = nsXULContentUtils::GetCollation();
+ if (collation) {
+ // Blobs? (We can only compare these reasonably if we have a
+ // collation object.)
+ nsCOMPtr<nsIRDFBlob> l = do_QueryInterface(leftNode);
+ if (l) {
+ nsCOMPtr<nsIRDFBlob> r = do_QueryInterface(rightNode);
+ if (r) {
+ const uint8_t *lval, *rval;
+ int32_t llen, rlen;
+ l->GetValue(&lval);
+ l->GetLength(&llen);
+ r->GetValue(&rval);
+ r->GetLength(&rlen);
+
+ collation->CompareRawSortKey(lval, llen, rval, rlen, aResult);
+ }
+ }
+ }
+
+ // if the results are none of the above, just pretend that they are equal
+ return NS_OK;
+}
+
+//----------------------------------------------------------------------
+//
+// nsIRDFObserver interface
+//
+
+
+NS_IMETHODIMP
+nsXULTemplateQueryProcessorRDF::OnAssert(nsIRDFDataSource* aDataSource,
+ nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget)
+{
+ // Ignore updates if we're batching
+ if (mUpdateBatchNest)
+ return(NS_OK);
+
+ if (! mBuilder)
+ return NS_OK;
+
+ LOG("onassert", aSource, aProperty, aTarget);
+
+ Propagate(aSource, aProperty, aTarget);
+ SynchronizeAll(aSource, aProperty, nullptr, aTarget);
+ return NS_OK;
+}
+
+
+
+NS_IMETHODIMP
+nsXULTemplateQueryProcessorRDF::OnUnassert(nsIRDFDataSource* aDataSource,
+ nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget)
+{
+ // Ignore updates if we're batching
+ if (mUpdateBatchNest)
+ return NS_OK;
+
+ if (! mBuilder)
+ return NS_OK;
+
+ LOG("onunassert", aSource, aProperty, aTarget);
+
+ Retract(aSource, aProperty, aTarget);
+ SynchronizeAll(aSource, aProperty, aTarget, nullptr);
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsXULTemplateQueryProcessorRDF::OnChange(nsIRDFDataSource* aDataSource,
+ nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aOldTarget,
+ nsIRDFNode* aNewTarget)
+{
+ // Ignore updates if we're batching
+ if (mUpdateBatchNest)
+ return NS_OK;
+
+ if (! mBuilder)
+ return NS_OK;
+
+ LOG("onchange", aSource, aProperty, aNewTarget);
+
+ if (aOldTarget) {
+ // Pull any old results that were relying on aOldTarget
+ Retract(aSource, aProperty, aOldTarget);
+ }
+
+ if (aNewTarget) {
+ // Fire any new results that are activated by aNewTarget
+ Propagate(aSource, aProperty, aNewTarget);
+ }
+
+ // Synchronize any of the content model that may have changed.
+ SynchronizeAll(aSource, aProperty, aOldTarget, aNewTarget);
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsXULTemplateQueryProcessorRDF::OnMove(nsIRDFDataSource* aDataSource,
+ nsIRDFResource* aOldSource,
+ nsIRDFResource* aNewSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget)
+{
+ // Ignore updates if we're batching
+ if (mUpdateBatchNest)
+ return NS_OK;
+
+ NS_NOTYETIMPLEMENTED("write me");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+
+NS_IMETHODIMP
+nsXULTemplateQueryProcessorRDF::OnBeginUpdateBatch(nsIRDFDataSource* aDataSource)
+{
+ mUpdateBatchNest++;
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsXULTemplateQueryProcessorRDF::OnEndUpdateBatch(nsIRDFDataSource* aDataSource)
+{
+ NS_ASSERTION(mUpdateBatchNest > 0, "badly nested update batch");
+ if (--mUpdateBatchNest <= 0) {
+ mUpdateBatchNest = 0;
+
+ if (mBuilder)
+ mBuilder->Rebuild();
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsXULTemplateQueryProcessorRDF::Propagate(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget)
+{
+ // When a new assertion is added to the graph, determine any new matches
+ // that must be added to the template builder. First, iterate through all
+ // the RDF tests (<member> and <triple> tests), and find the topmost test
+ // that would be affected by the new assertion.
+ nsresult rv;
+
+ ReteNodeSet livenodes;
+
+ if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) {
+ const char* sourceStr;
+ aSource->GetValueConst(&sourceStr);
+ const char* propertyStr;
+ aProperty->GetValueConst(&propertyStr);
+ nsAutoString targetStr;
+ nsXULContentUtils::GetTextForNode(aTarget, targetStr);
+
+ MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
+ ("nsXULTemplateQueryProcessorRDF::Propagate: [%s] -> [%s] -> [%s]\n",
+ sourceStr, propertyStr, NS_ConvertUTF16toUTF8(targetStr).get()));
+ }
+
+ {
+ ReteNodeSet::Iterator last = mRDFTests.Last();
+ for (ReteNodeSet::Iterator i = mRDFTests.First(); i != last; ++i) {
+ nsRDFTestNode* rdftestnode = static_cast<nsRDFTestNode*>(*i);
+
+ Instantiation seed;
+ if (rdftestnode->CanPropagate(aSource, aProperty, aTarget, seed)) {
+ rv = livenodes.Add(rdftestnode);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ }
+ }
+
+ // Now, we'll go through each, and any that aren't dominated by
+ // another live node will be used to propagate the assertion
+ // through the rule network
+ {
+ ReteNodeSet::Iterator last = livenodes.Last();
+ for (ReteNodeSet::Iterator i = livenodes.First(); i != last; ++i) {
+ nsRDFTestNode* rdftestnode = static_cast<nsRDFTestNode*>(*i);
+
+ // What happens here is we create an instantiation as if we were
+ // at the found test in the rule network. For example, if the
+ // found test was a member test (parent => child), the parent
+ // and child variables are assigned the values provided by the new
+ // RDF assertion in the graph. The Constrain call is used to go
+ // up to earlier RDF tests, filling in variables as it goes.
+ // Constrain will eventually get up to the top node, an
+ // nsContentTestNode, which takes the value of the reference
+ // variable and calls the template builder to see if a result has
+ // been generated already for the reference value. If it hasn't,
+ // the new assertion couldn't cause a new match. If the result
+ // exists, call Propagate to continue to the later RDF tests to
+ // fill in the rest of the variable assignments.
+
+ // Bogus, to get the seed instantiation
+ Instantiation seed;
+ rdftestnode->CanPropagate(aSource, aProperty, aTarget, seed);
+
+ InstantiationSet* instantiations = new InstantiationSet();
+ instantiations->Append(seed);
+
+ rv = rdftestnode->Constrain(*instantiations);
+ if (NS_FAILED(rv)) {
+ delete instantiations;
+ return rv;
+ }
+
+ bool owned = false;
+ if (!instantiations->Empty())
+ rv = rdftestnode->Propagate(*instantiations, true, owned);
+
+ // owned should always be false in update mode, but check just
+ // to be sure
+ if (!owned)
+ delete instantiations;
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ }
+
+ return NS_OK;
+}
+
+
+nsresult
+nsXULTemplateQueryProcessorRDF::Retract(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget)
+{
+
+ if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) {
+ const char* sourceStr;
+ aSource->GetValueConst(&sourceStr);
+ const char* propertyStr;
+ aProperty->GetValueConst(&propertyStr);
+ nsAutoString targetStr;
+ nsXULContentUtils::GetTextForNode(aTarget, targetStr);
+
+ MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
+ ("nsXULTemplateQueryProcessorRDF::Retract: [%s] -> [%s] -> [%s]\n",
+ sourceStr, propertyStr, NS_ConvertUTF16toUTF8(targetStr).get()));
+ }
+
+ // Retract any currently active rules that will no longer be matched.
+ ReteNodeSet::ConstIterator lastnode = mRDFTests.Last();
+ for (ReteNodeSet::ConstIterator node = mRDFTests.First(); node != lastnode; ++node) {
+ const nsRDFTestNode* rdftestnode = static_cast<const nsRDFTestNode*>(*node);
+
+ rdftestnode->Retract(aSource, aProperty, aTarget);
+
+ // Now fire any newly revealed rules
+ // XXXwaterson yo. write me.
+ // The intent here is to handle any rules that might be
+ // "revealed" by the removal of an assertion from the datasource.
+ // Waterson doesn't think we support negated conditions in a rule.
+ // Nor is he sure that this is currently useful.
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsXULTemplateQueryProcessorRDF::SynchronizeAll(nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aOldTarget,
+ nsIRDFNode* aNewTarget)
+{
+ // Update each match that contains <aSource, aProperty, aOldTarget>.
+
+ // Get all the matches whose assignments are currently supported
+ // by aSource and aProperty: we'll need to recompute them.
+ ResultArray* results;
+ if (!mBindingDependencies.Get(aSource, &results) || !mBuilder)
+ return NS_OK;
+
+ uint32_t length = results->Length();
+
+ for (uint32_t r = 0; r < length; r++) {
+ nsXULTemplateResultRDF* result = (*results)[r];
+ if (result) {
+ // synchronize the result's bindings and then update the builder
+ // so that content can be updated
+ if (result->SyncAssignments(aSource, aProperty, aNewTarget)) {
+ nsITemplateRDFQuery* query = result->Query();
+ if (query) {
+ nsCOMPtr<nsIDOMNode> querynode;
+ query->GetQueryNode(getter_AddRefs(querynode));
+
+ mBuilder->ResultBindingChanged(result);
+ }
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsXULTemplateQueryProcessorRDF::Log(const char* aOperation,
+ nsIRDFResource* aSource,
+ nsIRDFResource* aProperty,
+ nsIRDFNode* aTarget)
+{
+ if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) {
+ nsresult rv;
+
+ const char* sourceStr;
+ rv = aSource->GetValueConst(&sourceStr);
+ if (NS_FAILED(rv))
+ return rv;
+
+ MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
+ ("xultemplate[%p] %8s [%s]--", this, aOperation, sourceStr));
+
+ const char* propertyStr;
+ rv = aProperty->GetValueConst(&propertyStr);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsAutoString targetStr;
+ rv = nsXULContentUtils::GetTextForNode(aTarget, targetStr);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsAutoCString targetstrC;
+ targetstrC.AssignWithConversion(targetStr);
+ MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
+ (" --[%s]-->[%s]",
+ propertyStr,
+ targetstrC.get()));
+ }
+ return NS_OK;
+}
+
+nsresult
+nsXULTemplateQueryProcessorRDF::CheckContainer(nsIRDFResource* aResource,
+ bool* aIsContainer)
+{
+ NS_ENSURE_ARG_POINTER(aIsContainer);
+ NS_ENSURE_STATE(mDB);
+
+ // We have to look at all of the arcs extending out of the
+ // resource: if any of them are that "containment" property, then
+ // we know we'll have children.
+ bool isContainer = false;
+
+ for (nsResourceSet::ConstIterator property = mContainmentProperties.First();
+ property != mContainmentProperties.Last();
+ property++) {
+ bool hasArc = false;
+ mDB->HasArcOut(aResource, *property, &hasArc);
+
+ if (hasArc) {
+ // Well, it's a container...
+ isContainer = true;
+ break;
+ }
+ }
+
+ // If we get here, and we're still not sure if it's a container,
+ // then see if it's an RDF container
+ if (! isContainer) {
+ gRDFContainerUtils->IsContainer(mDB, aResource, &isContainer);
+ }
+
+ *aIsContainer = isContainer;
+
+ return NS_OK;
+}
+
+nsresult
+nsXULTemplateQueryProcessorRDF::CheckEmpty(nsIRDFResource* aResource,
+ bool* aIsEmpty)
+{
+ NS_ENSURE_STATE(mDB);
+ *aIsEmpty = true;
+
+ for (nsResourceSet::ConstIterator property = mContainmentProperties.First();
+ property != mContainmentProperties.Last();
+ property++) {
+
+ nsCOMPtr<nsIRDFNode> dummy;
+ mDB->GetTarget(aResource, *property, true, getter_AddRefs(dummy));
+
+ if (dummy) {
+ *aIsEmpty = false;
+ break;
+ }
+ }
+
+ if (*aIsEmpty){
+ return nsXULTemplateQueryProcessorRDF::gRDFContainerUtils->
+ IsEmpty(mDB, aResource, aIsEmpty);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsXULTemplateQueryProcessorRDF::CheckIsSeparator(nsIRDFResource* aResource,
+ bool* aIsSeparator)
+{
+ NS_ENSURE_STATE(mDB);
+ return mDB->HasAssertion(aResource, kRDF_type, kNC_BookmarkSeparator,
+ true, aIsSeparator);
+}
+
+//----------------------------------------------------------------------
+
+nsresult
+nsXULTemplateQueryProcessorRDF::ComputeContainmentProperties(nsIDOMNode* aRootNode)
+{
+ // The 'containment' attribute on the root node is a
+ // whitespace-separated list that tells us which properties we
+ // should use to test for containment.
+ nsresult rv;
+
+ mContainmentProperties.Clear();
+
+ nsCOMPtr<nsIContent> content = do_QueryInterface(aRootNode);
+
+ nsAutoString containment;
+ content->GetAttr(kNameSpaceID_None, nsGkAtoms::containment, containment);
+
+ uint32_t len = containment.Length();
+ uint32_t offset = 0;
+ while (offset < len) {
+ while (offset < len && nsCRT::IsAsciiSpace(containment[offset]))
+ ++offset;
+
+ if (offset >= len)
+ break;
+
+ uint32_t end = offset;
+ while (end < len && !nsCRT::IsAsciiSpace(containment[end]))
+ ++end;
+
+ nsAutoString propertyStr;
+ containment.Mid(propertyStr, offset, end - offset);
+
+ nsCOMPtr<nsIRDFResource> property;
+ rv = gRDFService->GetUnicodeResource(propertyStr, getter_AddRefs(property));
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = mContainmentProperties.Add(property);
+ if (NS_FAILED(rv))
+ return rv;
+
+ offset = end;
+ }
+
+#define TREE_PROPERTY_HACK 1
+#if defined(TREE_PROPERTY_HACK)
+ if (! len) {
+ // Some ever-present membership tests.
+ mContainmentProperties.Add(nsXULContentUtils::NC_child);
+ mContainmentProperties.Add(nsXULContentUtils::NC_Folder);
+ }
+#endif
+
+ return NS_OK;
+}
+
+nsresult
+nsXULTemplateQueryProcessorRDF::CompileExtendedQuery(nsRDFQuery* aQuery,
+ nsIContent* aConditions,
+ TestNode** aLastNode)
+{
+ // Compile an extended query's children
+ nsContentTestNode* idnode =
+ new nsContentTestNode(this, aQuery->mRefVariable);
+
+ aQuery->SetRoot(idnode);
+ nsresult rv = mAllTests.Add(idnode);
+ if (NS_FAILED(rv)) {
+ delete idnode;
+ return rv;
+ }
+
+ TestNode* prevnode = idnode;
+
+ for (nsIContent* condition = aConditions->GetFirstChild();
+ condition;
+ condition = condition->GetNextSibling()) {
+
+ // the <content> condition should always be the first child
+ if (condition->IsXULElement(nsGkAtoms::content)) {
+ if (condition != aConditions->GetFirstChild()) {
+ nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_CONTENT_NOT_FIRST);
+ continue;
+ }
+
+ // check for <content tag='tag'/> which indicates that matches
+ // should only be generated for items inside content with that tag
+ nsAutoString tagstr;
+ condition->GetAttr(kNameSpaceID_None, nsGkAtoms::tag, tagstr);
+
+ nsCOMPtr<nsIAtom> tag;
+ if (! tagstr.IsEmpty()) {
+ tag = NS_Atomize(tagstr);
+ }
+
+ nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(condition->GetComposedDoc());
+ if (! doc)
+ return NS_ERROR_FAILURE;
+
+ idnode->SetTag(tag, doc);
+ continue;
+ }
+
+ TestNode* testnode = nullptr;
+ nsresult rv = CompileQueryChild(condition->NodeInfo()->NameAtom(),
+ aQuery, condition, prevnode, &testnode);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (testnode) {
+ rv = prevnode->AddChild(testnode);
+ if (NS_FAILED(rv))
+ return rv;
+
+ prevnode = testnode;
+ }
+ }
+
+ *aLastNode = prevnode;
+
+ return NS_OK;
+}
+
+nsresult
+nsXULTemplateQueryProcessorRDF::CompileQueryChild(nsIAtom* aTag,
+ nsRDFQuery* aQuery,
+ nsIContent* aCondition,
+ TestNode* aParentNode,
+ TestNode** aResult)
+{
+ nsresult rv = NS_OK;
+
+ if (aTag == nsGkAtoms::triple) {
+ rv = CompileTripleCondition(aQuery, aCondition, aParentNode, aResult);
+ }
+ else if (aTag == nsGkAtoms::member) {
+ rv = CompileMemberCondition(aQuery, aCondition, aParentNode, aResult);
+ }
+ else if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Info)) {
+ nsAutoString tagstr;
+ aTag->ToString(tagstr);
+
+ nsAutoCString tagstrC;
+ tagstrC.AssignWithConversion(tagstr);
+ MOZ_LOG(gXULTemplateLog, LogLevel::Info,
+ ("xultemplate[%p] unrecognized condition test <%s>",
+ this, tagstrC.get()));
+ }
+
+ return rv;
+}
+
+nsresult
+nsXULTemplateQueryProcessorRDF::ParseLiteral(const nsString& aParseType,
+ const nsString& aValue,
+ nsIRDFNode** aResult)
+{
+ nsresult rv = NS_OK;
+ *aResult = nullptr;
+
+ if (aParseType.EqualsLiteral(PARSE_TYPE_INTEGER)) {
+ nsCOMPtr<nsIRDFInt> intLiteral;
+ nsresult errorCode;
+ int32_t intValue = aValue.ToInteger(&errorCode);
+ if (NS_FAILED(errorCode))
+ return NS_ERROR_FAILURE;
+ rv = gRDFService->GetIntLiteral(intValue, getter_AddRefs(intLiteral));
+ if (NS_FAILED(rv))
+ return rv;
+ intLiteral.forget(aResult);
+ }
+ else {
+ nsCOMPtr<nsIRDFLiteral> literal;
+ rv = gRDFService->GetLiteral(aValue.get(), getter_AddRefs(literal));
+ if (NS_FAILED(rv))
+ return rv;
+ literal.forget(aResult);
+ }
+ return rv;
+}
+
+nsresult
+nsXULTemplateQueryProcessorRDF::CompileTripleCondition(nsRDFQuery* aQuery,
+ nsIContent* aCondition,
+ TestNode* aParentNode,
+ TestNode** aResult)
+{
+ // Compile a <triple> condition, which must be of the form:
+ //
+ // <triple subject="?var1|resource"
+ // predicate="resource"
+ // object="?var2|resource|literal" />
+ //
+ // XXXwaterson Some day it would be cool to allow the 'predicate'
+ // to be bound to a variable.
+
+ // subject
+ nsAutoString subject;
+ aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::subject, subject);
+
+ nsCOMPtr<nsIAtom> svar;
+ nsCOMPtr<nsIRDFResource> sres;
+ if (subject.IsEmpty()) {
+ nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_BAD_SUBJECT);
+ return NS_OK;
+ }
+ if (subject[0] == char16_t('?'))
+ svar = NS_Atomize(subject);
+ else
+ gRDFService->GetUnicodeResource(subject, getter_AddRefs(sres));
+
+ // predicate
+ nsAutoString predicate;
+ aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::predicate, predicate);
+
+ nsCOMPtr<nsIRDFResource> pres;
+ if (predicate.IsEmpty() || predicate[0] == char16_t('?')) {
+ nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_BAD_PREDICATE);
+ return NS_OK;
+ }
+ gRDFService->GetUnicodeResource(predicate, getter_AddRefs(pres));
+
+ // object
+ nsAutoString object;
+ aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::object, object);
+
+ nsCOMPtr<nsIAtom> ovar;
+ nsCOMPtr<nsIRDFNode> onode;
+ if (object.IsEmpty()) {
+ nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_BAD_OBJECT);
+ return NS_OK;
+ }
+
+ if (object[0] == char16_t('?')) {
+ ovar = NS_Atomize(object);
+ }
+ else if (object.FindChar(':') != -1) { // XXXwaterson evil.
+ // treat as resource
+ nsCOMPtr<nsIRDFResource> resource;
+ gRDFService->GetUnicodeResource(object, getter_AddRefs(resource));
+ onode = do_QueryInterface(resource);
+ }
+ else {
+ nsAutoString parseType;
+ aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::parsetype, parseType);
+ nsresult rv = ParseLiteral(parseType, object, getter_AddRefs(onode));
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ nsRDFPropertyTestNode* testnode = nullptr;
+
+ if (svar && ovar) {
+ testnode = new nsRDFPropertyTestNode(aParentNode, this, svar, pres, ovar);
+ }
+ else if (svar) {
+ testnode = new nsRDFPropertyTestNode(aParentNode, this, svar, pres, onode);
+ }
+ else if (ovar) {
+ testnode = new nsRDFPropertyTestNode(aParentNode, this, sres, pres, ovar);
+ }
+ else {
+ nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_NO_VAR);
+ return NS_OK;
+ }
+
+ // add testnode to mAllTests first. If adding to mRDFTests fails, just
+ // leave it in the list so that it can be deleted later.
+ MOZ_ASSERT(testnode);
+ nsresult rv = mAllTests.Add(testnode);
+ if (NS_FAILED(rv)) {
+ delete testnode;
+ return rv;
+ }
+
+ rv = mRDFTests.Add(testnode);
+ if (NS_FAILED(rv))
+ return rv;
+
+ *aResult = testnode;
+ return NS_OK;
+}
+
+nsresult
+nsXULTemplateQueryProcessorRDF::CompileMemberCondition(nsRDFQuery* aQuery,
+ nsIContent* aCondition,
+ TestNode* aParentNode,
+ TestNode** aResult)
+{
+ // Compile a <member> condition, which must be of the form:
+ //
+ // <member container="?var1" child="?var2" />
+ //
+
+ // container
+ nsAutoString container;
+ aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::container, container);
+
+ if (!container.IsEmpty() && container[0] != char16_t('?')) {
+ nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_MEMBER_NOCONTAINERVAR);
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIAtom> containervar = NS_Atomize(container);
+
+ // child
+ nsAutoString child;
+ aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::child, child);
+
+ if (!child.IsEmpty() && child[0] != char16_t('?')) {
+ nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_MEMBER_NOCHILDVAR);
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIAtom> childvar = NS_Atomize(child);
+
+ TestNode* testnode =
+ new nsRDFConMemberTestNode(aParentNode,
+ this,
+ containervar,
+ childvar);
+
+ // add testnode to mAllTests first. If adding to mRDFTests fails, just
+ // leave it in the list so that it can be deleted later.
+ nsresult rv = mAllTests.Add(testnode);
+ if (NS_FAILED(rv)) {
+ delete testnode;
+ return rv;
+ }
+
+ rv = mRDFTests.Add(testnode);
+ if (NS_FAILED(rv))
+ return rv;
+
+ *aResult = testnode;
+ return NS_OK;
+}
+
+nsresult
+nsXULTemplateQueryProcessorRDF::AddDefaultSimpleRules(nsRDFQuery* aQuery,
+ TestNode** aChildNode)
+{
+ // XXXndeakin should check for tag in query processor instead of builder?
+ nsContentTestNode* idnode =
+ new nsContentTestNode(this,
+ aQuery->mRefVariable);
+
+ // Create (?container ^member ?member)
+ nsRDFConMemberTestNode* membernode =
+ new nsRDFConMemberTestNode(idnode,
+ this,
+ aQuery->mRefVariable,
+ aQuery->mMemberVariable);
+
+ // add nodes to mAllTests first. If later calls fail, just leave them in
+ // the list so that they can be deleted later.
+ nsresult rv = mAllTests.Add(idnode);
+ if (NS_FAILED(rv)) {
+ delete idnode;
+ delete membernode;
+ return rv;
+ }
+
+ rv = mAllTests.Add(membernode);
+ if (NS_FAILED(rv)) {
+ delete membernode;
+ return rv;
+ }
+
+ rv = mRDFTests.Add(membernode);
+ if (NS_FAILED(rv))
+ return rv;
+
+ rv = idnode->AddChild(membernode);
+ if (NS_FAILED(rv))
+ return rv;
+
+ mSimpleRuleMemberTest = membernode;
+ *aChildNode = membernode;
+
+ return NS_OK;
+}
+
+nsresult
+nsXULTemplateQueryProcessorRDF::CompileSimpleQuery(nsRDFQuery* aQuery,
+ nsIContent* aQueryElement,
+ TestNode** aLastNode)
+{
+ // Compile a "simple" (or old-school style) <template> query.
+ nsresult rv;
+
+ TestNode* parentNode;
+
+ if (! mSimpleRuleMemberTest) {
+ rv = AddDefaultSimpleRules(aQuery, &parentNode);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ bool hasContainerTest = false;
+
+ TestNode* prevnode = mSimpleRuleMemberTest;
+
+ // Add constraints for the LHS
+ const nsAttrName* name;
+ for (uint32_t i = 0; (name = aQueryElement->GetAttrNameAt(i)); ++i) {
+ // Note: some attributes must be skipped on XUL template query subtree
+
+ // never compare against rdf:property, rdf:instanceOf, {}:id or {}:parsetype attribute
+ if (name->Equals(nsGkAtoms::property, kNameSpaceID_RDF) ||
+ name->Equals(nsGkAtoms::instanceOf, kNameSpaceID_RDF) ||
+ name->Equals(nsGkAtoms::id, kNameSpaceID_None) ||
+ name->Equals(nsGkAtoms::parsetype, kNameSpaceID_None)) {
+ continue;
+ }
+
+ int32_t attrNameSpaceID = name->NamespaceID();
+ if (attrNameSpaceID == kNameSpaceID_XMLNS)
+ continue;
+ nsIAtom* attr = name->LocalName();
+
+ nsAutoString value;
+ aQueryElement->GetAttr(attrNameSpaceID, attr, value);
+
+ TestNode* testnode = nullptr;
+
+ if (name->Equals(nsGkAtoms::iscontainer, kNameSpaceID_None) ||
+ name->Equals(nsGkAtoms::isempty, kNameSpaceID_None)) {
+ // Tests about containerhood and emptiness. These can be
+ // globbed together, mostly. Check to see if we've already
+ // added a container test: we only need one.
+ if (hasContainerTest)
+ continue;
+
+ nsRDFConInstanceTestNode::Test iscontainer =
+ nsRDFConInstanceTestNode::eDontCare;
+
+ static nsIContent::AttrValuesArray strings[] =
+ {&nsGkAtoms::_true, &nsGkAtoms::_false, nullptr};
+ switch (aQueryElement->FindAttrValueIn(kNameSpaceID_None,
+ nsGkAtoms::iscontainer,
+ strings, eCaseMatters)) {
+ case 0: iscontainer = nsRDFConInstanceTestNode::eTrue; break;
+ case 1: iscontainer = nsRDFConInstanceTestNode::eFalse; break;
+ }
+
+ nsRDFConInstanceTestNode::Test isempty =
+ nsRDFConInstanceTestNode::eDontCare;
+
+ switch (aQueryElement->FindAttrValueIn(kNameSpaceID_None,
+ nsGkAtoms::isempty,
+ strings, eCaseMatters)) {
+ case 0: isempty = nsRDFConInstanceTestNode::eTrue; break;
+ case 1: isempty = nsRDFConInstanceTestNode::eFalse; break;
+ }
+
+ testnode = new nsRDFConInstanceTestNode(prevnode,
+ this,
+ aQuery->mMemberVariable,
+ iscontainer,
+ isempty);
+
+ rv = mAllTests.Add(testnode);
+ if (NS_FAILED(rv)) {
+ delete testnode;
+ return rv;
+ }
+
+ rv = mRDFTests.Add(testnode);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ else if (attrNameSpaceID != kNameSpaceID_None || attr != nsGkAtoms::parent) {
+ // It's a simple RDF test
+ nsCOMPtr<nsIRDFResource> property;
+ rv = nsXULContentUtils::GetResource(attrNameSpaceID, attr, getter_AddRefs(property));
+ if (NS_FAILED(rv))
+ return rv;
+
+ // XXXwaterson this is so manky
+ nsCOMPtr<nsIRDFNode> target;
+ if (value.FindChar(':') != -1) { // XXXwaterson WRONG WRONG WRONG!
+ nsCOMPtr<nsIRDFResource> resource;
+ rv = gRDFService->GetUnicodeResource(value, getter_AddRefs(resource));
+ if (NS_FAILED(rv))
+ return rv;
+
+ target = do_QueryInterface(resource);
+ }
+ else {
+ nsAutoString parseType;
+ aQueryElement->GetAttr(kNameSpaceID_None, nsGkAtoms::parsetype, parseType);
+ rv = ParseLiteral(parseType, value, getter_AddRefs(target));
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ testnode = new nsRDFPropertyTestNode(prevnode, this,
+ aQuery->mMemberVariable, property, target);
+ rv = mAllTests.Add(testnode);
+ if (NS_FAILED(rv)) {
+ delete testnode;
+ return rv;
+ }
+
+ rv = mRDFTests.Add(testnode);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+
+ if (testnode) {
+ if (prevnode) {
+ rv = prevnode->AddChild(testnode);
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ else {
+ aQuery->SetRoot(testnode);
+ }
+
+ prevnode = testnode;
+ }
+ }
+
+ *aLastNode = prevnode;
+
+ return NS_OK;
+}
+
+RDFBindingSet*
+nsXULTemplateQueryProcessorRDF::GetBindingsForRule(nsIDOMNode* aRuleNode)
+{
+ return mRuleToBindingsMap.GetWeak(aRuleNode);
+}
+
+void
+nsXULTemplateQueryProcessorRDF::AddBindingDependency(nsXULTemplateResultRDF* aResult,
+ nsIRDFResource* aResource)
+{
+ ResultArray* arr;
+ if (!mBindingDependencies.Get(aResource, &arr)) {
+ arr = new ResultArray();
+
+ mBindingDependencies.Put(aResource, arr);
+ }
+
+ int32_t index = arr->IndexOf(aResult);
+ if (index == -1)
+ arr->AppendElement(aResult);
+}
+
+void
+nsXULTemplateQueryProcessorRDF::RemoveBindingDependency(nsXULTemplateResultRDF* aResult,
+ nsIRDFResource* aResource)
+{
+ ResultArray* arr;
+ if (mBindingDependencies.Get(aResource, &arr)) {
+ int32_t index = arr->IndexOf(aResult);
+ if (index >= 0)
+ arr->RemoveElementAt(index);
+ }
+}
+
+
+nsresult
+nsXULTemplateQueryProcessorRDF::AddMemoryElements(const Instantiation& aInst,
+ nsXULTemplateResultRDF* aResult)
+{
+ // Add the result to a table indexed by supporting MemoryElement
+ MemoryElementSet::ConstIterator last = aInst.mSupport.Last();
+ for (MemoryElementSet::ConstIterator element = aInst.mSupport.First();
+ element != last; ++element) {
+
+ PLHashNumber hash = (element.operator->())->Hash();
+
+ nsCOMArray<nsXULTemplateResultRDF>* arr;
+ if (!mMemoryElementToResultMap.Get(hash, &arr)) {
+ arr = new nsCOMArray<nsXULTemplateResultRDF>();
+ mMemoryElementToResultMap.Put(hash, arr);
+ }
+
+ // results may be added more than once so they will all get deleted properly
+ arr->AppendObject(aResult);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsXULTemplateQueryProcessorRDF::RemoveMemoryElements(const Instantiation& aInst,
+ nsXULTemplateResultRDF* aResult)
+{
+ // Remove the results mapped by the supporting MemoryElement
+ MemoryElementSet::ConstIterator last = aInst.mSupport.Last();
+ for (MemoryElementSet::ConstIterator element = aInst.mSupport.First();
+ element != last; ++element) {
+
+ PLHashNumber hash = (element.operator->())->Hash();
+
+ nsCOMArray<nsXULTemplateResultRDF>* arr;
+ if (mMemoryElementToResultMap.Get(hash, &arr)) {
+ int32_t index = arr->IndexOf(aResult);
+ if (index >= 0)
+ arr->RemoveObjectAt(index);
+
+ uint32_t length = arr->Count();
+ if (! length)
+ mMemoryElementToResultMap.Remove(hash);
+ }
+ }
+
+ return NS_OK;
+}
+
+void
+nsXULTemplateQueryProcessorRDF::RetractElement(const MemoryElement& aMemoryElement)
+{
+ if (! mBuilder)
+ return;
+
+ // when an assertion is removed, look through the memory elements and
+ // find results that are associated with them. Those results will need
+ // to be removed because they no longer match.
+ PLHashNumber hash = aMemoryElement.Hash();
+
+ nsCOMArray<nsXULTemplateResultRDF>* arr;
+ if (mMemoryElementToResultMap.Get(hash, &arr)) {
+ uint32_t length = arr->Count();
+
+ for (int32_t r = length - 1; r >= 0; r--) {
+ nsXULTemplateResultRDF* result = (*arr)[r];
+ if (result) {
+ // because the memory elements are hashed by an integer,
+ // sometimes two different memory elements will have the same
+ // hash code. In this case we check the result to make sure
+ // and only remove those that refer to that memory element.
+ if (result->HasMemoryElement(aMemoryElement)) {
+ nsITemplateRDFQuery* query = result->Query();
+ if (query) {
+ nsCOMPtr<nsIDOMNode> querynode;
+ query->GetQueryNode(getter_AddRefs(querynode));
+
+ mBuilder->RemoveResult(result);
+ }
+
+ // a call to RemoveMemoryElements may have removed it
+ if (!mMemoryElementToResultMap.Get(hash, nullptr))
+ return;
+
+ // the array should have been reduced by one, but check
+ // just to make sure
+ uint32_t newlength = arr->Count();
+ if (r > (int32_t)newlength)
+ r = newlength;
+ }
+ }
+ }
+
+ // if there are no items left, remove the memory element from the hashtable
+ if (!arr->Count())
+ mMemoryElementToResultMap.Remove(hash);
+ }
+}
+
+int32_t
+nsXULTemplateQueryProcessorRDF::GetContainerIndexOf(nsIXULTemplateResult* aResult)
+{
+ // get the reference variable and look up the container in the result
+ nsCOMPtr<nsISupports> ref;
+ nsresult rv = aResult->GetBindingObjectFor(mRefVariable,
+ getter_AddRefs(ref));
+ if (NS_FAILED(rv) || !mDB)
+ return -1;
+
+ nsCOMPtr<nsIRDFResource> container = do_QueryInterface(ref);
+ if (container) {
+ // if the container is an RDF Seq, return the index of the result
+ // in the container.
+ bool isSequence = false;
+ gRDFContainerUtils->IsSeq(mDB, container, &isSequence);
+ if (isSequence) {
+ nsCOMPtr<nsIRDFResource> resource;
+ aResult->GetResource(getter_AddRefs(resource));
+ if (resource) {
+ int32_t index;
+ gRDFContainerUtils->IndexOf(mDB, container, resource, &index);
+ return index;
+ }
+ }
+ }
+
+ // if the container isn't a Seq, or the result isn't in the container,
+ // return -1 indicating no index.
+ return -1;
+}
+
+nsresult
+nsXULTemplateQueryProcessorRDF::GetSortValue(nsIXULTemplateResult* aResult,
+ nsIRDFResource* aPredicate,
+ nsIRDFResource* aSortPredicate,
+ nsISupports** aResultNode)
+{
+ nsCOMPtr<nsIRDFResource> source;
+ nsresult rv = aResult->GetResource(getter_AddRefs(source));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsCOMPtr<nsIRDFNode> value;
+ if (source && mDB) {
+ // first check predicate?sort=true so that datasources may use a
+ // custom value for sorting
+ rv = mDB->GetTarget(source, aSortPredicate, true,
+ getter_AddRefs(value));
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!value) {
+ rv = mDB->GetTarget(source, aPredicate, true,
+ getter_AddRefs(value));
+ if (NS_FAILED(rv))
+ return rv;
+ }
+ }
+
+ *aResultNode = value;
+ NS_IF_ADDREF(*aResultNode);
+ return NS_OK;
+}