content/xul/templates/src/nsXULTemplateQueryProcessorRDF.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsCOMPtr.h"
     7 #include "nsICollation.h"
     8 #include "nsIDOMNode.h"
     9 #include "nsIRDFNode.h"
    10 #include "nsIRDFObserver.h"
    11 #include "nsIRDFRemoteDataSource.h"
    12 #include "nsIRDFInferDataSource.h"
    13 #include "nsIRDFService.h"
    14 #include "nsRDFCID.h"
    15 #include "nsIServiceManager.h"
    16 #include "nsNameSpaceManager.h"
    17 #include "nsGkAtoms.h"
    18 #include "nsIDOMDocument.h"
    19 #include "nsAttrName.h"
    20 #include "rdf.h"
    21 #include "nsArrayUtils.h"
    22 #include "nsIURI.h"
    24 #include "nsContentTestNode.h"
    25 #include "nsRDFConInstanceTestNode.h"
    26 #include "nsRDFConMemberTestNode.h"
    27 #include "nsRDFPropertyTestNode.h"
    28 #include "nsInstantiationNode.h"
    29 #include "nsRDFTestNode.h"
    30 #include "nsXULContentUtils.h"
    31 #include "nsXULTemplateBuilder.h"
    32 #include "nsXULTemplateResultRDF.h"
    33 #include "nsXULTemplateResultSetRDF.h"
    34 #include "nsXULTemplateQueryProcessorRDF.h"
    35 #include "nsXULSortService.h"
    36 #include "nsIDocument.h"
    38 //----------------------------------------------------------------------
    40 #define PARSE_TYPE_INTEGER  "Integer"
    42 nsrefcnt                  nsXULTemplateQueryProcessorRDF::gRefCnt = 0;
    43 nsIRDFService*            nsXULTemplateQueryProcessorRDF::gRDFService;
    44 nsIRDFContainerUtils*     nsXULTemplateQueryProcessorRDF::gRDFContainerUtils;
    45 nsIRDFResource*           nsXULTemplateQueryProcessorRDF::kNC_BookmarkSeparator;
    46 nsIRDFResource*           nsXULTemplateQueryProcessorRDF::kRDF_type;
    48 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULTemplateQueryProcessorRDF)
    50 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULTemplateQueryProcessorRDF)
    51     tmp->Done();
    52 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    54 static PLDHashOperator
    55 BindingDependenciesTraverser(nsISupports* key,
    56                              nsXULTemplateQueryProcessorRDF::ResultArray* array,
    57                              void* userArg)
    58 {
    59     nsCycleCollectionTraversalCallback *cb = 
    60         static_cast<nsCycleCollectionTraversalCallback*>(userArg);
    62     int32_t i, count = array->Length();
    63     for (i = 0; i < count; ++i) {
    64         cb->NoteXPCOMChild(array->ElementAt(i));
    65     }
    67     return PL_DHASH_NEXT;
    68 }
    70 static PLDHashOperator
    71 MemoryElementTraverser(const uint32_t& key,
    72                        nsCOMArray<nsXULTemplateResultRDF>* array,
    73                        void* userArg)
    74 {
    75     nsCycleCollectionTraversalCallback *cb = 
    76         static_cast<nsCycleCollectionTraversalCallback*>(userArg);
    78     int32_t i, count = array->Count();
    79     for (i = 0; i < count; ++i) {
    80         cb->NoteXPCOMChild(array->ObjectAt(i));
    81     }
    83     return PL_DHASH_NEXT;
    84 }
    86 static PLDHashOperator
    87 RuleToBindingTraverser(nsISupports* key, RDFBindingSet* binding, void* userArg)
    88 {
    89     nsCycleCollectionTraversalCallback *cb = 
    90         static_cast<nsCycleCollectionTraversalCallback*>(userArg);
    92     cb->NoteXPCOMChild(key);
    94     return PL_DHASH_NEXT;
    95 }
    97 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULTemplateQueryProcessorRDF)
    98     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDB)
    99     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLastRef)
   100     tmp->mBindingDependencies.EnumerateRead(BindingDependenciesTraverser,
   101                                             &cb);
   102     tmp->mMemoryElementToResultMap.EnumerateRead(MemoryElementTraverser,
   103                                                  &cb);
   104     tmp->mRuleToBindingsMap.EnumerateRead(RuleToBindingTraverser, &cb);
   105     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueries)
   106 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   108 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULTemplateQueryProcessorRDF)
   109 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULTemplateQueryProcessorRDF)
   110 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULTemplateQueryProcessorRDF)
   111     NS_INTERFACE_MAP_ENTRY(nsIXULTemplateQueryProcessor)
   112     NS_INTERFACE_MAP_ENTRY(nsIRDFObserver)
   113     NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULTemplateQueryProcessor)
   114 NS_INTERFACE_MAP_END
   116 nsXULTemplateQueryProcessorRDF::nsXULTemplateQueryProcessorRDF(void)
   117     : mDB(nullptr),
   118       mBuilder(nullptr),
   119       mQueryProcessorRDFInited(false),
   120       mGenerationStarted(false),
   121       mUpdateBatchNest(0),
   122       mSimpleRuleMemberTest(nullptr)
   123 {
   124     gRefCnt++;
   125 }
   127 nsXULTemplateQueryProcessorRDF::~nsXULTemplateQueryProcessorRDF(void)
   128 {
   129     if (--gRefCnt == 0) {
   130         NS_IF_RELEASE(gRDFService);
   131         NS_IF_RELEASE(gRDFContainerUtils);
   132         NS_IF_RELEASE(kNC_BookmarkSeparator);
   133         NS_IF_RELEASE(kRDF_type);
   134     }
   135 }
   137 nsresult
   138 nsXULTemplateQueryProcessorRDF::InitGlobals()
   139 {
   140     nsresult rv;
   142     // Initialize the global shared reference to the service
   143     // manager and get some shared resource objects.
   144     if (!gRDFService) {
   145         NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
   146         rv = CallGetService(kRDFServiceCID, &gRDFService);
   147         if (NS_FAILED(rv))
   148             return rv;
   149     }
   151     if (!gRDFContainerUtils) {
   152         NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
   153         rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils);
   154         if (NS_FAILED(rv))
   155             return rv;
   156     }
   158     if (!kNC_BookmarkSeparator) {
   159         gRDFService->GetResource(
   160           NS_LITERAL_CSTRING(NC_NAMESPACE_URI "BookmarkSeparator"),
   161                              &kNC_BookmarkSeparator);
   162     }
   164     if (!kRDF_type) {
   165         gRDFService->GetResource(
   166           NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "type"),
   167                              &kRDF_type);
   168     }
   170     return NS_OK;
   171 }
   173 //----------------------------------------------------------------------
   174 //
   175 // nsIXULTemplateQueryProcessor interface
   176 //
   178 NS_IMETHODIMP
   179 nsXULTemplateQueryProcessorRDF::GetDatasource(nsIArray* aDataSources,
   180                                               nsIDOMNode* aRootNode,
   181                                               bool aIsTrusted,
   182                                               nsIXULTemplateBuilder* aBuilder,
   183                                               bool* aShouldDelayBuilding,
   184                                               nsISupports** aResult)
   185 {
   186     nsCOMPtr<nsIRDFCompositeDataSource> compDB;
   187     nsCOMPtr<nsIContent> root = do_QueryInterface(aRootNode);
   188     nsresult rv;
   190     *aResult = nullptr;
   191     *aShouldDelayBuilding = false;
   193     NS_ENSURE_TRUE(root, NS_ERROR_UNEXPECTED);
   195     // make sure the RDF service is set up
   196     rv = InitGlobals();
   197     NS_ENSURE_SUCCESS(rv, rv);
   199     // create a database for the builder
   200     compDB = do_CreateInstance(NS_RDF_DATASOURCE_CONTRACTID_PREFIX 
   201                                "composite-datasource");
   202     if (!compDB) {
   203         NS_ERROR("unable to construct new composite data source");
   204         return NS_ERROR_UNEXPECTED;
   205     }
   207     // check for magical attributes. XXX move to ``flags''?
   208     if (root->AttrValueIs(kNameSpaceID_None,
   209                           nsGkAtoms::coalesceduplicatearcs,
   210                           nsGkAtoms::_false, eCaseMatters))
   211         compDB->SetCoalesceDuplicateArcs(false);
   213     if (root->AttrValueIs(kNameSpaceID_None,
   214                           nsGkAtoms::allownegativeassertions,
   215                           nsGkAtoms::_false, eCaseMatters))
   216         compDB->SetAllowNegativeAssertions(false);
   218     if (aIsTrusted) {
   219         // If we're a privileged (e.g., chrome) document, then add the
   220         // local store as the first data source in the db. Note that
   221         // we _might_ not be able to get a local store if we haven't
   222         // got a profile to read from yet.
   223         nsCOMPtr<nsIRDFDataSource> localstore;
   224         rv = gRDFService->GetDataSource("rdf:local-store",
   225                                         getter_AddRefs(localstore));
   226         NS_ENSURE_SUCCESS(rv, rv);
   228         rv = compDB->AddDataSource(localstore);
   229         NS_ASSERTION(NS_SUCCEEDED(rv), "unable to add local store to db");
   230         NS_ENSURE_SUCCESS(rv, rv);
   231     }
   233     uint32_t length, index;
   234     rv = aDataSources->GetLength(&length);
   235     NS_ENSURE_SUCCESS(rv,rv);
   237     for (index = 0; index < length; index++) {
   239         nsCOMPtr<nsIURI> uri = do_QueryElementAt(aDataSources, index);
   240         if (!uri) // we ignore other datasources than uri
   241             continue;
   243         nsCOMPtr<nsIRDFDataSource> ds;
   244         nsAutoCString uristrC;
   245         uri->GetSpec(uristrC);
   247         rv = gRDFService->GetDataSource(uristrC.get(), getter_AddRefs(ds));
   249         if (NS_FAILED(rv)) {
   250             // This is only a warning because the data source may not
   251             // be accessible for any number of reasons, including
   252             // security, a bad URL, etc.
   253   #ifdef DEBUG
   254             nsAutoCString msg;
   255             msg.Append("unable to load datasource '");
   256             msg.Append(uristrC);
   257             msg.Append('\'');
   258             NS_WARNING(msg.get());
   259   #endif
   260             continue;
   261         }
   263         compDB->AddDataSource(ds);
   264     }
   267     // check if we were given an inference engine type
   268     nsAutoString infer;
   269     nsCOMPtr<nsIRDFDataSource> db;
   270     root->GetAttr(kNameSpaceID_None, nsGkAtoms::infer, infer);
   271     if (!infer.IsEmpty()) {
   272         nsCString inferCID(NS_RDF_INFER_DATASOURCE_CONTRACTID_PREFIX);
   273         AppendUTF16toUTF8(infer, inferCID);
   274         nsCOMPtr<nsIRDFInferDataSource> inferDB =
   275             do_CreateInstance(inferCID.get());
   277         if (inferDB) {
   278             inferDB->SetBaseDataSource(compDB);
   279             db = do_QueryInterface(inferDB);
   280         }
   281         else {
   282             NS_WARNING("failed to construct inference engine specified on template");
   283         }
   284     }
   286     if (!db)
   287         db = compDB;
   289     return CallQueryInterface(db, aResult);
   290 }
   292 NS_IMETHODIMP
   293 nsXULTemplateQueryProcessorRDF::InitializeForBuilding(nsISupports* aDatasource,
   294                                                       nsIXULTemplateBuilder* aBuilder,
   295                                                       nsIDOMNode* aRootNode)
   296 {
   297     if (!mQueryProcessorRDFInited) {
   298         nsresult rv = InitGlobals();
   299         if (NS_FAILED(rv))
   300             return rv;
   302         mQueryProcessorRDFInited = true;
   303     }
   305     // don't do anything if generation has already been done
   306     if (mGenerationStarted)
   307         return NS_ERROR_UNEXPECTED;
   309     mDB = do_QueryInterface(aDatasource);
   310     mBuilder = aBuilder;
   312     ComputeContainmentProperties(aRootNode);
   314     // Add ourselves as a datasource observer
   315     if (mDB)
   316         mDB->AddObserver(this);
   318     return NS_OK;
   319 }
   321 NS_IMETHODIMP
   322 nsXULTemplateQueryProcessorRDF::Done()
   323 {
   324     if (!mQueryProcessorRDFInited)
   325         return NS_OK;
   327     if (mDB)
   328         mDB->RemoveObserver(this);
   330     mDB = nullptr;
   331     mBuilder = nullptr;
   332     mRefVariable = nullptr;
   333     mLastRef = nullptr;
   335     mGenerationStarted = false;
   336     mUpdateBatchNest = 0;
   338     mContainmentProperties.Clear();
   340     for (ReteNodeSet::Iterator node = mAllTests.First();
   341          node != mAllTests.Last(); ++node)
   342         delete *node;
   344     mAllTests.Clear();
   345     mRDFTests.Clear();
   346     mQueries.Clear();
   348     mSimpleRuleMemberTest = nullptr;
   350     mBindingDependencies.Clear();
   352     mMemoryElementToResultMap.Clear();
   354     mRuleToBindingsMap.Clear();
   356     return NS_OK;
   357 }
   359 NS_IMETHODIMP
   360 nsXULTemplateQueryProcessorRDF::CompileQuery(nsIXULTemplateBuilder* aBuilder,
   361                                              nsIDOMNode* aQueryNode,
   362                                              nsIAtom* aRefVariable,
   363                                              nsIAtom* aMemberVariable,
   364                                              nsISupports** _retval)
   365 {
   366     nsRefPtr<nsRDFQuery> query = new nsRDFQuery(this);
   367     if (!query)
   368         return NS_ERROR_OUT_OF_MEMORY;
   370     query->mRefVariable = aRefVariable;
   371     if (!mRefVariable)
   372       mRefVariable = aRefVariable;
   374     if (!aMemberVariable)
   375         query->mMemberVariable = do_GetAtom("?");
   376     else
   377         query->mMemberVariable = aMemberVariable;
   379     nsresult rv;
   380     TestNode *lastnode = nullptr;
   382     nsCOMPtr<nsIContent> content = do_QueryInterface(aQueryNode);
   384     if (content->NodeInfo()->Equals(nsGkAtoms::_template, kNameSpaceID_XUL)) {
   385         // simplified syntax with no rules
   387         query->SetSimple();
   388         NS_ASSERTION(!mSimpleRuleMemberTest,
   389                      "CompileQuery called twice with the same template");
   390         if (!mSimpleRuleMemberTest)
   391             rv = CompileSimpleQuery(query, content, &lastnode);
   392         else
   393             rv = NS_ERROR_FAILURE;
   394     }
   395     else if (content->NodeInfo()->Equals(nsGkAtoms::rule, kNameSpaceID_XUL)) {
   396         // simplified syntax with at least one rule
   397         query->SetSimple();
   398         rv = CompileSimpleQuery(query, content, &lastnode);
   399     }
   400     else {
   401         rv = CompileExtendedQuery(query, content, &lastnode);
   402     }
   404     if (NS_FAILED(rv))
   405         return rv;
   407     query->SetQueryNode(aQueryNode);
   409     nsInstantiationNode* instnode = new nsInstantiationNode(this, query);
   410     if (!instnode)
   411         return NS_ERROR_OUT_OF_MEMORY;
   413     // this and other functions always add nodes to mAllTests first. That
   414     // way if something fails, the node will just sit harmlessly in mAllTests
   415     // where it can be deleted later. 
   416     rv = mAllTests.Add(instnode);
   417     if (NS_FAILED(rv)) {
   418         delete instnode;
   419         return rv;
   420     }
   422     rv = lastnode->AddChild(instnode);
   423     if (NS_FAILED(rv))
   424         return rv;
   426     mQueries.AppendElement(query);
   428     *_retval = query;
   429     NS_ADDREF(*_retval);
   431     return NS_OK;
   432 }
   434 NS_IMETHODIMP
   435 nsXULTemplateQueryProcessorRDF::GenerateResults(nsISupports* aDatasource,
   436                                                 nsIXULTemplateResult* aRef,
   437                                                 nsISupports* aQuery,
   438                                                 nsISimpleEnumerator** aResults)
   439 {
   440     nsCOMPtr<nsITemplateRDFQuery> rdfquery = do_QueryInterface(aQuery);
   441     if (! rdfquery)
   442         return NS_ERROR_INVALID_ARG;
   444     mGenerationStarted = true;
   446     // should be safe to cast here since the query is a
   447     // non-scriptable nsITemplateRDFQuery
   448     nsRDFQuery* query = static_cast<nsRDFQuery *>(aQuery);
   450     *aResults = nullptr;
   452     nsCOMPtr<nsISimpleEnumerator> results;
   454     if (aRef) {
   455         // make sure that cached results were generated for this ref, and if not,
   456         // regenerate them. Otherwise, things will go wrong for templates bound to
   457         // an HTML element as they are not generated lazily.
   458         if (aRef == mLastRef) {
   459             query->UseCachedResults(getter_AddRefs(results));
   460         }
   461         else {
   462             // clear the cached results
   463             int32_t count = mQueries.Length();
   464             for (int32_t r = 0; r < count; r++) {
   465                 mQueries[r]->ClearCachedResults();
   466             }
   467         }
   469         if (! results) {
   470             if (! query->mRefVariable)
   471                 query->mRefVariable = do_GetAtom("?uri");
   473             nsCOMPtr<nsIRDFResource> refResource;
   474             aRef->GetResource(getter_AddRefs(refResource));
   475             if (! refResource)
   476                 return NS_ERROR_FAILURE;
   478             // Propagate the assignments through the network
   479             TestNode* root = query->GetRoot();
   481             if (query->IsSimple() && mSimpleRuleMemberTest) {
   482                 // get the top node in the simple rule tree
   483                 root = mSimpleRuleMemberTest->GetParent();
   484                 mLastRef = aRef;
   485             }
   487 #ifdef PR_LOGGING
   488             if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
   489                 nsAutoString id;
   490                 aRef->GetId(id);
   492                 nsAutoString rvar;
   493                 query->mRefVariable->ToString(rvar);
   494                 nsAutoString mvar;
   495                 query->mMemberVariable->ToString(mvar);
   497                 PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
   498                        ("QueryProcessor::GenerateResults using ref %s and vars [ ref: %s  member: %s]",
   499                        NS_ConvertUTF16toUTF8(id).get(),
   500                        NS_ConvertUTF16toUTF8(rvar).get(),
   501                        NS_ConvertUTF16toUTF8(mvar).get()));
   502             }
   503 #endif
   505             if (root) {
   506                 // the seed is the initial instantiation, which has a single
   507                 // assignment holding the reference point
   508                 Instantiation seed;
   509                 seed.AddAssignment(query->mRefVariable, refResource);
   511                 InstantiationSet* instantiations = new InstantiationSet();
   512                 if (!instantiations)
   513                     return NS_ERROR_OUT_OF_MEMORY;
   514                 instantiations->Append(seed);
   516                 // if the propagation caused a match, then the results will be
   517                 // cached in the query, retrieved below by calling
   518                 // UseCachedResults. The matching result set owns the
   519                 // instantiations and will delete them when results have been
   520                 // iterated over. If the propagation did not match, the
   521                 // instantiations need to be deleted.
   522                 bool owned = false;
   523                 nsresult rv = root->Propagate(*instantiations, false, owned);
   524                 if (! owned)
   525                     delete instantiations;
   526                 if (NS_FAILED(rv))
   527                     return rv;
   529                 query->UseCachedResults(getter_AddRefs(results));
   530             }
   531         }
   532     }
   534     if (! results) {
   535         // no results were found so create an empty set
   536         results = new nsXULTemplateResultSetRDF(this, query, nullptr);
   537         if (! results)
   538             return NS_ERROR_OUT_OF_MEMORY;
   539     }
   541     results.swap(*aResults);
   543     return NS_OK;
   544 }
   546 NS_IMETHODIMP
   547 nsXULTemplateQueryProcessorRDF::AddBinding(nsIDOMNode* aRuleNode,
   548                                            nsIAtom* aVar,
   549                                            nsIAtom* aRef,
   550                                            const nsAString& aExpr)
   551 {
   552     // add a <binding> to a rule. When a result is matched, the bindings are
   553     // examined to add additional variable assignments
   555     // bindings can't be added once result generation has started, otherwise
   556     // the array sizes will get out of sync
   557     if (mGenerationStarted)
   558         return NS_ERROR_UNEXPECTED;
   560     nsCOMPtr<nsIRDFResource> property;
   561     nsresult rv = gRDFService->GetUnicodeResource(aExpr, getter_AddRefs(property));
   562     if (NS_FAILED(rv))
   563         return rv;
   565     nsRefPtr<RDFBindingSet> bindings = mRuleToBindingsMap.GetWeak(aRuleNode);
   566     if (!bindings) {
   567         bindings = new RDFBindingSet();
   568         mRuleToBindingsMap.Put(aRuleNode, bindings);
   569     }
   571     return bindings->AddBinding(aVar, aRef, property);
   572 }
   574 NS_IMETHODIMP
   575 nsXULTemplateQueryProcessorRDF::TranslateRef(nsISupports* aDatasource,
   576                                                            const nsAString& aRefString,
   577                                                            nsIXULTemplateResult** aRef)
   578 {
   579     // make sure the RDF service is set up
   580     nsresult rv = InitGlobals();
   581     if (NS_FAILED(rv))
   582         return rv;
   584     nsCOMPtr<nsIRDFResource> uri;
   585     gRDFService->GetUnicodeResource(aRefString, getter_AddRefs(uri));
   587     nsXULTemplateResultRDF* refresult = new nsXULTemplateResultRDF(uri);
   588     if (! refresult)
   589         return NS_ERROR_OUT_OF_MEMORY;
   591     *aRef = refresult;
   592     NS_ADDREF(*aRef);
   594     return NS_OK;
   595 }
   597 NS_IMETHODIMP
   598 nsXULTemplateQueryProcessorRDF::CompareResults(nsIXULTemplateResult* aLeft,
   599                                                nsIXULTemplateResult* aRight,
   600                                                nsIAtom* aVar,
   601                                                uint32_t aSortHints,
   602                                                int32_t* aResult)
   603 {
   604     NS_ENSURE_ARG_POINTER(aLeft);
   605     NS_ENSURE_ARG_POINTER(aRight);
   607     *aResult = 0;
   609     // for natural order sorting, use the index in the RDF container for the
   610     // order. If there is no container, just sort them arbitrarily.
   611     if (!aVar) {
   612         // if a result has a negative index, just sort it first
   613         int32_t leftindex = GetContainerIndexOf(aLeft);
   614         int32_t rightindex = GetContainerIndexOf(aRight);
   615         *aResult = leftindex == rightindex ? 0 :
   616                    leftindex > rightindex ? 1 :
   617                    -1;
   618         return NS_OK;
   619     }
   621     nsDependentAtomString sortkey(aVar);
   623     nsCOMPtr<nsISupports> leftNode, rightNode;
   625     if (!sortkey.IsEmpty() && sortkey[0] != '?' &&
   626         !StringBeginsWith(sortkey, NS_LITERAL_STRING("rdf:")) &&
   627         mDB) {
   628         // if the sort key is not a template variable, it should be an RDF
   629         // predicate. Get the targets and compare those instead.
   630         nsCOMPtr<nsIRDFResource> predicate;
   631         nsresult rv = gRDFService->GetUnicodeResource(sortkey, getter_AddRefs(predicate));
   632         NS_ENSURE_SUCCESS(rv, rv);
   634         // create a predicate with '?sort=true' appended. This special
   635         // predicate may be used to have a different sort value than the
   636         // displayed value
   637         sortkey.AppendLiteral("?sort=true");
   639         nsCOMPtr<nsIRDFResource> sortPredicate;
   640         rv = gRDFService->GetUnicodeResource(sortkey, getter_AddRefs(sortPredicate));
   641         NS_ENSURE_SUCCESS(rv, rv);
   643         rv = GetSortValue(aLeft, predicate, sortPredicate, getter_AddRefs(leftNode));
   644         NS_ENSURE_SUCCESS(rv, rv);
   646         rv = GetSortValue(aRight, predicate, sortPredicate, getter_AddRefs(rightNode));
   647         NS_ENSURE_SUCCESS(rv, rv);
   648     }
   649     else {
   650         // get the values for the sort key from the results
   651         aLeft->GetBindingObjectFor(aVar, getter_AddRefs(leftNode));
   652         aRight->GetBindingObjectFor(aVar, getter_AddRefs(rightNode));
   653     }
   655     {
   656         // Literals?
   657         nsCOMPtr<nsIRDFLiteral> l = do_QueryInterface(leftNode);
   658         if (l) {
   659             nsCOMPtr<nsIRDFLiteral> r = do_QueryInterface(rightNode);
   660             if (r) {
   661                 const char16_t *lstr, *rstr;
   662                 l->GetValueConst(&lstr);
   663                 r->GetValueConst(&rstr);
   665                 *aResult = XULSortServiceImpl::CompareValues(
   666                                nsDependentString(lstr),
   667                                nsDependentString(rstr), aSortHints);
   668             }
   670             return NS_OK;
   671         }
   672     }
   674     {
   675         // Dates?
   676         nsCOMPtr<nsIRDFDate> l = do_QueryInterface(leftNode);
   677         if (l) {
   678             nsCOMPtr<nsIRDFDate> r = do_QueryInterface(rightNode);
   679             if (r) {
   680                 PRTime ldate, rdate;
   681                 l->GetValue(&ldate);
   682                 r->GetValue(&rdate);
   684                 int64_t delta = ldate - rdate;
   685                 if (delta == 0)
   686                     *aResult = 0;
   687                 else if (delta >= 0)
   688                     *aResult = 1;
   689                 else
   690                     *aResult = -1;
   691             }
   693             return NS_OK;
   694         }
   695     }
   697     {
   698         // Integers?
   699         nsCOMPtr<nsIRDFInt> l = do_QueryInterface(leftNode);
   700         if (l) {
   701             nsCOMPtr<nsIRDFInt> r = do_QueryInterface(rightNode);
   702             if (r) {
   703                 int32_t lval, rval;
   704                 l->GetValue(&lval);
   705                 r->GetValue(&rval);
   707                 *aResult = lval - rval;
   708             }
   710             return NS_OK;
   711         }
   712     }
   714     nsICollation* collation = nsXULContentUtils::GetCollation();
   715     if (collation) {
   716         // Blobs? (We can only compare these reasonably if we have a
   717         // collation object.)
   718         nsCOMPtr<nsIRDFBlob> l = do_QueryInterface(leftNode);
   719         if (l) {
   720             nsCOMPtr<nsIRDFBlob> r = do_QueryInterface(rightNode);
   721             if (r) {
   722                 const uint8_t *lval, *rval;
   723                 int32_t llen, rlen;
   724                 l->GetValue(&lval);
   725                 l->GetLength(&llen);
   726                 r->GetValue(&rval);
   727                 r->GetLength(&rlen);
   729                 collation->CompareRawSortKey(lval, llen, rval, rlen, aResult);
   730             }
   731         }
   732     }
   734     // if the results are none of the above, just pretend that they are equal
   735     return NS_OK;
   736 }
   738 //----------------------------------------------------------------------
   739 //
   740 // nsIRDFObserver interface
   741 //
   744 NS_IMETHODIMP
   745 nsXULTemplateQueryProcessorRDF::OnAssert(nsIRDFDataSource* aDataSource,
   746                                          nsIRDFResource* aSource,
   747                                          nsIRDFResource* aProperty,
   748                                          nsIRDFNode* aTarget)
   749 {
   750     // Ignore updates if we're batching
   751     if (mUpdateBatchNest)
   752         return(NS_OK);
   754     if (! mBuilder)
   755         return NS_OK;
   757     LOG("onassert", aSource, aProperty, aTarget);
   759     Propagate(aSource, aProperty, aTarget);
   760     SynchronizeAll(aSource, aProperty, nullptr, aTarget);
   761     return NS_OK;
   762 }
   766 NS_IMETHODIMP
   767 nsXULTemplateQueryProcessorRDF::OnUnassert(nsIRDFDataSource* aDataSource,
   768                                            nsIRDFResource* aSource,
   769                                            nsIRDFResource* aProperty,
   770                                            nsIRDFNode* aTarget)
   771 {
   772     // Ignore updates if we're batching
   773     if (mUpdateBatchNest)
   774         return NS_OK;
   776     if (! mBuilder)
   777         return NS_OK;
   779     LOG("onunassert", aSource, aProperty, aTarget);
   781     Retract(aSource, aProperty, aTarget);
   782     SynchronizeAll(aSource, aProperty, aTarget, nullptr);
   783     return NS_OK;
   784 }
   787 NS_IMETHODIMP
   788 nsXULTemplateQueryProcessorRDF::OnChange(nsIRDFDataSource* aDataSource,
   789                                          nsIRDFResource* aSource,
   790                                          nsIRDFResource* aProperty,
   791                                          nsIRDFNode* aOldTarget,
   792                                          nsIRDFNode* aNewTarget)
   793 {
   794     // Ignore updates if we're batching
   795     if (mUpdateBatchNest)
   796         return NS_OK;
   798     if (! mBuilder)
   799         return NS_OK;
   801     LOG("onchange", aSource, aProperty, aNewTarget);
   803     if (aOldTarget) {
   804         // Pull any old results that were relying on aOldTarget
   805         Retract(aSource, aProperty, aOldTarget);
   806     }
   808     if (aNewTarget) {
   809         // Fire any new results that are activated by aNewTarget
   810         Propagate(aSource, aProperty, aNewTarget);
   811     }
   813     // Synchronize any of the content model that may have changed.
   814     SynchronizeAll(aSource, aProperty, aOldTarget, aNewTarget);
   815     return NS_OK;
   816 }
   819 NS_IMETHODIMP
   820 nsXULTemplateQueryProcessorRDF::OnMove(nsIRDFDataSource* aDataSource,
   821                                        nsIRDFResource* aOldSource,
   822                                        nsIRDFResource* aNewSource,
   823                                        nsIRDFResource* aProperty,
   824                                        nsIRDFNode* aTarget)
   825 {
   826     // Ignore updates if we're batching
   827     if (mUpdateBatchNest)
   828         return NS_OK;
   830     NS_NOTYETIMPLEMENTED("write me");
   831     return NS_ERROR_NOT_IMPLEMENTED;
   832 }
   835 NS_IMETHODIMP
   836 nsXULTemplateQueryProcessorRDF::OnBeginUpdateBatch(nsIRDFDataSource* aDataSource)
   837 {
   838     mUpdateBatchNest++;
   839     return NS_OK;
   840 }
   843 NS_IMETHODIMP
   844 nsXULTemplateQueryProcessorRDF::OnEndUpdateBatch(nsIRDFDataSource* aDataSource)
   845 {
   846     NS_ASSERTION(mUpdateBatchNest > 0, "badly nested update batch");
   847     if (--mUpdateBatchNest <= 0) {
   848         mUpdateBatchNest = 0;
   850         if (mBuilder)
   851             mBuilder->Rebuild();
   852     }
   854     return NS_OK;
   855 }
   857 nsresult
   858 nsXULTemplateQueryProcessorRDF::Propagate(nsIRDFResource* aSource,
   859                                           nsIRDFResource* aProperty,
   860                                           nsIRDFNode* aTarget)
   861 {
   862     // When a new assertion is added to the graph, determine any new matches
   863     // that must be added to the template builder. First, iterate through all
   864     // the RDF tests (<member> and <triple> tests), and find the topmost test
   865     // that would be affected by the new assertion.
   866     nsresult rv;
   868     ReteNodeSet livenodes;
   870 #ifdef PR_LOGGING
   871     if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
   872         const char* sourceStr;
   873         aSource->GetValueConst(&sourceStr);
   874         const char* propertyStr;
   875         aProperty->GetValueConst(&propertyStr);
   876         nsAutoString targetStr;
   877         nsXULContentUtils::GetTextForNode(aTarget, targetStr);
   879         PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
   880                ("nsXULTemplateQueryProcessorRDF::Propagate: [%s] -> [%s] -> [%s]\n",
   881                sourceStr, propertyStr, NS_ConvertUTF16toUTF8(targetStr).get()));
   882     }
   883 #endif
   885     {
   886         ReteNodeSet::Iterator last = mRDFTests.Last();
   887         for (ReteNodeSet::Iterator i = mRDFTests.First(); i != last; ++i) {
   888             nsRDFTestNode* rdftestnode = static_cast<nsRDFTestNode*>(*i);
   890             Instantiation seed;
   891             if (rdftestnode->CanPropagate(aSource, aProperty, aTarget, seed)) {
   892                 rv = livenodes.Add(rdftestnode);
   893                 if (NS_FAILED(rv))
   894                     return rv;
   895             }
   896         }
   897     }
   899     // Now, we'll go through each, and any that aren't dominated by
   900     // another live node will be used to propagate the assertion
   901     // through the rule network
   902     {
   903         ReteNodeSet::Iterator last = livenodes.Last();
   904         for (ReteNodeSet::Iterator i = livenodes.First(); i != last; ++i) {
   905             nsRDFTestNode* rdftestnode = static_cast<nsRDFTestNode*>(*i);
   907             // What happens here is we create an instantiation as if we were
   908             // at the found test in the rule network. For example, if the
   909             // found test was a member test (parent => child), the parent
   910             // and child variables are assigned the values provided by the new
   911             // RDF assertion in the graph. The Constrain call is used to go
   912             // up to earlier RDF tests, filling in variables as it goes.
   913             // Constrain will eventually get up to the top node, an
   914             // nsContentTestNode, which takes the value of the reference
   915             // variable and calls the template builder to see if a result has
   916             // been generated already for the reference value. If it hasn't,
   917             // the new assertion couldn't cause a new match. If the result
   918             // exists, call Propagate to continue to the later RDF tests to
   919             // fill in the rest of the variable assignments.
   921             // Bogus, to get the seed instantiation
   922             Instantiation seed;
   923             rdftestnode->CanPropagate(aSource, aProperty, aTarget, seed);
   925             InstantiationSet* instantiations = new InstantiationSet();
   926             if (!instantiations)
   927                 return NS_ERROR_OUT_OF_MEMORY;
   928             instantiations->Append(seed);
   930             rv = rdftestnode->Constrain(*instantiations);
   931             if (NS_FAILED(rv)) {
   932                 delete instantiations;
   933                 return rv;
   934             }
   936             bool owned = false;
   937             if (!instantiations->Empty())
   938                 rv = rdftestnode->Propagate(*instantiations, true, owned);
   940             // owned should always be false in update mode, but check just
   941             // to be sure
   942             if (!owned)
   943                 delete instantiations;
   944             if (NS_FAILED(rv))
   945                 return rv;
   946         }
   947     }
   949     return NS_OK;
   950 }
   953 nsresult
   954 nsXULTemplateQueryProcessorRDF::Retract(nsIRDFResource* aSource,
   955                                         nsIRDFResource* aProperty,
   956                                         nsIRDFNode* aTarget)
   957 {
   959 #ifdef PR_LOGGING
   960     if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
   961         const char* sourceStr;
   962         aSource->GetValueConst(&sourceStr);
   963         const char* propertyStr;
   964         aProperty->GetValueConst(&propertyStr);
   965         nsAutoString targetStr;
   966         nsXULContentUtils::GetTextForNode(aTarget, targetStr);
   968         PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
   969                ("nsXULTemplateQueryProcessorRDF::Retract: [%s] -> [%s] -> [%s]\n",
   970                sourceStr, propertyStr, NS_ConvertUTF16toUTF8(targetStr).get()));
   971     }
   972 #endif
   974     // Retract any currently active rules that will no longer be matched.
   975     ReteNodeSet::ConstIterator lastnode = mRDFTests.Last();
   976     for (ReteNodeSet::ConstIterator node = mRDFTests.First(); node != lastnode; ++node) {
   977         const nsRDFTestNode* rdftestnode = static_cast<const nsRDFTestNode*>(*node);
   979         rdftestnode->Retract(aSource, aProperty, aTarget);
   981         // Now fire any newly revealed rules
   982         // XXXwaterson yo. write me.
   983         // The intent here is to handle any rules that might be
   984         // "revealed" by the removal of an assertion from the datasource.
   985         // Waterson doesn't think we support negated conditions in a rule.
   986         // Nor is he sure that this is currently useful.
   987     }
   989     return NS_OK;
   990 }
   992 nsresult
   993 nsXULTemplateQueryProcessorRDF::SynchronizeAll(nsIRDFResource* aSource,
   994                                                nsIRDFResource* aProperty,
   995                                                nsIRDFNode* aOldTarget,
   996                                                nsIRDFNode* aNewTarget)
   997 {
   998     // Update each match that contains <aSource, aProperty, aOldTarget>.
  1000     // Get all the matches whose assignments are currently supported
  1001     // by aSource and aProperty: we'll need to recompute them.
  1002     ResultArray* results;
  1003     if (!mBindingDependencies.Get(aSource, &results) || !mBuilder)
  1004         return NS_OK;
  1006     uint32_t length = results->Length();
  1008     for (uint32_t r = 0; r < length; r++) {
  1009         nsXULTemplateResultRDF* result = (*results)[r];
  1010         if (result) {
  1011             // synchronize the result's bindings and then update the builder
  1012             // so that content can be updated
  1013             if (result->SyncAssignments(aSource, aProperty, aNewTarget)) {
  1014                 nsITemplateRDFQuery* query = result->Query();
  1015                 if (query) {
  1016                     nsCOMPtr<nsIDOMNode> querynode;
  1017                     query->GetQueryNode(getter_AddRefs(querynode));
  1019                     mBuilder->ResultBindingChanged(result);
  1025     return NS_OK;
  1028 #ifdef PR_LOGGING
  1029 nsresult
  1030 nsXULTemplateQueryProcessorRDF::Log(const char* aOperation,
  1031                                     nsIRDFResource* aSource,
  1032                                     nsIRDFResource* aProperty,
  1033                                     nsIRDFNode* aTarget)
  1035     if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
  1036         nsresult rv;
  1038         const char* sourceStr;
  1039         rv = aSource->GetValueConst(&sourceStr);
  1040         if (NS_FAILED(rv))
  1041             return rv;
  1043         PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
  1044                ("xultemplate[%p] %8s [%s]--", this, aOperation, sourceStr));
  1046         const char* propertyStr;
  1047         rv = aProperty->GetValueConst(&propertyStr);
  1048         if (NS_FAILED(rv))
  1049             return rv;
  1051         nsAutoString targetStr;
  1052         rv = nsXULContentUtils::GetTextForNode(aTarget, targetStr);
  1053         if (NS_FAILED(rv))
  1054             return rv;
  1056         nsAutoCString targetstrC;
  1057         targetstrC.AssignWithConversion(targetStr);
  1058         PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
  1059                ("                        --[%s]-->[%s]",
  1060                 propertyStr,
  1061                 targetstrC.get()));
  1063     return NS_OK;
  1065 #endif
  1067 nsresult
  1068 nsXULTemplateQueryProcessorRDF::CheckContainer(nsIRDFResource* aResource,
  1069                                                bool* aIsContainer)
  1071     NS_ENSURE_ARG_POINTER(aIsContainer);
  1072     NS_ENSURE_STATE(mDB);
  1074     // We have to look at all of the arcs extending out of the
  1075     // resource: if any of them are that "containment" property, then
  1076     // we know we'll have children.
  1077     bool isContainer = false;
  1079     for (nsResourceSet::ConstIterator property = mContainmentProperties.First();
  1080          property != mContainmentProperties.Last();
  1081          property++) {
  1082         bool hasArc = false;
  1083         mDB->HasArcOut(aResource, *property, &hasArc);
  1085         if (hasArc) {
  1086             // Well, it's a container...
  1087             isContainer = true;
  1088             break;
  1092     // If we get here, and we're still not sure if it's a container,
  1093     // then see if it's an RDF container
  1094     if (! isContainer) {
  1095         gRDFContainerUtils->IsContainer(mDB, aResource, &isContainer);
  1098     *aIsContainer = isContainer;
  1100     return NS_OK;
  1103 nsresult
  1104 nsXULTemplateQueryProcessorRDF::CheckEmpty(nsIRDFResource* aResource,
  1105                                            bool* aIsEmpty)
  1107     NS_ENSURE_STATE(mDB);
  1108     *aIsEmpty = true;
  1110     for (nsResourceSet::ConstIterator property = mContainmentProperties.First();
  1111          property != mContainmentProperties.Last();
  1112          property++) {
  1114         nsCOMPtr<nsIRDFNode> dummy;
  1115         mDB->GetTarget(aResource, *property, true, getter_AddRefs(dummy));
  1117         if (dummy) {
  1118             *aIsEmpty = false;
  1119             break;
  1123     if (*aIsEmpty){
  1124         return nsXULTemplateQueryProcessorRDF::gRDFContainerUtils->
  1125                    IsEmpty(mDB, aResource, aIsEmpty);
  1128     return NS_OK;
  1131 nsresult
  1132 nsXULTemplateQueryProcessorRDF::CheckIsSeparator(nsIRDFResource* aResource,
  1133                                                  bool* aIsSeparator)
  1135     NS_ENSURE_STATE(mDB);
  1136     return mDB->HasAssertion(aResource, kRDF_type, kNC_BookmarkSeparator,
  1137                              true, aIsSeparator);
  1140 //----------------------------------------------------------------------
  1142 nsresult
  1143 nsXULTemplateQueryProcessorRDF::ComputeContainmentProperties(nsIDOMNode* aRootNode)
  1145     // The 'containment' attribute on the root node is a
  1146     // whitespace-separated list that tells us which properties we
  1147     // should use to test for containment.
  1148     nsresult rv;
  1150     mContainmentProperties.Clear();
  1152     nsCOMPtr<nsIContent> content = do_QueryInterface(aRootNode);
  1154     nsAutoString containment;
  1155     content->GetAttr(kNameSpaceID_None, nsGkAtoms::containment, containment);
  1157     uint32_t len = containment.Length();
  1158     uint32_t offset = 0;
  1159     while (offset < len) {
  1160         while (offset < len && nsCRT::IsAsciiSpace(containment[offset]))
  1161             ++offset;
  1163         if (offset >= len)
  1164             break;
  1166         uint32_t end = offset;
  1167         while (end < len && !nsCRT::IsAsciiSpace(containment[end]))
  1168             ++end;
  1170         nsAutoString propertyStr;
  1171         containment.Mid(propertyStr, offset, end - offset);
  1173         nsCOMPtr<nsIRDFResource> property;
  1174         rv = gRDFService->GetUnicodeResource(propertyStr, getter_AddRefs(property));
  1175         if (NS_FAILED(rv))
  1176             return rv;
  1178         rv = mContainmentProperties.Add(property);
  1179         if (NS_FAILED(rv))
  1180             return rv;
  1182         offset = end;
  1185 #define TREE_PROPERTY_HACK 1
  1186 #if defined(TREE_PROPERTY_HACK)
  1187     if (! len) {
  1188         // Some ever-present membership tests.
  1189         mContainmentProperties.Add(nsXULContentUtils::NC_child);
  1190         mContainmentProperties.Add(nsXULContentUtils::NC_Folder);
  1192 #endif
  1194     return NS_OK;
  1197 nsresult
  1198 nsXULTemplateQueryProcessorRDF::CompileExtendedQuery(nsRDFQuery* aQuery,
  1199                                                      nsIContent* aConditions,
  1200                                                      TestNode** aLastNode)
  1202     // Compile an extended query's children
  1204     nsContentTestNode* idnode =
  1205         new nsContentTestNode(this, aQuery->mRefVariable);
  1206     if (! idnode)
  1207         return NS_ERROR_OUT_OF_MEMORY;
  1209     aQuery->SetRoot(idnode);
  1210     nsresult rv = mAllTests.Add(idnode);
  1211     if (NS_FAILED(rv)) {
  1212         delete idnode;
  1213         return rv;
  1216     TestNode* prevnode = idnode;
  1218     for (nsIContent* condition = aConditions->GetFirstChild();
  1219          condition;
  1220          condition = condition->GetNextSibling()) {
  1222         // the <content> condition should always be the first child
  1223         if (condition->Tag() == nsGkAtoms::content) {
  1224             if (condition != aConditions->GetFirstChild()) {
  1225                 nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_CONTENT_NOT_FIRST);
  1226                 continue;
  1229             // check for <content tag='tag'/> which indicates that matches
  1230             // should only be generated for items inside content with that tag
  1231             nsAutoString tagstr;
  1232             condition->GetAttr(kNameSpaceID_None, nsGkAtoms::tag, tagstr);
  1234             nsCOMPtr<nsIAtom> tag;
  1235             if (! tagstr.IsEmpty()) {
  1236                 tag = do_GetAtom(tagstr);
  1239             nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(condition->GetDocument());
  1240             if (! doc)
  1241                 return NS_ERROR_FAILURE;
  1243             idnode->SetTag(tag, doc);
  1244             continue;
  1247         TestNode* testnode = nullptr;
  1248         nsresult rv = CompileQueryChild(condition->Tag(), aQuery, condition,
  1249                                         prevnode, &testnode);
  1250         if (NS_FAILED(rv))
  1251             return rv;
  1253         if (testnode) {
  1254             rv = prevnode->AddChild(testnode);
  1255             if (NS_FAILED(rv))
  1256                 return rv;
  1258             prevnode = testnode;
  1262     *aLastNode = prevnode;
  1264     return NS_OK;
  1267 nsresult
  1268 nsXULTemplateQueryProcessorRDF::CompileQueryChild(nsIAtom* aTag,
  1269                                                   nsRDFQuery* aQuery,
  1270                                                   nsIContent* aCondition,
  1271                                                   TestNode* aParentNode,
  1272                                                   TestNode** aResult)
  1274     nsresult rv;
  1276     if (aTag == nsGkAtoms::triple) {
  1277         rv = CompileTripleCondition(aQuery, aCondition, aParentNode, aResult);
  1279     else if (aTag == nsGkAtoms::member) {
  1280         rv = CompileMemberCondition(aQuery, aCondition, aParentNode, aResult);
  1282     else {
  1283 #ifdef PR_LOGGING
  1284         nsAutoString tagstr;
  1285         aTag->ToString(tagstr);
  1287         nsAutoCString tagstrC;
  1288         tagstrC.AssignWithConversion(tagstr);
  1289         PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
  1290                ("xultemplate[%p] unrecognized condition test <%s>",
  1291                 this, tagstrC.get()));
  1292 #endif
  1294         rv = NS_OK;
  1297     return rv;
  1300 nsresult
  1301 nsXULTemplateQueryProcessorRDF::ParseLiteral(const nsString& aParseType, 
  1302                                              const nsString& aValue,
  1303                                              nsIRDFNode** aResult)
  1305     nsresult rv = NS_OK;
  1306     *aResult = nullptr;
  1308     if (aParseType.EqualsLiteral(PARSE_TYPE_INTEGER)) {
  1309         nsCOMPtr<nsIRDFInt> intLiteral;
  1310         nsresult errorCode;
  1311         int32_t intValue = aValue.ToInteger(&errorCode);
  1312         if (NS_FAILED(errorCode))
  1313             return NS_ERROR_FAILURE;
  1314         rv = gRDFService->GetIntLiteral(intValue, getter_AddRefs(intLiteral));
  1315         if (NS_FAILED(rv)) 
  1316             return rv;
  1317         rv = CallQueryInterface(intLiteral, aResult);
  1319     else {
  1320         nsCOMPtr<nsIRDFLiteral> literal;
  1321         rv = gRDFService->GetLiteral(aValue.get(), getter_AddRefs(literal));
  1322         if (NS_FAILED(rv)) 
  1323             return rv;
  1324         rv = CallQueryInterface(literal, aResult);
  1326     return rv;
  1329 nsresult
  1330 nsXULTemplateQueryProcessorRDF::CompileTripleCondition(nsRDFQuery* aQuery,
  1331                                                        nsIContent* aCondition,
  1332                                                        TestNode* aParentNode,
  1333                                                        TestNode** aResult)
  1335     // Compile a <triple> condition, which must be of the form:
  1336     //
  1337     //   <triple subject="?var1|resource"
  1338     //           predicate="resource"
  1339     //           object="?var2|resource|literal" />
  1340     //
  1341     // XXXwaterson Some day it would be cool to allow the 'predicate'
  1342     // to be bound to a variable.
  1344     // subject
  1345     nsAutoString subject;
  1346     aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::subject, subject);
  1348     nsCOMPtr<nsIAtom> svar;
  1349     nsCOMPtr<nsIRDFResource> sres;
  1350     if (subject.IsEmpty()) {
  1351         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_BAD_SUBJECT);
  1352         return NS_OK;
  1354     if (subject[0] == char16_t('?'))
  1355         svar = do_GetAtom(subject);
  1356     else
  1357         gRDFService->GetUnicodeResource(subject, getter_AddRefs(sres));
  1359     // predicate
  1360     nsAutoString predicate;
  1361     aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::predicate, predicate);
  1363     nsCOMPtr<nsIRDFResource> pres;
  1364     if (predicate.IsEmpty() || predicate[0] == char16_t('?')) {
  1365         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_BAD_PREDICATE);
  1366         return NS_OK;
  1368     gRDFService->GetUnicodeResource(predicate, getter_AddRefs(pres));
  1370     // object
  1371     nsAutoString object;
  1372     aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::object, object);
  1374     nsCOMPtr<nsIAtom> ovar;
  1375     nsCOMPtr<nsIRDFNode> onode;
  1376     if (object.IsEmpty()) {
  1377         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_BAD_OBJECT);
  1378         return NS_OK;
  1381     if (object[0] == char16_t('?')) {
  1382         ovar = do_GetAtom(object);
  1384     else if (object.FindChar(':') != -1) { // XXXwaterson evil.
  1385         // treat as resource
  1386         nsCOMPtr<nsIRDFResource> resource;
  1387         gRDFService->GetUnicodeResource(object, getter_AddRefs(resource));
  1388         onode = do_QueryInterface(resource);
  1390     else {
  1391         nsAutoString parseType;
  1392         aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::parsetype, parseType);
  1393         nsresult rv = ParseLiteral(parseType, object, getter_AddRefs(onode));
  1394         if (NS_FAILED(rv))
  1395             return rv;
  1398     nsRDFPropertyTestNode* testnode = nullptr;
  1400     if (svar && ovar) {
  1401         testnode = new nsRDFPropertyTestNode(aParentNode, this, svar, pres, ovar);
  1403     else if (svar) {
  1404         testnode = new nsRDFPropertyTestNode(aParentNode, this, svar, pres, onode);
  1406     else if (ovar) {
  1407         testnode = new nsRDFPropertyTestNode(aParentNode, this, sres, pres, ovar);
  1409     else {
  1410         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_NO_VAR);
  1411         return NS_OK;
  1414     if (! testnode)
  1415         return NS_ERROR_OUT_OF_MEMORY;
  1417     // add testnode to mAllTests first. If adding to mRDFTests fails, just
  1418     // leave it in the list so that it can be deleted later.
  1419     nsresult rv = mAllTests.Add(testnode);
  1420     if (NS_FAILED(rv)) {
  1421         delete testnode;
  1422         return rv;
  1425     rv = mRDFTests.Add(testnode);
  1426     if (NS_FAILED(rv))
  1427         return rv;
  1429     *aResult = testnode;
  1430     return NS_OK;
  1433 nsresult
  1434 nsXULTemplateQueryProcessorRDF::CompileMemberCondition(nsRDFQuery* aQuery,
  1435                                                        nsIContent* aCondition,
  1436                                                        TestNode* aParentNode,
  1437                                                        TestNode** aResult)
  1439     // Compile a <member> condition, which must be of the form:
  1440     //
  1441     //   <member container="?var1" child="?var2" />
  1442     //
  1444     // container
  1445     nsAutoString container;
  1446     aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::container, container);
  1448     if (!container.IsEmpty() && container[0] != char16_t('?')) {
  1449         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_MEMBER_NOCONTAINERVAR);
  1450         return NS_OK;
  1453     nsCOMPtr<nsIAtom> containervar = do_GetAtom(container);
  1455     // child
  1456     nsAutoString child;
  1457     aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::child, child);
  1459     if (!child.IsEmpty() && child[0] != char16_t('?')) {
  1460         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_MEMBER_NOCHILDVAR);
  1461         return NS_OK;
  1464     nsCOMPtr<nsIAtom> childvar = do_GetAtom(child);
  1466     TestNode* testnode =
  1467         new nsRDFConMemberTestNode(aParentNode,
  1468                                    this,
  1469                                    containervar,
  1470                                    childvar);
  1472     if (! testnode)
  1473         return NS_ERROR_OUT_OF_MEMORY;
  1475     // add testnode to mAllTests first. If adding to mRDFTests fails, just
  1476     // leave it in the list so that it can be deleted later.
  1477     nsresult rv = mAllTests.Add(testnode);
  1478     if (NS_FAILED(rv)) {
  1479         delete testnode;
  1480         return rv;
  1483     rv = mRDFTests.Add(testnode);
  1484     if (NS_FAILED(rv))
  1485         return rv;
  1487     *aResult = testnode;
  1488     return NS_OK;
  1491 nsresult
  1492 nsXULTemplateQueryProcessorRDF::AddDefaultSimpleRules(nsRDFQuery* aQuery,
  1493                                                       TestNode** aChildNode)
  1495     // XXXndeakin should check for tag in query processor instead of builder?
  1496     nsContentTestNode* idnode =
  1497         new nsContentTestNode(this,
  1498                               aQuery->mRefVariable);
  1499     if (! idnode)
  1500         return NS_ERROR_OUT_OF_MEMORY;
  1502     // Create (?container ^member ?member)
  1503     nsRDFConMemberTestNode* membernode =
  1504         new nsRDFConMemberTestNode(idnode,
  1505                                    this,
  1506                                    aQuery->mRefVariable,
  1507                                    aQuery->mMemberVariable);
  1509     if (! membernode) {
  1510         delete idnode;
  1511         return NS_ERROR_OUT_OF_MEMORY;
  1514     // add nodes to mAllTests first. If later calls fail, just leave them in
  1515     // the list so that they can be deleted later.
  1516     nsresult rv = mAllTests.Add(idnode);
  1517     if (NS_FAILED(rv)) {
  1518         delete idnode;
  1519         delete membernode;
  1520         return rv;
  1523     rv = mAllTests.Add(membernode);
  1524     if (NS_FAILED(rv)) {
  1525         delete membernode;
  1526         return rv;
  1529     rv = mRDFTests.Add(membernode);
  1530     if (NS_FAILED(rv))
  1531         return rv;
  1533     rv = idnode->AddChild(membernode);
  1534     if (NS_FAILED(rv))
  1535         return rv;
  1537     mSimpleRuleMemberTest = membernode;
  1538     *aChildNode = membernode;
  1540     return NS_OK;
  1543 nsresult
  1544 nsXULTemplateQueryProcessorRDF::CompileSimpleQuery(nsRDFQuery* aQuery,
  1545                                                    nsIContent* aQueryElement,
  1546                                                    TestNode** aLastNode)
  1548     // Compile a "simple" (or old-school style) <template> query.
  1549     nsresult rv;
  1551     TestNode* parentNode;
  1553     if (! mSimpleRuleMemberTest) {
  1554         rv = AddDefaultSimpleRules(aQuery, &parentNode);
  1555         if (NS_FAILED(rv))
  1556             return rv;
  1559     bool hasContainerTest = false;
  1561     TestNode* prevnode = mSimpleRuleMemberTest;
  1563     // Add constraints for the LHS
  1564     const nsAttrName* name;
  1565     for (uint32_t i = 0; (name = aQueryElement->GetAttrNameAt(i)); ++i) {
  1566         // Note: some attributes must be skipped on XUL template query subtree
  1568         // never compare against rdf:property, rdf:instanceOf, {}:id or {}:parsetype attribute
  1569         if (name->Equals(nsGkAtoms::property, kNameSpaceID_RDF) ||
  1570             name->Equals(nsGkAtoms::instanceOf, kNameSpaceID_RDF) ||
  1571             name->Equals(nsGkAtoms::id, kNameSpaceID_None) ||
  1572             name->Equals(nsGkAtoms::parsetype, kNameSpaceID_None)) {
  1573             continue;
  1576         int32_t attrNameSpaceID = name->NamespaceID();
  1577         if (attrNameSpaceID == kNameSpaceID_XMLNS)
  1578           continue;
  1579         nsIAtom* attr = name->LocalName();
  1581         nsAutoString value;
  1582         aQueryElement->GetAttr(attrNameSpaceID, attr, value);
  1584         TestNode* testnode = nullptr;
  1586         if (name->Equals(nsGkAtoms::iscontainer, kNameSpaceID_None) ||
  1587             name->Equals(nsGkAtoms::isempty, kNameSpaceID_None)) {
  1588             // Tests about containerhood and emptiness. These can be
  1589             // globbed together, mostly. Check to see if we've already
  1590             // added a container test: we only need one.
  1591             if (hasContainerTest)
  1592                 continue;
  1594             nsRDFConInstanceTestNode::Test iscontainer =
  1595                 nsRDFConInstanceTestNode::eDontCare;
  1597             static nsIContent::AttrValuesArray strings[] =
  1598               {&nsGkAtoms::_true, &nsGkAtoms::_false, nullptr};
  1599             switch (aQueryElement->FindAttrValueIn(kNameSpaceID_None,
  1600                                                    nsGkAtoms::iscontainer,
  1601                                                    strings, eCaseMatters)) {
  1602                 case 0: iscontainer = nsRDFConInstanceTestNode::eTrue; break;
  1603                 case 1: iscontainer = nsRDFConInstanceTestNode::eFalse; break;
  1606             nsRDFConInstanceTestNode::Test isempty =
  1607                 nsRDFConInstanceTestNode::eDontCare;
  1609             switch (aQueryElement->FindAttrValueIn(kNameSpaceID_None,
  1610                                                    nsGkAtoms::isempty,
  1611                                                    strings, eCaseMatters)) {
  1612                 case 0: isempty = nsRDFConInstanceTestNode::eTrue; break;
  1613                 case 1: isempty = nsRDFConInstanceTestNode::eFalse; break;
  1616             testnode = new nsRDFConInstanceTestNode(prevnode,
  1617                                                     this,
  1618                                                     aQuery->mMemberVariable,
  1619                                                     iscontainer,
  1620                                                     isempty);
  1622             if (! testnode)
  1623                 return NS_ERROR_OUT_OF_MEMORY;
  1625             rv = mAllTests.Add(testnode);
  1626             if (NS_FAILED(rv)) {
  1627                 delete testnode;
  1628                 return rv;
  1631             rv = mRDFTests.Add(testnode);
  1632             if (NS_FAILED(rv))
  1633                 return rv;
  1635         else if (attrNameSpaceID != kNameSpaceID_None || attr != nsGkAtoms::parent) {
  1636             // It's a simple RDF test
  1637             nsCOMPtr<nsIRDFResource> property;
  1638             rv = nsXULContentUtils::GetResource(attrNameSpaceID, attr, getter_AddRefs(property));
  1639             if (NS_FAILED(rv))
  1640                 return rv;
  1642             // XXXwaterson this is so manky
  1643             nsCOMPtr<nsIRDFNode> target;
  1644             if (value.FindChar(':') != -1) { // XXXwaterson WRONG WRONG WRONG!
  1645                 nsCOMPtr<nsIRDFResource> resource;
  1646                 rv = gRDFService->GetUnicodeResource(value, getter_AddRefs(resource));
  1647                 if (NS_FAILED(rv))
  1648                     return rv;
  1650                 target = do_QueryInterface(resource);
  1652             else {                
  1653               nsAutoString parseType;
  1654               aQueryElement->GetAttr(kNameSpaceID_None, nsGkAtoms::parsetype, parseType);
  1655               rv = ParseLiteral(parseType, value, getter_AddRefs(target));
  1656               if (NS_FAILED(rv))
  1657                   return rv;
  1660             testnode = new nsRDFPropertyTestNode(prevnode, this,
  1661                                                  aQuery->mMemberVariable, property, target);
  1662             if (! testnode)
  1663                 return NS_ERROR_OUT_OF_MEMORY;
  1665             rv = mAllTests.Add(testnode);
  1666             if (NS_FAILED(rv)) {
  1667                 delete testnode;
  1668                 return rv;
  1671             rv = mRDFTests.Add(testnode);
  1672             if (NS_FAILED(rv))
  1673                 return rv;
  1676         if (testnode) {
  1677             if (prevnode) {
  1678                 rv = prevnode->AddChild(testnode);
  1679                 if (NS_FAILED(rv))
  1680                     return rv;
  1682             else {
  1683                 aQuery->SetRoot(testnode);
  1686             prevnode = testnode;
  1690     *aLastNode = prevnode;
  1692     return NS_OK;
  1695 RDFBindingSet*
  1696 nsXULTemplateQueryProcessorRDF::GetBindingsForRule(nsIDOMNode* aRuleNode)
  1698     return mRuleToBindingsMap.GetWeak(aRuleNode);
  1701 void
  1702 nsXULTemplateQueryProcessorRDF::AddBindingDependency(nsXULTemplateResultRDF* aResult,
  1703                                                      nsIRDFResource* aResource)
  1705     ResultArray* arr;
  1706     if (!mBindingDependencies.Get(aResource, &arr)) {
  1707         arr = new ResultArray();
  1709         mBindingDependencies.Put(aResource, arr);
  1712     int32_t index = arr->IndexOf(aResult);
  1713     if (index == -1)
  1714         arr->AppendElement(aResult);
  1717 void
  1718 nsXULTemplateQueryProcessorRDF::RemoveBindingDependency(nsXULTemplateResultRDF* aResult,
  1719                                                         nsIRDFResource* aResource)
  1721     ResultArray* arr;
  1722     if (mBindingDependencies.Get(aResource, &arr)) {
  1723         int32_t index = arr->IndexOf(aResult);
  1724         if (index >= 0)
  1725             arr->RemoveElementAt(index);
  1730 nsresult
  1731 nsXULTemplateQueryProcessorRDF::AddMemoryElements(const Instantiation& aInst,
  1732                                                   nsXULTemplateResultRDF* aResult)
  1734     // Add the result to a table indexed by supporting MemoryElement
  1735     MemoryElementSet::ConstIterator last = aInst.mSupport.Last();
  1736     for (MemoryElementSet::ConstIterator element = aInst.mSupport.First();
  1737                                          element != last; ++element) {
  1739         PLHashNumber hash = (element.operator->())->Hash();
  1741         nsCOMArray<nsXULTemplateResultRDF>* arr;
  1742         if (!mMemoryElementToResultMap.Get(hash, &arr)) {
  1743             arr = new nsCOMArray<nsXULTemplateResultRDF>();
  1744             if (!arr)
  1745                 return NS_ERROR_OUT_OF_MEMORY;
  1747             mMemoryElementToResultMap.Put(hash, arr);
  1750         // results may be added more than once so they will all get deleted properly
  1751         arr->AppendObject(aResult);
  1754     return NS_OK;
  1757 nsresult
  1758 nsXULTemplateQueryProcessorRDF::RemoveMemoryElements(const Instantiation& aInst,
  1759                                                      nsXULTemplateResultRDF* aResult)
  1761     // Remove the results mapped by the supporting MemoryElement
  1762     MemoryElementSet::ConstIterator last = aInst.mSupport.Last();
  1763     for (MemoryElementSet::ConstIterator element = aInst.mSupport.First();
  1764                                          element != last; ++element) {
  1766         PLHashNumber hash = (element.operator->())->Hash();
  1768         nsCOMArray<nsXULTemplateResultRDF>* arr;
  1769         if (mMemoryElementToResultMap.Get(hash, &arr)) {
  1770             int32_t index = arr->IndexOf(aResult);
  1771             if (index >= 0)
  1772                 arr->RemoveObjectAt(index);
  1774             uint32_t length = arr->Count();
  1775             if (! length)
  1776                 mMemoryElementToResultMap.Remove(hash);
  1780     return NS_OK;
  1783 void
  1784 nsXULTemplateQueryProcessorRDF::RetractElement(const MemoryElement& aMemoryElement)
  1786     if (! mBuilder)
  1787         return;
  1789     // when an assertion is removed, look through the memory elements and
  1790     // find results that are associated with them. Those results will need
  1791     // to be removed because they no longer match.
  1792     PLHashNumber hash = aMemoryElement.Hash();
  1794     nsCOMArray<nsXULTemplateResultRDF>* arr;
  1795     if (mMemoryElementToResultMap.Get(hash, &arr)) {
  1796         uint32_t length = arr->Count();
  1798         for (int32_t r = length - 1; r >= 0; r--) {
  1799             nsXULTemplateResultRDF* result = (*arr)[r];
  1800             if (result) {
  1801                 // because the memory elements are hashed by an integer,
  1802                 // sometimes two different memory elements will have the same
  1803                 // hash code. In this case we check the result to make sure
  1804                 // and only remove those that refer to that memory element.
  1805                 if (result->HasMemoryElement(aMemoryElement)) {
  1806                     nsITemplateRDFQuery* query = result->Query();
  1807                     if (query) {
  1808                         nsCOMPtr<nsIDOMNode> querynode;
  1809                         query->GetQueryNode(getter_AddRefs(querynode));
  1811                         mBuilder->RemoveResult(result);
  1814                     // a call to RemoveMemoryElements may have removed it
  1815                     if (!mMemoryElementToResultMap.Get(hash, nullptr))
  1816                         return;
  1818                     // the array should have been reduced by one, but check
  1819                     // just to make sure
  1820                     uint32_t newlength = arr->Count();
  1821                     if (r > (int32_t)newlength)
  1822                         r = newlength;
  1827         // if there are no items left, remove the memory element from the hashtable
  1828         if (!arr->Count())
  1829             mMemoryElementToResultMap.Remove(hash);
  1833 int32_t
  1834 nsXULTemplateQueryProcessorRDF::GetContainerIndexOf(nsIXULTemplateResult* aResult)
  1836     // get the reference variable and look up the container in the result
  1837     nsCOMPtr<nsISupports> ref;
  1838     nsresult rv = aResult->GetBindingObjectFor(mRefVariable,
  1839                                                getter_AddRefs(ref));
  1840     if (NS_FAILED(rv) || !mDB)
  1841         return -1;
  1843     nsCOMPtr<nsIRDFResource> container = do_QueryInterface(ref);
  1844     if (container) {
  1845         // if the container is an RDF Seq, return the index of the result
  1846         // in the container.
  1847         bool isSequence = false;
  1848         gRDFContainerUtils->IsSeq(mDB, container, &isSequence);
  1849         if (isSequence) {
  1850             nsCOMPtr<nsIRDFResource> resource;
  1851             aResult->GetResource(getter_AddRefs(resource));
  1852             if (resource) {
  1853                 int32_t index;
  1854                 gRDFContainerUtils->IndexOf(mDB, container, resource, &index);
  1855                 return index;
  1860     // if the container isn't a Seq, or the result isn't in the container,
  1861     // return -1 indicating no index.
  1862     return -1;
  1865 nsresult
  1866 nsXULTemplateQueryProcessorRDF::GetSortValue(nsIXULTemplateResult* aResult,
  1867                                              nsIRDFResource* aPredicate,
  1868                                              nsIRDFResource* aSortPredicate,
  1869                                              nsISupports** aResultNode)
  1871     nsCOMPtr<nsIRDFResource> source;
  1872     nsresult rv = aResult->GetResource(getter_AddRefs(source));
  1873     if (NS_FAILED(rv))
  1874         return rv;
  1876     nsCOMPtr<nsIRDFNode> value;
  1877     if (source && mDB) {
  1878         // first check predicate?sort=true so that datasources may use a
  1879         // custom value for sorting
  1880         rv = mDB->GetTarget(source, aSortPredicate, true,
  1881                             getter_AddRefs(value));
  1882         if (NS_FAILED(rv))
  1883             return rv;
  1885         if (!value) {
  1886             rv = mDB->GetTarget(source, aPredicate, true,
  1887                                 getter_AddRefs(value));
  1888             if (NS_FAILED(rv))
  1889                 return rv;
  1893     *aResultNode = value;
  1894     NS_IF_ADDREF(*aResultNode);
  1895     return NS_OK;

mercurial