content/xul/templates/src/nsXULTemplateBuilder.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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 /*
     8   Builds content from a datasource using the XUL <template> tag.
    10   TO DO
    12   . Fix ContentTagTest's location in the network construction
    14   To turn on logging for this module, set:
    16     NSPR_LOG_MODULES nsXULTemplateBuilder:5
    18  */
    20 #include "nsCOMPtr.h"
    21 #include "nsCRT.h"
    22 #include "nsIContent.h"
    23 #include "nsIDOMElement.h"
    24 #include "nsIDOMNode.h"
    25 #include "nsIDOMDocument.h"
    26 #include "nsIDOMXMLDocument.h"
    27 #include "nsIDOMXULElement.h"
    28 #include "nsIDocument.h"
    29 #include "nsBindingManager.h"
    30 #include "nsIDOMNodeList.h"
    31 #include "nsIObserverService.h"
    32 #include "nsIRDFCompositeDataSource.h"
    33 #include "nsIRDFInferDataSource.h"
    34 #include "nsIRDFContainerUtils.h"
    35 #include "nsIXULDocument.h"
    36 #include "nsIXULTemplateBuilder.h"
    37 #include "nsIXULBuilderListener.h"
    38 #include "nsIRDFRemoteDataSource.h"
    39 #include "nsIRDFService.h"
    40 #include "nsIScriptContext.h"
    41 #include "nsIScriptGlobalObject.h"
    42 #include "nsIServiceManager.h"
    43 #include "nsISimpleEnumerator.h"
    44 #include "nsIMutableArray.h"
    45 #include "nsIURL.h"
    46 #include "nsIXPConnect.h"
    47 #include "nsContentCID.h"
    48 #include "nsNameSpaceManager.h"
    49 #include "nsRDFCID.h"
    50 #include "nsXULContentUtils.h"
    51 #include "nsString.h"
    52 #include "nsTArray.h"
    53 #include "nsXPIDLString.h"
    54 #include "nsWhitespaceTokenizer.h"
    55 #include "nsGkAtoms.h"
    56 #include "nsXULElement.h"
    57 #include "jsapi.h"
    58 #include "prlog.h"
    59 #include "rdf.h"
    60 #include "pldhash.h"
    61 #include "plhash.h"
    62 #include "nsDOMClassInfoID.h"
    63 #include "nsPIDOMWindow.h"
    64 #include "nsIConsoleService.h" 
    65 #include "nsNetUtil.h"
    66 #include "nsXULTemplateBuilder.h"
    67 #include "nsXULTemplateQueryProcessorRDF.h"
    68 #include "nsXULTemplateQueryProcessorXML.h"
    69 #include "nsXULTemplateQueryProcessorStorage.h"
    70 #include "nsContentUtils.h"
    71 #include "ChildIterator.h"
    72 #include "mozilla/dom/ScriptSettings.h"
    74 using namespace mozilla::dom;
    75 using namespace mozilla;
    77 //----------------------------------------------------------------------
    78 //
    79 // nsXULTemplateBuilder
    80 //
    82 nsrefcnt                  nsXULTemplateBuilder::gRefCnt = 0;
    83 nsIRDFService*            nsXULTemplateBuilder::gRDFService;
    84 nsIRDFContainerUtils*     nsXULTemplateBuilder::gRDFContainerUtils;
    85 nsIScriptSecurityManager* nsXULTemplateBuilder::gScriptSecurityManager;
    86 nsIPrincipal*             nsXULTemplateBuilder::gSystemPrincipal;
    87 nsIObserverService*       nsXULTemplateBuilder::gObserverService;
    89 #ifdef PR_LOGGING
    90 PRLogModuleInfo* gXULTemplateLog;
    91 #endif
    93 #define NS_QUERY_PROCESSOR_CONTRACTID_PREFIX "@mozilla.org/xul/xul-query-processor;1?name="
    95 //----------------------------------------------------------------------
    96 //
    97 // nsXULTemplateBuilder methods
    98 //
   100 nsXULTemplateBuilder::nsXULTemplateBuilder(void)
   101     : mQueriesCompiled(false),
   102       mFlags(0),
   103       mTop(nullptr),
   104       mObservedDocument(nullptr)
   105 {
   106     MOZ_COUNT_CTOR(nsXULTemplateBuilder);
   107 }
   109 static PLDHashOperator
   110 DestroyMatchList(nsISupports* aKey, nsTemplateMatch*& aMatch, void* aContext)
   111 {
   112     // delete all the matches in the list
   113     while (aMatch) {
   114         nsTemplateMatch* next = aMatch->mNext;
   115         nsTemplateMatch::Destroy(aMatch, true);
   116         aMatch = next;
   117     }
   119     return PL_DHASH_REMOVE;
   120 }
   122 nsXULTemplateBuilder::~nsXULTemplateBuilder(void)
   123 {
   124     Uninit(true);
   126     if (--gRefCnt == 0) {
   127         NS_IF_RELEASE(gRDFService);
   128         NS_IF_RELEASE(gRDFContainerUtils);
   129         NS_IF_RELEASE(gSystemPrincipal);
   130         NS_IF_RELEASE(gScriptSecurityManager);
   131         NS_IF_RELEASE(gObserverService);
   132     }
   134     MOZ_COUNT_DTOR(nsXULTemplateBuilder);
   135 }
   138 nsresult
   139 nsXULTemplateBuilder::InitGlobals()
   140 {
   141     nsresult rv;
   143     if (gRefCnt++ == 0) {
   144         // Initialize the global shared reference to the service
   145         // manager and get some shared resource objects.
   146         NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
   147         rv = CallGetService(kRDFServiceCID, &gRDFService);
   148         if (NS_FAILED(rv))
   149             return rv;
   151         NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
   152         rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils);
   153         if (NS_FAILED(rv))
   154             return rv;
   156         rv = CallGetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID,
   157                             &gScriptSecurityManager);
   158         if (NS_FAILED(rv))
   159             return rv;
   161         rv = gScriptSecurityManager->GetSystemPrincipal(&gSystemPrincipal);
   162         if (NS_FAILED(rv))
   163             return rv;
   165         rv = CallGetService(NS_OBSERVERSERVICE_CONTRACTID, &gObserverService);
   166         if (NS_FAILED(rv))
   167             return rv;
   168     }
   170 #ifdef PR_LOGGING
   171     if (! gXULTemplateLog)
   172         gXULTemplateLog = PR_NewLogModule("nsXULTemplateBuilder");
   173 #endif
   175     return NS_OK;
   176 }
   178 void
   179 nsXULTemplateBuilder::StartObserving(nsIDocument* aDocument)
   180 {
   181     aDocument->AddObserver(this);
   182     mObservedDocument = aDocument;
   183     gObserverService->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC, false);
   184 }
   186 void
   187 nsXULTemplateBuilder::StopObserving()
   188 {
   189     MOZ_ASSERT(mObservedDocument);
   190     mObservedDocument->RemoveObserver(this);
   191     mObservedDocument = nullptr;
   192     gObserverService->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
   193 }
   195 void
   196 nsXULTemplateBuilder::CleanUp(bool aIsFinal)
   197 {
   198     for (int32_t q = mQuerySets.Length() - 1; q >= 0; q--) {
   199         nsTemplateQuerySet* qs = mQuerySets[q];
   200         delete qs;
   201     }
   203     mQuerySets.Clear();
   205     mMatchMap.Enumerate(DestroyMatchList, nullptr);
   207     // Setting mQueryProcessor to null will close connections. This would be
   208     // handled by the cycle collector, but we want to close them earlier.
   209     if (aIsFinal)
   210         mQueryProcessor = nullptr;
   211 }
   213 void
   214 nsXULTemplateBuilder::Uninit(bool aIsFinal)
   215 {
   216     if (mObservedDocument && aIsFinal) {
   217         StopObserving();
   218     }
   220     if (mQueryProcessor)
   221         mQueryProcessor->Done();
   223     CleanUp(aIsFinal);
   225     mRootResult = nullptr;
   226     mRefVariable = nullptr;
   227     mMemberVariable = nullptr;
   229     mQueriesCompiled = false;
   230 }
   232 static PLDHashOperator
   233 TraverseMatchList(nsISupports* aKey, nsTemplateMatch* aMatch, void* aContext)
   234 {
   235     nsCycleCollectionTraversalCallback *cb =
   236         static_cast<nsCycleCollectionTraversalCallback*>(aContext);
   238     cb->NoteXPCOMChild(aKey);
   239     nsTemplateMatch* match = aMatch;
   240     while (match) {
   241         cb->NoteXPCOMChild(match->GetContainer());
   242         cb->NoteXPCOMChild(match->mResult);
   243         match = match->mNext;
   244     }
   246     return PL_DHASH_NEXT;
   247 }
   249 NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULTemplateBuilder)
   251 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULTemplateBuilder)
   252     NS_IMPL_CYCLE_COLLECTION_UNLINK(mDataSource)
   253     NS_IMPL_CYCLE_COLLECTION_UNLINK(mDB)
   254     NS_IMPL_CYCLE_COLLECTION_UNLINK(mCompDB)
   255     NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot)
   256     NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootResult)
   257     NS_IMPL_CYCLE_COLLECTION_UNLINK(mListeners)
   258     NS_IMPL_CYCLE_COLLECTION_UNLINK(mQueryProcessor)
   259     tmp->mMatchMap.Enumerate(DestroyMatchList, nullptr);
   260     for (uint32_t i = 0; i < tmp->mQuerySets.Length(); ++i) {
   261         nsTemplateQuerySet* qs = tmp->mQuerySets[i];
   262         delete qs;
   263     }
   264     tmp->mQuerySets.Clear();
   265 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   266 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULTemplateBuilder)
   267     if (tmp->mObservedDocument && !cb.WantAllTraces()) {
   268         // The global observer service holds us alive.
   269         return NS_SUCCESS_INTERRUPTED_TRAVERSE;
   270     }
   272     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDataSource)
   273     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDB)
   274     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCompDB)
   275     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
   276     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootResult)
   277     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListeners)
   278     NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueryProcessor)
   279     tmp->mMatchMap.EnumerateRead(TraverseMatchList, &cb);
   280     {
   281       uint32_t i, count = tmp->mQuerySets.Length();
   282       for (i = 0; i < count; ++i) {
   283         nsTemplateQuerySet *set = tmp->mQuerySets[i];
   284         cb.NoteXPCOMChild(set->mQueryNode);
   285         cb.NoteXPCOMChild(set->mCompiledQuery);
   286         uint16_t j, rulesCount = set->RuleCount();
   287         for (j = 0; j < rulesCount; ++j) {
   288           set->GetRuleAt(j)->Traverse(cb);
   289         }
   290       }
   291     }
   292     tmp->Traverse(cb);
   293 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   295 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULTemplateBuilder)
   296 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULTemplateBuilder)
   298 DOMCI_DATA(XULTemplateBuilder, nsXULTemplateBuilder)
   300 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULTemplateBuilder)
   301   NS_INTERFACE_MAP_ENTRY(nsIXULTemplateBuilder)
   302   NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
   303   NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
   304   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   305   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULTemplateBuilder)
   306   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XULTemplateBuilder)
   307 NS_INTERFACE_MAP_END
   309 //----------------------------------------------------------------------
   310 //
   311 // nsIXULTemplateBuilder methods
   312 //
   314 NS_IMETHODIMP
   315 nsXULTemplateBuilder::GetRoot(nsIDOMElement** aResult)
   316 {
   317     if (mRoot) {
   318         return CallQueryInterface(mRoot, aResult);
   319     }
   320     *aResult = nullptr;
   321     return NS_OK;
   322 }
   324 NS_IMETHODIMP
   325 nsXULTemplateBuilder::GetDatasource(nsISupports** aResult)
   326 {
   327     if (mCompDB)
   328         NS_ADDREF(*aResult = mCompDB);
   329     else
   330         NS_IF_ADDREF(*aResult = mDataSource);
   331     return NS_OK;
   332 }
   334 NS_IMETHODIMP
   335 nsXULTemplateBuilder::SetDatasource(nsISupports* aResult)
   336 {
   337     mDataSource = aResult;
   338     mCompDB = do_QueryInterface(mDataSource);
   340     return Rebuild();
   341 }
   343 NS_IMETHODIMP
   344 nsXULTemplateBuilder::GetDatabase(nsIRDFCompositeDataSource** aResult)
   345 {
   346     NS_IF_ADDREF(*aResult = mCompDB);
   347     return NS_OK;
   348 }
   350 NS_IMETHODIMP
   351 nsXULTemplateBuilder::GetQueryProcessor(nsIXULTemplateQueryProcessor** aResult)
   352 {
   353     NS_IF_ADDREF(*aResult = mQueryProcessor.get());
   354     return NS_OK;
   355 }
   357 NS_IMETHODIMP
   358 nsXULTemplateBuilder::AddRuleFilter(nsIDOMNode* aRule, nsIXULTemplateRuleFilter* aFilter)
   359 {
   360     if (!aRule || !aFilter)
   361         return NS_ERROR_NULL_POINTER;
   363     // a custom rule filter may be added, one for each rule. If a new one is
   364     // added, it replaces the old one. Look for the right rule and set its
   365     // filter
   367     int32_t count = mQuerySets.Length();
   368     for (int32_t q = 0; q < count; q++) {
   369         nsTemplateQuerySet* queryset = mQuerySets[q];
   371         int16_t rulecount = queryset->RuleCount();
   372         for (int16_t r = 0; r < rulecount; r++) {
   373             nsTemplateRule* rule = queryset->GetRuleAt(r);
   375             nsCOMPtr<nsIDOMNode> rulenode;
   376             rule->GetRuleNode(getter_AddRefs(rulenode));
   377             if (aRule == rulenode) {
   378                 rule->SetRuleFilter(aFilter);
   379                 return NS_OK;
   380             }
   381         }
   382     }
   384     return NS_OK;
   385 }
   387 NS_IMETHODIMP
   388 nsXULTemplateBuilder::Rebuild()
   389 {
   390     int32_t i;
   392     for (i = mListeners.Count() - 1; i >= 0; --i) {
   393         mListeners[i]->WillRebuild(this);
   394     }
   396     nsresult rv = RebuildAll();
   398     for (i = mListeners.Count() - 1; i >= 0; --i) {
   399         mListeners[i]->DidRebuild(this);
   400     }
   402     return rv;
   403 }
   405 NS_IMETHODIMP
   406 nsXULTemplateBuilder::Refresh()
   407 {
   408     nsresult rv;
   410     if (!mCompDB)
   411         return NS_ERROR_FAILURE;
   413     nsCOMPtr<nsISimpleEnumerator> dslist;
   414     rv = mCompDB->GetDataSources(getter_AddRefs(dslist));
   415     NS_ENSURE_SUCCESS(rv, rv);
   417     bool hasMore;
   418     nsCOMPtr<nsISupports> next;
   419     nsCOMPtr<nsIRDFRemoteDataSource> rds;
   421     while(NS_SUCCEEDED(dslist->HasMoreElements(&hasMore)) && hasMore) {
   422         dslist->GetNext(getter_AddRefs(next));
   423         if (next && (rds = do_QueryInterface(next))) {
   424             rds->Refresh(false);
   425         }
   426     }
   428     // XXXbsmedberg: it would be kinda nice to install an async nsIRDFXMLSink
   429     // observer and call rebuild() once the load is complete. See bug 254600.
   431     return NS_OK;
   432 }
   434 NS_IMETHODIMP
   435 nsXULTemplateBuilder::Init(nsIContent* aElement)
   436 {
   437     NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
   438     mRoot = aElement;
   440     nsCOMPtr<nsIDocument> doc = mRoot->GetDocument();
   441     NS_ASSERTION(doc, "element has no document");
   442     if (! doc)
   443         return NS_ERROR_UNEXPECTED;
   445     bool shouldDelay;
   446     nsresult rv = LoadDataSources(doc, &shouldDelay);
   448     if (NS_SUCCEEDED(rv)) {
   449         StartObserving(doc);
   450     }
   452     return rv;
   453 }
   455 NS_IMETHODIMP
   456 nsXULTemplateBuilder::CreateContents(nsIContent* aElement, bool aForceCreation)
   457 {
   458     return NS_OK;
   459 }
   461 NS_IMETHODIMP
   462 nsXULTemplateBuilder::HasGeneratedContent(nsIRDFResource* aResource,
   463                                           nsIAtom* aTag,
   464                                           bool* aGenerated)
   465 {
   466     *aGenerated = false;
   467     return NS_OK;
   468 }
   470 NS_IMETHODIMP
   471 nsXULTemplateBuilder::AddResult(nsIXULTemplateResult* aResult,
   472                                 nsIDOMNode* aQueryNode)
   473 {
   474     NS_ENSURE_ARG_POINTER(aResult);
   475     NS_ENSURE_ARG_POINTER(aQueryNode);
   477     return UpdateResult(nullptr, aResult, aQueryNode);
   478 }
   480 NS_IMETHODIMP
   481 nsXULTemplateBuilder::RemoveResult(nsIXULTemplateResult* aResult)
   482 {
   483     NS_ENSURE_ARG_POINTER(aResult);
   485     return UpdateResult(aResult, nullptr, nullptr);
   486 }
   488 NS_IMETHODIMP
   489 nsXULTemplateBuilder::ReplaceResult(nsIXULTemplateResult* aOldResult,
   490                                     nsIXULTemplateResult* aNewResult,
   491                                     nsIDOMNode* aQueryNode)
   492 {
   493     NS_ENSURE_ARG_POINTER(aOldResult);
   494     NS_ENSURE_ARG_POINTER(aNewResult);
   495     NS_ENSURE_ARG_POINTER(aQueryNode);
   497     // just remove the old result and then add a new result separately
   499     nsresult rv = UpdateResult(aOldResult, nullptr, nullptr);
   500     if (NS_FAILED(rv))
   501         return rv;
   503     return UpdateResult(nullptr, aNewResult, aQueryNode);
   504 }
   506 nsresult
   507 nsXULTemplateBuilder::UpdateResult(nsIXULTemplateResult* aOldResult,
   508                                    nsIXULTemplateResult* aNewResult,
   509                                    nsIDOMNode* aQueryNode)
   510 {
   511     PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
   512            ("nsXULTemplateBuilder::UpdateResult %p %p %p",
   513            aOldResult, aNewResult, aQueryNode));
   515     if (!mRoot || !mQueriesCompiled)
   516       return NS_OK;
   518     // get the containers where content may be inserted. If
   519     // GetInsertionLocations returns false, no container has generated
   520     // any content yet so new content should not be generated either. This
   521     // will be false if the result applies to content that is in a closed menu
   522     // or treeitem for example.
   524     nsAutoPtr<nsCOMArray<nsIContent> > insertionPoints;
   525     bool mayReplace = GetInsertionLocations(aOldResult ? aOldResult : aNewResult,
   526                                               getter_Transfers(insertionPoints));
   527     if (! mayReplace)
   528         return NS_OK;
   530     nsresult rv = NS_OK;
   532     nsCOMPtr<nsIRDFResource> oldId, newId;
   533     nsTemplateQuerySet* queryset = nullptr;
   535     if (aOldResult) {
   536         rv = GetResultResource(aOldResult, getter_AddRefs(oldId));
   537         if (NS_FAILED(rv))
   538             return rv;
   540         // Ignore re-entrant builds for content that is currently in our
   541         // activation stack.
   542         if (IsActivated(oldId))
   543             return NS_OK;
   544     }
   546     if (aNewResult) {
   547         rv = GetResultResource(aNewResult, getter_AddRefs(newId));
   548         if (NS_FAILED(rv))
   549             return rv;
   551         // skip results that don't have ids
   552         if (! newId)
   553             return NS_OK;
   555         // Ignore re-entrant builds for content that is currently in our
   556         // activation stack.
   557         if (IsActivated(newId))
   558             return NS_OK;
   560         // look for the queryset associated with the supplied query node
   561         nsCOMPtr<nsIContent> querycontent = do_QueryInterface(aQueryNode);
   563         int32_t count = mQuerySets.Length();
   564         for (int32_t q = 0; q < count; q++) {
   565             nsTemplateQuerySet* qs = mQuerySets[q];
   566             if (qs->mQueryNode == querycontent) {
   567                 queryset = qs;
   568                 break;
   569             }
   570         }
   572         if (! queryset)
   573             return NS_OK;
   574     }
   576     if (insertionPoints) {
   577         // iterate over each insertion point and add or remove the result from
   578         // that container
   579         uint32_t count = insertionPoints->Count();
   580         for (uint32_t t = 0; t < count; t++) {
   581             nsCOMPtr<nsIContent> insertionPoint = insertionPoints->SafeObjectAt(t);
   582             if (insertionPoint) {
   583                 rv = UpdateResultInContainer(aOldResult, aNewResult, queryset,
   584                                              oldId, newId, insertionPoint);
   585                 if (NS_FAILED(rv))
   586                     return rv;
   587             }
   588         }
   589     }
   590     else {
   591         // The tree builder doesn't use insertion points, so no insertion
   592         // points will be set. In this case, just update the one result.
   593         rv = UpdateResultInContainer(aOldResult, aNewResult, queryset,
   594                                      oldId, newId, nullptr);
   595     }
   597     return NS_OK;
   598 }
   600 nsresult
   601 nsXULTemplateBuilder::UpdateResultInContainer(nsIXULTemplateResult* aOldResult,
   602                                               nsIXULTemplateResult* aNewResult,
   603                                               nsTemplateQuerySet* aQuerySet,
   604                                               nsIRDFResource* aOldId,
   605                                               nsIRDFResource* aNewId,
   606                                               nsIContent* aInsertionPoint)
   607 {
   608     // This method takes a result that no longer applies (aOldResult) and
   609     // replaces it with a new result (aNewResult). Either may be null
   610     // indicating to just remove a result or add a new one without replacing.
   611     //
   612     // Matches are stored in the hashtable mMatchMap, keyed by result id. If
   613     // there is more than one query, or the same id is found in different
   614     // containers, the values in the hashtable will be a linked list of all
   615     // the matches for that id. The matches are sorted according to the
   616     // queries they are associated with. Matches for earlier queries in the
   617     // template take priority over matches from later queries. The priority
   618     // for a match is determined from the match's QuerySetPriority method.
   619     // The first query has a priority 0, and higher numbers are for later
   620     // queries with successively higher priorities. Thus, a match takes
   621     // precedence if it has a lower priority than another. If there is only
   622     // one query or container, then the match doesn't have any linked items.
   623     //
   624     // Matches are nsTemplateMatch objects. They are wrappers around
   625     // nsIXULTemplateResult result objects and are created with
   626     // nsTemplateMatch::Create below. The aQuerySet argument specifies which
   627     // query the match is associated with.
   628     //
   629     // When a result id exists in multiple containers, the match's mContainer
   630     // field is set to the container it corresponds to. The aInsertionPoint
   631     // argument specifies which container is being updated. Even though they
   632     // are stored in the same linked list as other matches of the same id, the
   633     // matches for different containers are treated separately. They are only
   634     // stored in the same hashtable to avoid a more complex data structure, as
   635     // the use of the same id in multiple containers isn't a common occurance.
   636     //
   637     // Only one match with a given id per container is active at a time. When
   638     // a match is active, content is generated for it. When a match is
   639     // inactive, content is not generated for it. A match becomes active if
   640     // another match with the same id and container with a lower priority
   641     // isn't already active, and the match has a rule or conditions clause
   642     // which evaluates to true. The former is checked by comparing the value
   643     // of the QuerySetPriority method of the match with earlier matches. The
   644     // latter is checked with the DetermineMatchedRule method.
   645     //
   646     // Naturally, if a match with a lower priority is active, it overrides
   647     // the new match, so the new match is hooked up into the match linked
   648     // list as inactive, and no content is generated for it. If a match with a
   649     // higher priority is active, and the new match's conditions evaluate
   650     // to true, then this existing match with the higher priority needs to have
   651     // its generated content removed and replaced with the new match's
   652     // generated content.
   653     //
   654     // Similar situations apply when removing an existing match. If the match
   655     // is active, the existing generated content will need to be removed, and
   656     // a match of higher priority that is revealed may become active and need
   657     // to have content generated.
   658     //
   659     // Content removal and generation is done by the ReplaceMatch method which
   660     // is overridden for the content builder and tree builder to update the
   661     // generated output for each type.
   662     //
   663     // The code below handles all of the various cases and ensures that the
   664     // match lists are maintained properly.
   666     nsresult rv = NS_OK;
   667     int16_t ruleindex;
   668     nsTemplateRule* matchedrule = nullptr;
   670     // Indicates that the old match was active and must have its content
   671     // removed
   672     bool oldMatchWasActive = false;
   674     // acceptedmatch will be set to a new match that has to have new content
   675     // generated for it. If a new match doesn't need to have content
   676     // generated, (because for example, a match with a lower priority
   677     // already applies), then acceptedmatch will be null, but the match will
   678     // be still hooked up into the chain, since it may become active later
   679     // as other results are updated.
   680     nsTemplateMatch* acceptedmatch = nullptr;
   682     // When aOldResult is specified, removematch will be set to the
   683     // corresponding match. This match needs to be deleted as it no longer
   684     // applies. However, removedmatch will be null when aOldResult is null, or
   685     // when no match was found corresponding to aOldResult.
   686     nsTemplateMatch* removedmatch = nullptr;
   688     // These will be set when aNewResult is specified indicating to add a
   689     // result, but will end up replacing an existing match. The former
   690     // indicates a match being replaced that was active and had content
   691     // generated for it, while the latter indicates a match that wasn't active
   692     // and just needs to be deleted. Both may point to different matches. For
   693     // example, if the new match becomes active, replacing an inactive match,
   694     // the inactive match will need to be deleted. However, if another match
   695     // with a higher priority is active, the new match will override it, so
   696     // content will need to be generated for the new match and removed for
   697     // this existing active match.
   698     nsTemplateMatch* replacedmatch = nullptr, * replacedmatchtodelete = nullptr;
   700     if (aOldResult) {
   701         nsTemplateMatch* firstmatch;
   702         if (mMatchMap.Get(aOldId, &firstmatch)) {
   703             nsTemplateMatch* oldmatch = firstmatch;
   704             nsTemplateMatch* prevmatch = nullptr;
   706             // look for the right match if there was more than one
   707             while (oldmatch && (oldmatch->mResult != aOldResult)) {
   708                 prevmatch = oldmatch;
   709                 oldmatch = oldmatch->mNext;
   710             }
   712             if (oldmatch) {
   713                 nsTemplateMatch* findmatch = oldmatch->mNext;
   715                 // Keep a reference so that linked list can be hooked up at
   716                 // the end in case an error occurs.
   717                 nsTemplateMatch* nextmatch = findmatch;
   719                 if (oldmatch->IsActive()) {
   720                     // Indicate that the old match was active so its content
   721                     // will be removed later.
   722                     oldMatchWasActive = true;
   724                     // The match being removed is the active match, so scan
   725                     // through the later matches to determine if one should
   726                     // now become the active match.
   727                     while (findmatch) {
   728                         // only other matches with the same container should
   729                         // now match, leave other containers alone
   730                         if (findmatch->GetContainer() == aInsertionPoint) {
   731                             nsTemplateQuerySet* qs =
   732                                 mQuerySets[findmatch->QuerySetPriority()];
   734                             DetermineMatchedRule(aInsertionPoint, findmatch->mResult,
   735                                                  qs, &matchedrule, &ruleindex);
   737                             if (matchedrule) {
   738                                 rv = findmatch->RuleMatched(qs,
   739                                                             matchedrule, ruleindex,
   740                                                             findmatch->mResult);
   741                                 if (NS_FAILED(rv))
   742                                     return rv;
   744                                 acceptedmatch = findmatch;
   745                                 break;
   746                             }
   747                         }
   749                         findmatch = findmatch->mNext;
   750                     }
   751                 }
   753                 if (oldmatch == firstmatch) {
   754                     // the match to remove is at the beginning
   755                     if (oldmatch->mNext) {
   756                         mMatchMap.Put(aOldId, oldmatch->mNext);
   757                     }
   758                     else {
   759                         mMatchMap.Remove(aOldId);
   760                     }
   761                 }
   763                 if (prevmatch)
   764                     prevmatch->mNext = nextmatch;
   766                 removedmatch = oldmatch;
   767                 if (mFlags & eLoggingEnabled)
   768                     OutputMatchToLog(aOldId, removedmatch, false);
   769             }
   770         }
   771     }
   773     nsTemplateMatch *newmatch = nullptr;
   774     if (aNewResult) {
   775         // only allow a result to be inserted into containers with a matching tag
   776         nsIAtom* tag = aQuerySet->GetTag();
   777         if (aInsertionPoint && tag && tag != aInsertionPoint->Tag())
   778             return NS_OK;
   780         int32_t findpriority = aQuerySet->Priority();
   782         newmatch = nsTemplateMatch::Create(findpriority,
   783                                            aNewResult, aInsertionPoint);
   784         if (!newmatch)
   785             return NS_ERROR_OUT_OF_MEMORY;
   787         nsTemplateMatch* firstmatch;
   788         if (mMatchMap.Get(aNewId, &firstmatch)) {
   789             bool hasEarlierActiveMatch = false;
   791             // Scan through the existing matches to find where the new one
   792             // should be inserted. oldmatch will be set to the old match for
   793             // the same query and prevmatch will be set to the match before it.
   794             nsTemplateMatch* prevmatch = nullptr;
   795             nsTemplateMatch* oldmatch = firstmatch;
   796             while (oldmatch) {
   797                 // Break out once we've reached a query in the list with a
   798                 // lower priority. The new match will be inserted at this
   799                 // location so that the match list is sorted by priority.
   800                 int32_t priority = oldmatch->QuerySetPriority();
   801                 if (priority > findpriority) {
   802                     oldmatch = nullptr;
   803                     break;
   804                 }
   806                 // look for matches that belong in the same container
   807                 if (oldmatch->GetContainer() == aInsertionPoint) {
   808                     if (priority == findpriority)
   809                         break;
   811                     // If a match with a lower priority is active, the new
   812                     // match can't replace it.
   813                     if (oldmatch->IsActive())
   814                         hasEarlierActiveMatch = true;
   815                 }
   817                 prevmatch = oldmatch;
   818                 oldmatch = oldmatch->mNext;
   819             }
   821             // At this point, oldmatch will either be null, or set to a match
   822             // with the same container and priority. If set, oldmatch will
   823             // need to be replaced by newmatch.
   825             if (oldmatch)
   826                 newmatch->mNext = oldmatch->mNext;
   827             else if (prevmatch)
   828                 newmatch->mNext = prevmatch->mNext;
   829             else
   830                 newmatch->mNext = firstmatch;
   832             // hasEarlierActiveMatch will be set to true if a match with a
   833             // lower priority was found. The new match won't replace it in
   834             // this case. If hasEarlierActiveMatch is false, then the new match
   835             // may be become active if it matches one of the rules, and will
   836             // generate output. It's also possible however, that a match with
   837             // the same priority already exists, which means that the new match
   838             // will replace the old one. In this case, oldmatch will be set to
   839             // the old match. The content for the old match must be removed and
   840             // content for the new match generated in its place.
   841             if (! hasEarlierActiveMatch) {
   842                 // If the old match was the active match, set replacedmatch to
   843                 // indicate that it needs its content removed.
   844                 if (oldmatch) {
   845                     if (oldmatch->IsActive())
   846                         replacedmatch = oldmatch;
   847                     replacedmatchtodelete = oldmatch;
   848                 }
   850                 // check if the new result matches the rules
   851                 rv = DetermineMatchedRule(aInsertionPoint, newmatch->mResult,
   852                                           aQuerySet, &matchedrule, &ruleindex);
   853                 if (NS_FAILED(rv)) {
   854                     nsTemplateMatch::Destroy(newmatch, false);
   855                     return rv;
   856                 }
   858                 if (matchedrule) {
   859                     rv = newmatch->RuleMatched(aQuerySet,
   860                                                matchedrule, ruleindex,
   861                                                newmatch->mResult);
   862                     if (NS_FAILED(rv)) {
   863                         nsTemplateMatch::Destroy(newmatch, false);
   864                         return rv;
   865                     }
   867                     // acceptedmatch may have been set in the block handling
   868                     // aOldResult earlier. If so, we would only get here when
   869                     // that match has a higher priority than this new match.
   870                     // As only one match can have content generated for it, it
   871                     // is OK to set acceptedmatch here to the new match,
   872                     // ignoring the other one.
   873                     acceptedmatch = newmatch;
   875                     // Clear the matched state of the later results for the
   876                     // same container.
   877                     nsTemplateMatch* clearmatch = newmatch->mNext;
   878                     while (clearmatch) {
   879                         if (clearmatch->GetContainer() == aInsertionPoint &&
   880                             clearmatch->IsActive()) {
   881                             clearmatch->SetInactive();
   882                             // Replacedmatch should be null here. If not, it
   883                             // means that two matches were active which isn't
   884                             // a valid state
   885                             NS_ASSERTION(!replacedmatch,
   886                                          "replaced match already set");
   887                             replacedmatch = clearmatch;
   888                             break;
   889                         }
   890                         clearmatch = clearmatch->mNext;
   891                     }
   892                 }
   893                 else if (oldmatch && oldmatch->IsActive()) {
   894                     // The result didn't match the rules, so look for a later
   895                     // one. However, only do this if the old match was the
   896                     // active match.
   897                     newmatch = newmatch->mNext;
   898                     while (newmatch) {
   899                         if (newmatch->GetContainer() == aInsertionPoint) {
   900                             rv = DetermineMatchedRule(aInsertionPoint, newmatch->mResult,
   901                                                       aQuerySet, &matchedrule, &ruleindex);
   902                             if (NS_FAILED(rv)) {
   903                                 nsTemplateMatch::Destroy(newmatch, false);
   904                                 return rv;
   905                             }
   907                             if (matchedrule) {
   908                                 rv = newmatch->RuleMatched(aQuerySet,
   909                                                            matchedrule, ruleindex,
   910                                                            newmatch->mResult);
   911                                 if (NS_FAILED(rv)) {
   912                                     nsTemplateMatch::Destroy(newmatch, false);
   913                                     return rv;
   914                                 }
   916                                 acceptedmatch = newmatch;
   917                                 break;
   918                             }
   919                         }
   921                         newmatch = newmatch->mNext;
   922                     }
   923                 }
   925                 // put the match in the map if there isn't a previous match
   926                 if (! prevmatch) {
   927                     mMatchMap.Put(aNewId, newmatch);
   928                 }
   929             }
   931             // hook up the match last in case an error occurs
   932             if (prevmatch)
   933                 prevmatch->mNext = newmatch;
   934         }
   935         else {
   936             // The id is not used in the hashtable yet so create a new match
   937             // and add it to the hashtable.
   938             rv = DetermineMatchedRule(aInsertionPoint, aNewResult,
   939                                       aQuerySet, &matchedrule, &ruleindex);
   940             if (NS_FAILED(rv)) {
   941                 nsTemplateMatch::Destroy(newmatch, false);
   942                 return rv;
   943             }
   945             if (matchedrule) {
   946                 rv = newmatch->RuleMatched(aQuerySet, matchedrule,
   947                                            ruleindex, aNewResult);
   948                 if (NS_FAILED(rv)) {
   949                     nsTemplateMatch::Destroy(newmatch, false);
   950                     return rv;
   951                 }
   953                 acceptedmatch = newmatch;
   954             }
   956             mMatchMap.Put(aNewId, newmatch);
   957         }
   958     }
   960     // The ReplaceMatch method is builder specific and removes the generated
   961     // content for a match.
   963     // Remove the content for a match that was active and needs to be replaced.
   964     if (replacedmatch) {
   965         rv = ReplaceMatch(replacedmatch->mResult, nullptr, nullptr,
   966                           aInsertionPoint);
   968         if (mFlags & eLoggingEnabled)
   969             OutputMatchToLog(aNewId, replacedmatch, false);
   970     }
   972     // remove a match that needs to be deleted.
   973     if (replacedmatchtodelete)
   974         nsTemplateMatch::Destroy(replacedmatchtodelete, true);
   976     // If the old match was active, the content for it needs to be removed.
   977     // If the old match was not active, it shouldn't have had any content,
   978     // so just pass null to ReplaceMatch. If acceptedmatch was set, then
   979     // content needs to be generated for a new match.
   980     if (oldMatchWasActive || acceptedmatch)
   981         rv = ReplaceMatch(oldMatchWasActive ? aOldResult : nullptr,
   982                           acceptedmatch, matchedrule, aInsertionPoint);
   984     // delete the old match that was replaced
   985     if (removedmatch)
   986         nsTemplateMatch::Destroy(removedmatch, true);
   988     if (mFlags & eLoggingEnabled && newmatch)
   989         OutputMatchToLog(aNewId, newmatch, true);
   991     return rv;
   992 }
   994 NS_IMETHODIMP
   995 nsXULTemplateBuilder::ResultBindingChanged(nsIXULTemplateResult* aResult)
   996 {
   997     // A binding update is used when only the values of the bindings have
   998     // changed, so the same rule still applies. Just synchronize the content.
   999     // The new result will have the new values.
  1000     NS_ENSURE_ARG_POINTER(aResult);
  1002     if (!mRoot || !mQueriesCompiled)
  1003       return NS_OK;
  1005     return SynchronizeResult(aResult);
  1008 NS_IMETHODIMP
  1009 nsXULTemplateBuilder::GetRootResult(nsIXULTemplateResult** aResult)
  1011   *aResult = mRootResult;
  1012   NS_IF_ADDREF(*aResult);
  1013   return NS_OK;
  1016 NS_IMETHODIMP
  1017 nsXULTemplateBuilder::GetResultForId(const nsAString& aId,
  1018                                      nsIXULTemplateResult** aResult)
  1020     if (aId.IsEmpty())
  1021         return NS_ERROR_INVALID_ARG;
  1023     nsCOMPtr<nsIRDFResource> resource;
  1024     gRDFService->GetUnicodeResource(aId, getter_AddRefs(resource));
  1026     *aResult = nullptr;
  1028     nsTemplateMatch* match;
  1029     if (mMatchMap.Get(resource, &match)) {
  1030         // find the active match
  1031         while (match) {
  1032             if (match->IsActive()) {
  1033                 *aResult = match->mResult;
  1034                 NS_IF_ADDREF(*aResult);
  1035                 break;
  1037             match = match->mNext;
  1041     return NS_OK;
  1044 NS_IMETHODIMP
  1045 nsXULTemplateBuilder::GetResultForContent(nsIDOMElement* aContent,
  1046                                           nsIXULTemplateResult** aResult)
  1048     *aResult = nullptr;
  1049     return NS_OK;
  1052 NS_IMETHODIMP
  1053 nsXULTemplateBuilder::AddListener(nsIXULBuilderListener* aListener)
  1055     NS_ENSURE_ARG(aListener);
  1057     if (!mListeners.AppendObject(aListener))
  1058         return NS_ERROR_OUT_OF_MEMORY;
  1060     return NS_OK;
  1063 NS_IMETHODIMP
  1064 nsXULTemplateBuilder::RemoveListener(nsIXULBuilderListener* aListener)
  1066     NS_ENSURE_ARG(aListener);
  1068     mListeners.RemoveObject(aListener);
  1070     return NS_OK;
  1073 NS_IMETHODIMP
  1074 nsXULTemplateBuilder::Observe(nsISupports* aSubject,
  1075                               const char* aTopic,
  1076                               const char16_t* aData)
  1078     // Uuuuber hack to clean up circular references that the cycle collector
  1079     // doesn't know about. See bug 394514.
  1080     if (!strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC)) {
  1081         nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aSubject);
  1082         if (window) {
  1083             nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
  1084             if (doc && doc == mObservedDocument)
  1085                 NodeWillBeDestroyed(doc);
  1088     return NS_OK;
  1090 //----------------------------------------------------------------------
  1091 //
  1092 // nsIDocumentOberver interface
  1093 //
  1095 void
  1096 nsXULTemplateBuilder::AttributeChanged(nsIDocument* aDocument,
  1097                                        Element*     aElement,
  1098                                        int32_t      aNameSpaceID,
  1099                                        nsIAtom*     aAttribute,
  1100                                        int32_t      aModType)
  1102     if (aElement == mRoot && aNameSpaceID == kNameSpaceID_None) {
  1103         // Check for a change to the 'ref' attribute on an atom, in which
  1104         // case we may need to nuke and rebuild the entire content model
  1105         // beneath the element.
  1106         if (aAttribute == nsGkAtoms::ref)
  1107             nsContentUtils::AddScriptRunner(
  1108                 NS_NewRunnableMethod(this, &nsXULTemplateBuilder::RunnableRebuild));
  1110         // Check for a change to the 'datasources' attribute. If so, setup
  1111         // mDB by parsing the new value and rebuild.
  1112         else if (aAttribute == nsGkAtoms::datasources) {
  1113             nsContentUtils::AddScriptRunner(
  1114                 NS_NewRunnableMethod(this, &nsXULTemplateBuilder::RunnableLoadAndRebuild));
  1119 void
  1120 nsXULTemplateBuilder::ContentRemoved(nsIDocument* aDocument,
  1121                                      nsIContent* aContainer,
  1122                                      nsIContent* aChild,
  1123                                      int32_t aIndexInContainer,
  1124                                      nsIContent* aPreviousSibling)
  1126     if (mRoot && nsContentUtils::ContentIsDescendantOf(mRoot, aChild)) {
  1127         nsRefPtr<nsXULTemplateBuilder> kungFuDeathGrip(this);
  1129         if (mQueryProcessor)
  1130             mQueryProcessor->Done();
  1132         // Pass false to Uninit since content is going away anyway
  1133         nsContentUtils::AddScriptRunner(
  1134             NS_NewRunnableMethod(this, &nsXULTemplateBuilder::UninitFalse));
  1136         MOZ_ASSERT(aDocument == mObservedDocument);
  1137         StopObserving();
  1139         nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(aDocument);
  1140         if (xuldoc)
  1141             xuldoc->SetTemplateBuilderFor(mRoot, nullptr);
  1143         // clear the template state when removing content so that template
  1144         // content will be regenerated again if the content is reinserted
  1145         nsXULElement *xulcontent = nsXULElement::FromContent(mRoot);
  1146         if (xulcontent)
  1147             xulcontent->ClearTemplateGenerated();
  1149         CleanUp(true);
  1151         mDB = nullptr;
  1152         mCompDB = nullptr;
  1153         mDataSource = nullptr;
  1157 void
  1158 nsXULTemplateBuilder::NodeWillBeDestroyed(const nsINode* aNode)
  1160     // The call to RemoveObserver could release the last reference to
  1161     // |this|, so hold another reference.
  1162     nsRefPtr<nsXULTemplateBuilder> kungFuDeathGrip(this);
  1164     // Break circular references
  1165     if (mQueryProcessor)
  1166         mQueryProcessor->Done();
  1168     mDataSource = nullptr;
  1169     mDB = nullptr;
  1170     mCompDB = nullptr;
  1172     nsContentUtils::AddScriptRunner(
  1173         NS_NewRunnableMethod(this, &nsXULTemplateBuilder::UninitTrue));
  1179 //----------------------------------------------------------------------
  1180 //
  1181 // Implementation methods
  1182 //
  1184 nsresult
  1185 nsXULTemplateBuilder::LoadDataSources(nsIDocument* aDocument,
  1186                                       bool* aShouldDelayBuilding)
  1188     NS_PRECONDITION(mRoot != nullptr, "not initialized");
  1190     nsresult rv;
  1191     bool isRDFQuery = false;
  1193     // we'll set these again later, after we create a new composite ds
  1194     mDB = nullptr;
  1195     mCompDB = nullptr;
  1196     mDataSource = nullptr;
  1198     *aShouldDelayBuilding = false;
  1200     nsAutoString datasources;
  1201     mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::datasources, datasources);
  1203     nsAutoString querytype;
  1204     mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::querytype, querytype);
  1206     // create the query processor. The querytype attribute on the root element
  1207     // may be used to create one of a specific type.
  1209     // XXX should non-chrome be restricted to specific names?
  1210     if (querytype.IsEmpty())
  1211         querytype.AssignLiteral("rdf");
  1213     if (querytype.EqualsLiteral("rdf")) {
  1214         isRDFQuery = true;
  1215         mQueryProcessor = new nsXULTemplateQueryProcessorRDF();
  1216         NS_ENSURE_TRUE(mQueryProcessor, NS_ERROR_OUT_OF_MEMORY);
  1218     else if (querytype.EqualsLiteral("xml")) {
  1219         mQueryProcessor = new nsXULTemplateQueryProcessorXML();
  1220         NS_ENSURE_TRUE(mQueryProcessor, NS_ERROR_OUT_OF_MEMORY);
  1222     else if (querytype.EqualsLiteral("storage")) {
  1223         mQueryProcessor = new nsXULTemplateQueryProcessorStorage();
  1224         NS_ENSURE_TRUE(mQueryProcessor, NS_ERROR_OUT_OF_MEMORY);
  1226     else {
  1227         nsAutoCString cid(NS_QUERY_PROCESSOR_CONTRACTID_PREFIX);
  1228         AppendUTF16toUTF8(querytype, cid);
  1229         mQueryProcessor = do_CreateInstance(cid.get(), &rv);
  1231         if (!mQueryProcessor) {
  1232             nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_INVALID_QUERYPROCESSOR);
  1233             return rv;
  1237     rv = LoadDataSourceUrls(aDocument, datasources,
  1238                             isRDFQuery, aShouldDelayBuilding);
  1239     NS_ENSURE_SUCCESS(rv, rv);
  1241     // Now set the database on the element, so that script writers can
  1242     // access it.
  1243     nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(aDocument);
  1244     if (xuldoc)
  1245         xuldoc->SetTemplateBuilderFor(mRoot, this);
  1247     if (!mRoot->IsXUL()) {
  1248         // Hmm. This must be an HTML element. Try to set it as a
  1249         // JS property "by hand".
  1250         InitHTMLTemplateRoot();
  1253     return NS_OK;
  1256 nsresult
  1257 nsXULTemplateBuilder::LoadDataSourceUrls(nsIDocument* aDocument,
  1258                                          const nsAString& aDataSources,
  1259                                          bool aIsRDFQuery,
  1260                                          bool* aShouldDelayBuilding)
  1262     // Grab the doc's principal...
  1263     nsIPrincipal *docPrincipal = aDocument->NodePrincipal();
  1265     NS_ASSERTION(docPrincipal == mRoot->NodePrincipal(),
  1266                  "Principal mismatch?  Which one to use?");
  1268     bool isTrusted = false;
  1269     nsresult rv = IsSystemPrincipal(docPrincipal, &isTrusted);
  1270     NS_ENSURE_SUCCESS(rv, rv);
  1272     // Parse datasources: they are assumed to be a whitespace
  1273     // separated list of URIs; e.g.,
  1274     //
  1275     //     rdf:bookmarks rdf:history http://foo.bar.com/blah.cgi?baz=9
  1276     //
  1277     nsIURI *docurl = aDocument->GetDocumentURI();
  1279     nsCOMPtr<nsIMutableArray> uriList = do_CreateInstance(NS_ARRAY_CONTRACTID);
  1280     if (!uriList)
  1281         return NS_ERROR_FAILURE;
  1283     nsAutoString datasources(aDataSources);
  1284     uint32_t first = 0;
  1285     while (1) {
  1286         while (first < datasources.Length() && nsCRT::IsAsciiSpace(datasources.CharAt(first)))
  1287             ++first;
  1289         if (first >= datasources.Length())
  1290             break;
  1292         uint32_t last = first;
  1293         while (last < datasources.Length() && !nsCRT::IsAsciiSpace(datasources.CharAt(last)))
  1294             ++last;
  1296         nsAutoString uriStr;
  1297         datasources.Mid(uriStr, first, last - first);
  1298         first = last + 1;
  1300         // A special 'dummy' datasource
  1301         if (uriStr.EqualsLiteral("rdf:null"))
  1302             continue;
  1304         if (uriStr.CharAt(0) == '#') {
  1305             // ok, the datasource is certainly a node of the current document
  1306             nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(aDocument);
  1307             nsCOMPtr<nsIDOMElement> dsnode;
  1309             domdoc->GetElementById(Substring(uriStr, 1),
  1310                                    getter_AddRefs(dsnode));
  1312             if (dsnode)
  1313                 uriList->AppendElement(dsnode, false);
  1314             continue;
  1317         // N.B. that `failure' (e.g., because it's an unknown
  1318         // protocol) leaves uriStr unaltered.
  1319         NS_MakeAbsoluteURI(uriStr, uriStr, docurl);
  1321         nsCOMPtr<nsIURI> uri;
  1322         rv = NS_NewURI(getter_AddRefs(uri), uriStr);
  1323         if (NS_FAILED(rv) || !uri)
  1324             continue; // Necko will barf if our URI is weird
  1326         // don't add the uri to the list if the document is not allowed to
  1327         // load it
  1328         if (!isTrusted && NS_FAILED(docPrincipal->CheckMayLoad(uri, true, false)))
  1329           continue;
  1331         uriList->AppendElement(uri, false);
  1334     nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(mRoot);
  1335     rv = mQueryProcessor->GetDatasource(uriList,
  1336                                         rootNode,
  1337                                         isTrusted,
  1338                                         this,
  1339                                         aShouldDelayBuilding,
  1340                                         getter_AddRefs(mDataSource));
  1341     NS_ENSURE_SUCCESS(rv, rv);
  1343     if (aIsRDFQuery && mDataSource) {  
  1344         // check if we were given an inference engine type
  1345         nsCOMPtr<nsIRDFInferDataSource> inferDB = do_QueryInterface(mDataSource);
  1346         if (inferDB) {
  1347             nsCOMPtr<nsIRDFDataSource> ds;
  1348             inferDB->GetBaseDataSource(getter_AddRefs(ds));
  1349             if (ds)
  1350                 mCompDB = do_QueryInterface(ds);
  1353         if (!mCompDB)
  1354             mCompDB = do_QueryInterface(mDataSource);
  1356         mDB = do_QueryInterface(mDataSource);
  1359     if (!mDB && isTrusted) {
  1360         gRDFService->GetDataSource("rdf:local-store", getter_AddRefs(mDB));
  1363     return NS_OK;
  1366 nsresult
  1367 nsXULTemplateBuilder::InitHTMLTemplateRoot()
  1369     // Use XPConnect and the JS APIs to whack mDB and this as the
  1370     // 'database' and 'builder' properties onto aElement.
  1371     nsresult rv;
  1373     nsCOMPtr<nsIDocument> doc = mRoot->GetDocument();
  1374     NS_ASSERTION(doc, "no document");
  1375     if (! doc)
  1376         return NS_ERROR_UNEXPECTED;
  1378     nsCOMPtr<nsIScriptGlobalObject> global =
  1379       do_QueryInterface(doc->GetWindow());
  1380     if (! global)
  1381         return NS_ERROR_UNEXPECTED;
  1383     nsCOMPtr<nsIGlobalObject> innerWin =
  1384         do_QueryInterface(doc->GetInnerWindow());
  1386     // We are going to run script via JS_SetProperty, so we need a script entry
  1387     // point, but as this is XUL related it does not appear in the HTML spec.
  1388     AutoEntryScript entryScript(innerWin, true);
  1389     JSContext* jscontext = entryScript.cx();
  1391     JS::Rooted<JS::Value> v(jscontext);
  1392     rv = nsContentUtils::WrapNative(jscontext, mRoot, mRoot, &v);
  1393     NS_ENSURE_SUCCESS(rv, rv);
  1395     JS::Rooted<JSObject*> jselement(jscontext, JSVAL_TO_OBJECT(v));
  1397     if (mDB) {
  1398         // database
  1399         JS::Rooted<JS::Value> jsdatabase(jscontext);
  1400         rv = nsContentUtils::WrapNative(jscontext, mDB,
  1401                                         &NS_GET_IID(nsIRDFCompositeDataSource),
  1402                                         &jsdatabase);
  1403         NS_ENSURE_SUCCESS(rv, rv);
  1405         bool ok = JS_SetProperty(jscontext, jselement, "database", jsdatabase);
  1406         NS_ASSERTION(ok, "unable to set database property");
  1407         if (! ok)
  1408             return NS_ERROR_FAILURE;
  1412         // builder
  1413         JS::Rooted<JS::Value> jsbuilder(jscontext);
  1414         rv = nsContentUtils::WrapNative(jscontext,
  1415                                         static_cast<nsIXULTemplateBuilder*>(this),
  1416                                         &NS_GET_IID(nsIXULTemplateBuilder),
  1417                                         &jsbuilder);
  1418         NS_ENSURE_SUCCESS(rv, rv);
  1420         bool ok = JS_SetProperty(jscontext, jselement, "builder", jsbuilder);
  1421         if (! ok)
  1422             return NS_ERROR_FAILURE;
  1425     return NS_OK;
  1428 nsresult
  1429 nsXULTemplateBuilder::DetermineMatchedRule(nsIContent *aContainer,
  1430                                            nsIXULTemplateResult* aResult,
  1431                                            nsTemplateQuerySet* aQuerySet,
  1432                                            nsTemplateRule** aMatchedRule,
  1433                                            int16_t *aRuleIndex)
  1435     // iterate through the rules and look for one that the result matches
  1436     int16_t count = aQuerySet->RuleCount();
  1437     for (int16_t r = 0; r < count; r++) {
  1438         nsTemplateRule* rule = aQuerySet->GetRuleAt(r);
  1439         // If a tag was specified, it must match the tag of the container
  1440         // where content is being inserted.
  1441         nsIAtom* tag = rule->GetTag();
  1442         if ((!aContainer || !tag || tag == aContainer->Tag()) &&
  1443             rule->CheckMatch(aResult)) {
  1444             *aMatchedRule = rule;
  1445             *aRuleIndex = r;
  1446             return NS_OK;
  1450     *aRuleIndex = -1;
  1451     *aMatchedRule = nullptr;
  1452     return NS_OK;
  1455 void
  1456 nsXULTemplateBuilder::ParseAttribute(const nsAString& aAttributeValue,
  1457                                      void (*aVariableCallback)(nsXULTemplateBuilder*, const nsAString&, void*),
  1458                                      void (*aTextCallback)(nsXULTemplateBuilder*, const nsAString&, void*),
  1459                                      void* aClosure)
  1461     nsAString::const_iterator done_parsing;
  1462     aAttributeValue.EndReading(done_parsing);
  1464     nsAString::const_iterator iter;
  1465     aAttributeValue.BeginReading(iter);
  1467     nsAString::const_iterator mark(iter), backup(iter);
  1469     for (; iter != done_parsing; backup = ++iter) {
  1470         // A variable is either prefixed with '?' (in the extended
  1471         // syntax) or "rdf:" (in the simple syntax).
  1472         bool isvar;
  1473         if (*iter == char16_t('?') && (++iter != done_parsing)) {
  1474             isvar = true;
  1476         else if ((*iter == char16_t('r') && (++iter != done_parsing)) &&
  1477                  (*iter == char16_t('d') && (++iter != done_parsing)) &&
  1478                  (*iter == char16_t('f') && (++iter != done_parsing)) &&
  1479                  (*iter == char16_t(':') && (++iter != done_parsing))) {
  1480             isvar = true;
  1482         else {
  1483             isvar = false;
  1486         if (! isvar) {
  1487             // It's not a variable, or we ran off the end of the
  1488             // string after the initial variable prefix. Since we may
  1489             // have slurped down some characters before realizing that
  1490             // fact, back up to the point where we started.
  1491             iter = backup;
  1492             continue;
  1494         else if (backup != mark && aTextCallback) {
  1495             // Okay, we've found a variable, and there's some vanilla
  1496             // text that's been buffered up. Flush it.
  1497             (*aTextCallback)(this, Substring(mark, backup), aClosure);
  1500         if (*iter == char16_t('?')) {
  1501             // Well, it was not really a variable, but "??". We use one
  1502             // question mark (the second one, actually) literally.
  1503             mark = iter;
  1504             continue;
  1507         // Construct a substring that is the symbol we need to look up
  1508         // in the rule's symbol table. The symbol is terminated by a
  1509         // space character, a caret, or the end of the string,
  1510         // whichever comes first.
  1511         nsAString::const_iterator first(backup);
  1513         char16_t c = 0;
  1514         while (iter != done_parsing) {
  1515             c = *iter;
  1516             if ((c == char16_t(' ')) || (c == char16_t('^')))
  1517                 break;
  1519             ++iter;
  1522         nsAString::const_iterator last(iter);
  1524         // Back up so we don't consume the terminating character
  1525         // *unless* the terminating character was a caret: the caret
  1526         // means "concatenate with no space in between".
  1527         if (c != char16_t('^'))
  1528             --iter;
  1530         (*aVariableCallback)(this, Substring(first, last), aClosure);
  1531         mark = iter;
  1532         ++mark;
  1535     if (backup != mark && aTextCallback) {
  1536         // If there's any text left over, then fire the text callback
  1537         (*aTextCallback)(this, Substring(mark, backup), aClosure);
  1542 struct MOZ_STACK_CLASS SubstituteTextClosure {
  1543     SubstituteTextClosure(nsIXULTemplateResult* aResult, nsAString& aString)
  1544         : result(aResult), str(aString) {}
  1546     // some datasources are lazily initialized or modified while values are
  1547     // being retrieved, causing results to be removed. Due to this, hold a
  1548     // strong reference to the result.
  1549     nsCOMPtr<nsIXULTemplateResult> result;
  1550     nsAString& str;
  1551 };
  1553 nsresult
  1554 nsXULTemplateBuilder::SubstituteText(nsIXULTemplateResult* aResult,
  1555                                      const nsAString& aAttributeValue,
  1556                                      nsAString& aString)
  1558     // See if it's the special value "..."
  1559     if (aAttributeValue.EqualsLiteral("...")) {
  1560         aResult->GetId(aString);
  1561         return NS_OK;
  1564     // Reasonable guess at how big it should be
  1565     aString.SetCapacity(aAttributeValue.Length());
  1567     SubstituteTextClosure closure(aResult, aString);
  1568     ParseAttribute(aAttributeValue,
  1569                    SubstituteTextReplaceVariable,
  1570                    SubstituteTextAppendText,
  1571                    &closure);
  1573     return NS_OK;
  1577 void
  1578 nsXULTemplateBuilder::SubstituteTextAppendText(nsXULTemplateBuilder* aThis,
  1579                                                const nsAString& aText,
  1580                                                void* aClosure)
  1582     // Append aString to the closure's result
  1583     SubstituteTextClosure* c = static_cast<SubstituteTextClosure*>(aClosure);
  1584     c->str.Append(aText);
  1587 void
  1588 nsXULTemplateBuilder::SubstituteTextReplaceVariable(nsXULTemplateBuilder* aThis,
  1589                                                     const nsAString& aVariable,
  1590                                                     void* aClosure)
  1592     // Substitute the value for the variable and append to the
  1593     // closure's result.
  1594     SubstituteTextClosure* c = static_cast<SubstituteTextClosure*>(aClosure);
  1596     nsAutoString replacementText;
  1598     // The symbol "rdf:*" is special, and means "this guy's URI"
  1599     if (aVariable.EqualsLiteral("rdf:*")){
  1600         c->result->GetId(replacementText);
  1602     else {
  1603         // Got a variable; get the value it's assigned to
  1604         nsCOMPtr<nsIAtom> var = do_GetAtom(aVariable);
  1605         c->result->GetBindingFor(var, replacementText);
  1608     c->str += replacementText;
  1611 bool
  1612 nsXULTemplateBuilder::IsTemplateElement(nsIContent* aContent)
  1614     return aContent->NodeInfo()->Equals(nsGkAtoms::_template,
  1615                                         kNameSpaceID_XUL);
  1618 nsresult
  1619 nsXULTemplateBuilder::GetTemplateRoot(nsIContent** aResult)
  1621     NS_PRECONDITION(mRoot != nullptr, "not initialized");
  1622     if (! mRoot)
  1623         return NS_ERROR_NOT_INITIALIZED;
  1625     // First, check and see if the root has a template attribute. This
  1626     // allows a template to be specified "out of line"; e.g.,
  1627     //
  1628     //   <window>
  1629     //     <foo template="MyTemplate">...</foo>
  1630     //     <template id="MyTemplate">...</template>
  1631     //   </window>
  1632     //
  1633     nsAutoString templateID;
  1634     mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::_template, templateID);
  1636     if (! templateID.IsEmpty()) {
  1637         nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(mRoot->GetDocument());
  1638         if (! domDoc)
  1639             return NS_OK;
  1641         nsCOMPtr<nsIDOMElement> domElement;
  1642         domDoc->GetElementById(templateID, getter_AddRefs(domElement));
  1644         if (domElement) {
  1645             nsCOMPtr<nsIContent> content = do_QueryInterface(domElement);
  1646             NS_ENSURE_STATE(content &&
  1647                             !nsContentUtils::ContentIsDescendantOf(mRoot,
  1648                                                                    content));
  1649             content.forget(aResult);
  1650             return NS_OK;
  1654     // If root node has no template attribute, then look for a child
  1655     // node which is a template tag.
  1656     for (nsIContent* child = mRoot->GetFirstChild();
  1657          child;
  1658          child = child->GetNextSibling()) {
  1660         if (IsTemplateElement(child)) {
  1661             NS_ADDREF(*aResult = child);
  1662             return NS_OK;
  1666     // Look through the anonymous children as well. Although FlattenedChildIterator
  1667     // will find a template element that has been placed in an insertion point, many
  1668     // bindings do not have a specific insertion point for the template element, which
  1669     // would cause it to not be part of the flattened content tree. The check above to
  1670     // check the explicit children as well handles this case.
  1671     FlattenedChildIterator iter(mRoot);
  1672     for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
  1673         if (IsTemplateElement(child)) {
  1674             NS_ADDREF(*aResult = child);
  1675             return NS_OK;
  1679     *aResult = nullptr;
  1680     return NS_OK;
  1683 nsresult
  1684 nsXULTemplateBuilder::CompileQueries()
  1686     nsCOMPtr<nsIContent> tmpl;
  1687     GetTemplateRoot(getter_AddRefs(tmpl));
  1688     if (! tmpl)
  1689         return NS_OK;
  1691     if (! mRoot)
  1692         return NS_ERROR_NOT_INITIALIZED;
  1694     // Determine if there are any special settings we need to observe
  1695     mFlags = 0;
  1697     nsAutoString flags;
  1698     mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::flags, flags);
  1700     // if the dont-test-empty flag is set, containers should not be checked to
  1701     // see if they are empty. If dont-recurse is set, then don't process the
  1702     // template recursively and only show one level of results. The logging
  1703     // flag logs errors and results to the console, which is useful when
  1704     // debugging templates.
  1705     nsWhitespaceTokenizer tokenizer(flags);
  1706     while (tokenizer.hasMoreTokens()) {
  1707       const nsDependentSubstring& token(tokenizer.nextToken());
  1708       if (token.EqualsLiteral("dont-test-empty"))
  1709         mFlags |= eDontTestEmpty;
  1710       else if (token.EqualsLiteral("dont-recurse"))
  1711         mFlags |= eDontRecurse;
  1712       else if (token.EqualsLiteral("logging"))
  1713         mFlags |= eLoggingEnabled;
  1716 #ifdef PR_LOGGING
  1717     // always enable logging if the debug setting is used
  1718     if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG))
  1719         mFlags |= eLoggingEnabled;
  1720 #endif
  1722     nsCOMPtr<nsIDOMNode> rootnode = do_QueryInterface(mRoot);
  1723     nsresult rv =
  1724         mQueryProcessor->InitializeForBuilding(mDataSource, this, rootnode);
  1725     if (NS_FAILED(rv))
  1726         return rv;
  1728     // Set the "container" and "member" variables, if the user has specified
  1729     // them. The container variable may be specified with the container
  1730     // attribute on the <template> and the member variable may be specified
  1731     // using the member attribute or the value of the uri attribute inside the
  1732     // first action body in the template. If not specified, the container
  1733     // variable defaults to '?uri' and the member variable defaults to '?' or
  1734     // 'rdf:*' for simple queries.
  1736     // For RDF queries, the container variable may also be set via the
  1737     // <content> tag.
  1739     nsAutoString containervar;
  1740     tmpl->GetAttr(kNameSpaceID_None, nsGkAtoms::container, containervar);
  1742     if (containervar.IsEmpty())
  1743         mRefVariable = do_GetAtom("?uri");
  1744     else
  1745         mRefVariable = do_GetAtom(containervar);
  1747     nsAutoString membervar;
  1748     tmpl->GetAttr(kNameSpaceID_None, nsGkAtoms::member, membervar);
  1750     if (membervar.IsEmpty())
  1751         mMemberVariable = nullptr;
  1752     else
  1753         mMemberVariable = do_GetAtom(membervar);
  1755     nsTemplateQuerySet* queryset = new nsTemplateQuerySet(0);
  1756     if (!queryset)
  1757         return NS_ERROR_OUT_OF_MEMORY;
  1759     if (!mQuerySets.AppendElement(queryset)) {
  1760         delete queryset;
  1761         return NS_ERROR_OUT_OF_MEMORY;
  1764     bool canUseTemplate = false;
  1765     int32_t priority = 0;
  1766     rv = CompileTemplate(tmpl, queryset, false, &priority, &canUseTemplate);
  1768     if (NS_FAILED(rv) || !canUseTemplate) {
  1769         for (int32_t q = mQuerySets.Length() - 1; q >= 0; q--) {
  1770             nsTemplateQuerySet* qs = mQuerySets[q];
  1771             delete qs;
  1773         mQuerySets.Clear();
  1776     mQueriesCompiled = true;
  1778     return NS_OK;
  1781 nsresult
  1782 nsXULTemplateBuilder::CompileTemplate(nsIContent* aTemplate,
  1783                                       nsTemplateQuerySet* aQuerySet,
  1784                                       bool aIsQuerySet,
  1785                                       int32_t* aPriority,
  1786                                       bool* aCanUseTemplate)
  1788     NS_ASSERTION(aQuerySet, "No queryset supplied");
  1790     nsresult rv = NS_OK;
  1792     bool isQuerySetMode = false;
  1793     bool hasQuerySet = false, hasRule = false, hasQuery = false;
  1795     for (nsIContent* rulenode = aTemplate->GetFirstChild();
  1796          rulenode;
  1797          rulenode = rulenode->GetNextSibling()) {
  1799         nsINodeInfo *ni = rulenode->NodeInfo();
  1801         // don't allow more queries than can be supported
  1802         if (*aPriority == INT16_MAX)
  1803             return NS_ERROR_FAILURE;
  1805         // XXXndeakin queryset isn't a good name for this tag since it only
  1806         //            ever contains one query
  1807         if (!aIsQuerySet && ni->Equals(nsGkAtoms::queryset, kNameSpaceID_XUL)) {
  1808             if (hasRule || hasQuery) {
  1809               nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_INVALID_QUERYSET);
  1810               continue;
  1813             isQuerySetMode = true;
  1815             // only create a queryset for those after the first since the
  1816             // first one is always created by CompileQueries
  1817             if (hasQuerySet) {
  1818                 aQuerySet = new nsTemplateQuerySet(++*aPriority);
  1819                 if (!aQuerySet)
  1820                     return NS_ERROR_OUT_OF_MEMORY;
  1822                 // once the queryset is appended to the mQuerySets list, it
  1823                 // will be removed by CompileQueries if an error occurs
  1824                 if (!mQuerySets.AppendElement(aQuerySet)) {
  1825                     delete aQuerySet;
  1826                     return NS_ERROR_OUT_OF_MEMORY;
  1830             hasQuerySet = true;
  1832             rv = CompileTemplate(rulenode, aQuerySet, true, aPriority, aCanUseTemplate);
  1833             if (NS_FAILED(rv))
  1834                 return rv;
  1837         // once a queryset is used, everything must be a queryset
  1838         if (isQuerySetMode)
  1839             continue;
  1841         if (ni->Equals(nsGkAtoms::rule, kNameSpaceID_XUL)) {
  1842             nsCOMPtr<nsIContent> action;
  1843             nsXULContentUtils::FindChildByTag(rulenode,
  1844                                               kNameSpaceID_XUL,
  1845                                               nsGkAtoms::action,
  1846                                               getter_AddRefs(action));
  1848             if (action){
  1849                 nsCOMPtr<nsIAtom> memberVariable = mMemberVariable;
  1850                 if (!memberVariable) {
  1851                     memberVariable = DetermineMemberVariable(action);
  1852                     if (!memberVariable) {
  1853                         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_NO_MEMBERVAR);
  1854                         continue;
  1858                 if (hasQuery) {
  1859                     nsCOMPtr<nsIAtom> tag;
  1860                     DetermineRDFQueryRef(aQuerySet->mQueryNode,
  1861                                          getter_AddRefs(tag));
  1862                     if (tag)
  1863                         aQuerySet->SetTag(tag);
  1865                     if (! aQuerySet->mCompiledQuery) {
  1866                         nsCOMPtr<nsIDOMNode> query(do_QueryInterface(aQuerySet->mQueryNode));
  1868                         rv = mQueryProcessor->CompileQuery(this, query,
  1869                                                            mRefVariable, memberVariable,
  1870                                                            getter_AddRefs(aQuerySet->mCompiledQuery));
  1871                         if (NS_FAILED(rv))
  1872                             return rv;
  1875                     if (aQuerySet->mCompiledQuery) {
  1876                         rv = CompileExtendedQuery(rulenode, action, memberVariable,
  1877                                                   aQuerySet);
  1878                         if (NS_FAILED(rv))
  1879                             return rv;
  1881                         *aCanUseTemplate = true;
  1884                 else {
  1885                     // backwards-compatible RDF template syntax where there is
  1886                     // an <action> node but no <query> node. In this case,
  1887                     // use the conditions as if it was the query.
  1889                     nsCOMPtr<nsIContent> conditions;
  1890                     nsXULContentUtils::FindChildByTag(rulenode,
  1891                                                       kNameSpaceID_XUL,
  1892                                                       nsGkAtoms::conditions,
  1893                                                       getter_AddRefs(conditions));
  1895                     if (conditions) {
  1896                         // create a new queryset if one hasn't been created already
  1897                         if (hasQuerySet) {
  1898                             aQuerySet = new nsTemplateQuerySet(++*aPriority);
  1899                             if (! aQuerySet)
  1900                                 return NS_ERROR_OUT_OF_MEMORY;
  1902                             if (!mQuerySets.AppendElement(aQuerySet)) {
  1903                                 delete aQuerySet;
  1904                                 return NS_ERROR_OUT_OF_MEMORY;
  1908                         nsCOMPtr<nsIAtom> tag;
  1909                         DetermineRDFQueryRef(conditions, getter_AddRefs(tag));
  1910                         if (tag)
  1911                             aQuerySet->SetTag(tag);
  1913                         hasQuerySet = true;
  1915                         nsCOMPtr<nsIDOMNode> conditionsnode(do_QueryInterface(conditions));
  1917                         aQuerySet->mQueryNode = conditions;
  1918                         rv = mQueryProcessor->CompileQuery(this, conditionsnode,
  1919                                                            mRefVariable,
  1920                                                            memberVariable,
  1921                                                            getter_AddRefs(aQuerySet->mCompiledQuery));
  1922                         if (NS_FAILED(rv))
  1923                             return rv;
  1925                         if (aQuerySet->mCompiledQuery) {
  1926                             rv = CompileExtendedQuery(rulenode, action, memberVariable,
  1927                                                       aQuerySet);
  1928                             if (NS_FAILED(rv))
  1929                                 return rv;
  1931                             *aCanUseTemplate = true;
  1936             else {
  1937                 if (hasQuery)
  1938                     continue;
  1940                 // a new queryset must always be created in this case
  1941                 if (hasQuerySet) {
  1942                     aQuerySet = new nsTemplateQuerySet(++*aPriority);
  1943                     if (! aQuerySet)
  1944                         return NS_ERROR_OUT_OF_MEMORY;
  1946                     if (!mQuerySets.AppendElement(aQuerySet)) {
  1947                         delete aQuerySet;
  1948                         return NS_ERROR_OUT_OF_MEMORY;
  1952                 hasQuerySet = true;
  1954                 rv = CompileSimpleQuery(rulenode, aQuerySet, aCanUseTemplate);
  1955                 if (NS_FAILED(rv))
  1956                     return rv;
  1959             hasRule = true;
  1961         else if (ni->Equals(nsGkAtoms::query, kNameSpaceID_XUL)) {
  1962             if (hasQuery)
  1963               continue;
  1965             aQuerySet->mQueryNode = rulenode;
  1966             hasQuery = true;
  1968         else if (ni->Equals(nsGkAtoms::action, kNameSpaceID_XUL)) {
  1969             // the query must appear before the action
  1970             if (! hasQuery)
  1971                 continue;
  1973             nsCOMPtr<nsIAtom> tag;
  1974             DetermineRDFQueryRef(aQuerySet->mQueryNode, getter_AddRefs(tag));
  1975             if (tag)
  1976                 aQuerySet->SetTag(tag);
  1978             nsCOMPtr<nsIAtom> memberVariable = mMemberVariable;
  1979             if (!memberVariable) {
  1980                 memberVariable = DetermineMemberVariable(rulenode);
  1981                 if (!memberVariable) {
  1982                     nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_NO_MEMBERVAR);
  1983                     continue;
  1987             nsCOMPtr<nsIDOMNode> query(do_QueryInterface(aQuerySet->mQueryNode));
  1989             rv = mQueryProcessor->CompileQuery(this, query,
  1990                                                mRefVariable, memberVariable,
  1991                                                getter_AddRefs(aQuerySet->mCompiledQuery));
  1993             if (aQuerySet->mCompiledQuery) {
  1994                 nsTemplateRule* rule = aQuerySet->NewRule(aTemplate, rulenode, aQuerySet);
  1995                 if (! rule)
  1996                     return NS_ERROR_OUT_OF_MEMORY;
  1998                 rule->SetVars(mRefVariable, memberVariable);
  2000                 *aCanUseTemplate = true;
  2002                 return NS_OK;
  2007     if (! hasRule && ! hasQuery && ! hasQuerySet) {
  2008         // if no rules are specified in the template, then the contents of the
  2009         // <template> tag are the one-and-only template.
  2010         rv = CompileSimpleQuery(aTemplate, aQuerySet, aCanUseTemplate);
  2013     return rv;
  2016 nsresult
  2017 nsXULTemplateBuilder::CompileExtendedQuery(nsIContent* aRuleElement,
  2018                                            nsIContent* aActionElement,
  2019                                            nsIAtom* aMemberVariable,
  2020                                            nsTemplateQuerySet* aQuerySet)
  2022     // Compile an "extended" <template> rule. An extended rule may have
  2023     // a <conditions> child, an <action> child, and a <bindings> child.
  2024     nsresult rv;
  2026     nsTemplateRule* rule = aQuerySet->NewRule(aRuleElement, aActionElement, aQuerySet);
  2027     if (! rule)
  2028          return NS_ERROR_OUT_OF_MEMORY;
  2030     nsCOMPtr<nsIContent> conditions;
  2031     nsXULContentUtils::FindChildByTag(aRuleElement,
  2032                                       kNameSpaceID_XUL,
  2033                                       nsGkAtoms::conditions,
  2034                                       getter_AddRefs(conditions));
  2036     // allow the conditions to be placed directly inside the rule
  2037     if (!conditions)
  2038         conditions = aRuleElement;
  2040     rv = CompileConditions(rule, conditions);
  2041     // If the rule compilation failed, then we have to bail.
  2042     if (NS_FAILED(rv)) {
  2043         aQuerySet->RemoveRule(rule);
  2044         return rv;
  2047     rule->SetVars(mRefVariable, aMemberVariable);
  2049     // If we've got bindings, add 'em.
  2050     nsCOMPtr<nsIContent> bindings;
  2051     nsXULContentUtils::FindChildByTag(aRuleElement,
  2052                                       kNameSpaceID_XUL,
  2053                                       nsGkAtoms::bindings,
  2054                                       getter_AddRefs(bindings));
  2056     // allow bindings to be placed directly inside rule
  2057     if (!bindings)
  2058         bindings = aRuleElement;
  2060     rv = CompileBindings(rule, bindings);
  2061     NS_ENSURE_SUCCESS(rv, rv);
  2063     return NS_OK;
  2066 already_AddRefed<nsIAtom>
  2067 nsXULTemplateBuilder::DetermineMemberVariable(nsIContent* aElement)
  2069     // recursively iterate over the children looking for an element
  2070     // with uri="?..."
  2071     for (nsIContent* child = aElement->GetFirstChild();
  2072          child;
  2073          child = child->GetNextSibling()) {
  2074         nsAutoString uri;
  2075         child->GetAttr(kNameSpaceID_None, nsGkAtoms::uri, uri);
  2076         if (!uri.IsEmpty() && uri[0] == char16_t('?')) {
  2077             return NS_NewAtom(uri);
  2080         nsCOMPtr<nsIAtom> result = DetermineMemberVariable(child);
  2081         if (result) {
  2082             return result.forget();
  2086     return nullptr;
  2089 void
  2090 nsXULTemplateBuilder::DetermineRDFQueryRef(nsIContent* aQueryElement, nsIAtom** aTag)
  2092     // check for a tag
  2093     nsCOMPtr<nsIContent> content;
  2094     nsXULContentUtils::FindChildByTag(aQueryElement,
  2095                                       kNameSpaceID_XUL,
  2096                                       nsGkAtoms::content,
  2097                                       getter_AddRefs(content));
  2099     if (! content) {
  2100         // look for older treeitem syntax as well
  2101         nsXULContentUtils::FindChildByTag(aQueryElement,
  2102                                           kNameSpaceID_XUL,
  2103                                           nsGkAtoms::treeitem,
  2104                                           getter_AddRefs(content));
  2107     if (content) {
  2108         nsAutoString uri;
  2109         content->GetAttr(kNameSpaceID_None, nsGkAtoms::uri, uri);
  2111         if (!uri.IsEmpty())
  2112             mRefVariable = do_GetAtom(uri);
  2114         nsAutoString tag;
  2115         content->GetAttr(kNameSpaceID_None, nsGkAtoms::tag, tag);
  2117         if (!tag.IsEmpty())
  2118             *aTag = NS_NewAtom(tag).take();
  2122 nsresult
  2123 nsXULTemplateBuilder::CompileSimpleQuery(nsIContent* aRuleElement,
  2124                                          nsTemplateQuerySet* aQuerySet,
  2125                                          bool* aCanUseTemplate)
  2127     // compile a simple query, which is a query with no <query> or
  2128     // <conditions>. This means that a default query is used.
  2129     nsCOMPtr<nsIDOMNode> query(do_QueryInterface(aRuleElement));
  2131     nsCOMPtr<nsIAtom> memberVariable;
  2132     if (mMemberVariable)
  2133         memberVariable = mMemberVariable;
  2134     else
  2135         memberVariable = do_GetAtom("rdf:*");
  2137     // since there is no <query> node for a simple query, the query node will
  2138     // be either the <rule> node if multiple rules are used, or the <template> node.
  2139     aQuerySet->mQueryNode = aRuleElement;
  2140     nsresult rv = mQueryProcessor->CompileQuery(this, query,
  2141                                                 mRefVariable, memberVariable,
  2142                                                 getter_AddRefs(aQuerySet->mCompiledQuery));
  2143     if (NS_FAILED(rv))
  2144         return rv;
  2146     if (! aQuerySet->mCompiledQuery) {
  2147         *aCanUseTemplate = false;
  2148         return NS_OK;
  2151     nsTemplateRule* rule = aQuerySet->NewRule(aRuleElement, aRuleElement, aQuerySet);
  2152     if (! rule)
  2153         return NS_ERROR_OUT_OF_MEMORY;
  2155     rule->SetVars(mRefVariable, memberVariable);
  2157     nsAutoString tag;
  2158     aRuleElement->GetAttr(kNameSpaceID_None, nsGkAtoms::parent, tag);
  2160     if (!tag.IsEmpty()) {
  2161         nsCOMPtr<nsIAtom> tagatom = do_GetAtom(tag);
  2162         aQuerySet->SetTag(tagatom);
  2165     *aCanUseTemplate = true;
  2167     return AddSimpleRuleBindings(rule, aRuleElement);
  2170 nsresult
  2171 nsXULTemplateBuilder::CompileConditions(nsTemplateRule* aRule,
  2172                                         nsIContent* aCondition)
  2174     nsAutoString tag;
  2175     aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::parent, tag);
  2177     if (!tag.IsEmpty()) {
  2178         nsCOMPtr<nsIAtom> tagatom = do_GetAtom(tag);
  2179         aRule->SetTag(tagatom);
  2182     nsTemplateCondition* currentCondition = nullptr;
  2184     for (nsIContent* node = aCondition->GetFirstChild();
  2185          node;
  2186          node = node->GetNextSibling()) {
  2188         if (node->NodeInfo()->Equals(nsGkAtoms::where, kNameSpaceID_XUL)) {
  2189             nsresult rv = CompileWhereCondition(aRule, node, &currentCondition);
  2190             if (NS_FAILED(rv))
  2191                 return rv;
  2195     return NS_OK;
  2198 nsresult
  2199 nsXULTemplateBuilder::CompileWhereCondition(nsTemplateRule* aRule,
  2200                                             nsIContent* aCondition,
  2201                                             nsTemplateCondition** aCurrentCondition)
  2203     // Compile a <where> condition, which must be of the form:
  2204     //
  2205     //   <where subject="?var1|string" rel="relation" value="?var2|string" />
  2206     //
  2207     //    The value of rel may be:
  2208     //      equal - subject must be equal to object
  2209     //      notequal - subject must not be equal to object
  2210     //      less - subject must be less than object
  2211     //      greater - subject must be greater than object
  2212     //      startswith - subject must start with object
  2213     //      endswith - subject must end with object
  2214     //      contains - subject must contain object
  2215     //    Comparisons are done as strings unless the subject is an integer.
  2217     // subject
  2218     nsAutoString subject;
  2219     aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::subject, subject);
  2220     if (subject.IsEmpty()) {
  2221         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_WHERE_NO_SUBJECT);
  2222         return NS_OK;
  2225     nsCOMPtr<nsIAtom> svar;
  2226     if (subject[0] == char16_t('?'))
  2227         svar = do_GetAtom(subject);
  2229     nsAutoString relstring;
  2230     aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, relstring);
  2231     if (relstring.IsEmpty()) {
  2232         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_WHERE_NO_RELATION);
  2233         return NS_OK;
  2236     // object
  2237     nsAutoString value;
  2238     aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::value, value);
  2239     if (value.IsEmpty()) {
  2240         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_WHERE_NO_VALUE);
  2241         return NS_OK;
  2244     // multiple
  2245     bool shouldMultiple =
  2246       aCondition->AttrValueIs(kNameSpaceID_None, nsGkAtoms::multiple,
  2247                               nsGkAtoms::_true, eCaseMatters);
  2249     nsCOMPtr<nsIAtom> vvar;
  2250     if (!shouldMultiple && (value[0] == char16_t('?'))) {
  2251         vvar = do_GetAtom(value);
  2254     // ignorecase
  2255     bool shouldIgnoreCase =
  2256       aCondition->AttrValueIs(kNameSpaceID_None, nsGkAtoms::ignorecase,
  2257                               nsGkAtoms::_true, eCaseMatters);
  2259     // negate
  2260     bool shouldNegate =
  2261       aCondition->AttrValueIs(kNameSpaceID_None, nsGkAtoms::negate,
  2262                               nsGkAtoms::_true, eCaseMatters);
  2264     nsTemplateCondition* condition;
  2266     if (svar && vvar) {
  2267         condition = new nsTemplateCondition(svar, relstring, vvar,
  2268                                             shouldIgnoreCase, shouldNegate);
  2270     else if (svar && !value.IsEmpty()) {
  2271         condition = new nsTemplateCondition(svar, relstring, value,
  2272                                             shouldIgnoreCase, shouldNegate, shouldMultiple);
  2274     else if (vvar) {
  2275         condition = new nsTemplateCondition(subject, relstring, vvar,
  2276                                             shouldIgnoreCase, shouldNegate);
  2278     else {
  2279         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_WHERE_NO_VAR);
  2280         return NS_OK;
  2283     if (! condition)
  2284         return NS_ERROR_OUT_OF_MEMORY;
  2286     if (*aCurrentCondition) {
  2287         (*aCurrentCondition)->SetNext(condition);
  2289     else {
  2290         aRule->SetCondition(condition);
  2293     *aCurrentCondition = condition;
  2295     return NS_OK;
  2298 nsresult
  2299 nsXULTemplateBuilder::CompileBindings(nsTemplateRule* aRule, nsIContent* aBindings)
  2301     // Add an extended rule's bindings.
  2302     nsresult rv;
  2304     for (nsIContent* binding = aBindings->GetFirstChild();
  2305          binding;
  2306          binding = binding->GetNextSibling()) {
  2308         if (binding->NodeInfo()->Equals(nsGkAtoms::binding,
  2309                                         kNameSpaceID_XUL)) {
  2310             rv = CompileBinding(aRule, binding);
  2311             if (NS_FAILED(rv))
  2312                 return rv;
  2316     aRule->AddBindingsToQueryProcessor(mQueryProcessor);
  2318     return NS_OK;
  2322 nsresult
  2323 nsXULTemplateBuilder::CompileBinding(nsTemplateRule* aRule,
  2324                                      nsIContent* aBinding)
  2326     // Compile a <binding> "condition", which must be of the form:
  2327     //
  2328     //   <binding subject="?var1"
  2329     //            predicate="resource"
  2330     //            object="?var2" />
  2331     //
  2332     // XXXwaterson Some day it would be cool to allow the 'predicate'
  2333     // to be bound to a variable.
  2335     // subject
  2336     nsAutoString subject;
  2337     aBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::subject, subject);
  2338     if (subject.IsEmpty()) {
  2339         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_SUBJECT);
  2340         return NS_OK;
  2343     nsCOMPtr<nsIAtom> svar;
  2344     if (subject[0] == char16_t('?')) {
  2345         svar = do_GetAtom(subject);
  2347     else {
  2348         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_SUBJECT);
  2349         return NS_OK;
  2352     // predicate
  2353     nsAutoString predicate;
  2354     aBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::predicate, predicate);
  2355     if (predicate.IsEmpty()) {
  2356         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_PREDICATE);
  2357         return NS_OK;
  2360     // object
  2361     nsAutoString object;
  2362     aBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::object, object);
  2364     if (object.IsEmpty()) {
  2365         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_OBJECT);
  2366         return NS_OK;
  2369     nsCOMPtr<nsIAtom> ovar;
  2370     if (object[0] == char16_t('?')) {
  2371         ovar = do_GetAtom(object);
  2373     else {
  2374         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_OBJECT);
  2375         return NS_OK;
  2378     return aRule->AddBinding(svar, predicate, ovar);
  2381 nsresult
  2382 nsXULTemplateBuilder::AddSimpleRuleBindings(nsTemplateRule* aRule,
  2383                                             nsIContent* aElement)
  2385     // Crawl the content tree of a "simple" rule, adding a variable
  2386     // assignment for any attribute whose value is "rdf:".
  2388     nsAutoTArray<nsIContent*, 8> elements;
  2390     if (elements.AppendElement(aElement) == nullptr)
  2391         return NS_ERROR_OUT_OF_MEMORY;
  2393     while (elements.Length()) {
  2394         // Pop the next element off the stack
  2395         uint32_t i = elements.Length() - 1;
  2396         nsIContent* element = elements[i];
  2397         elements.RemoveElementAt(i);
  2399         // Iterate through its attributes, looking for substitutions
  2400         // that we need to add as bindings.
  2401         uint32_t count = element->GetAttrCount();
  2403         for (i = 0; i < count; ++i) {
  2404             const nsAttrName* name = element->GetAttrNameAt(i);
  2406             if (!name->Equals(nsGkAtoms::id, kNameSpaceID_None) &&
  2407                 !name->Equals(nsGkAtoms::uri, kNameSpaceID_None)) {
  2408                 nsAutoString value;
  2409                 element->GetAttr(name->NamespaceID(), name->LocalName(), value);
  2411                 // Scan the attribute for variables, adding a binding for
  2412                 // each one.
  2413                 ParseAttribute(value, AddBindingsFor, nullptr, aRule);
  2417         // Push kids onto the stack, and search them next.
  2418         for (nsIContent* child = element->GetLastChild();
  2419              child;
  2420              child = child->GetPreviousSibling()) {
  2422             if (!elements.AppendElement(child))
  2423                 return NS_ERROR_OUT_OF_MEMORY;
  2427     aRule->AddBindingsToQueryProcessor(mQueryProcessor);
  2429     return NS_OK;
  2432 void
  2433 nsXULTemplateBuilder::AddBindingsFor(nsXULTemplateBuilder* aThis,
  2434                                      const nsAString& aVariable,
  2435                                      void* aClosure)
  2437     // We should *only* be recieving "rdf:"-style variables. Make
  2438     // sure...
  2439     if (!StringBeginsWith(aVariable, NS_LITERAL_STRING("rdf:")))
  2440         return;
  2442     nsTemplateRule* rule = static_cast<nsTemplateRule*>(aClosure);
  2444     nsCOMPtr<nsIAtom> var = do_GetAtom(aVariable);
  2446     // Strip it down to the raw RDF property by clobbering the "rdf:"
  2447     // prefix
  2448     nsAutoString property;
  2449     property.Assign(Substring(aVariable, uint32_t(4), aVariable.Length() - 4));
  2451     if (! rule->HasBinding(rule->GetMemberVariable(), property, var))
  2452         // In the simple syntax, the binding is always from the
  2453         // member variable, through the property, to the target.
  2454         rule->AddBinding(rule->GetMemberVariable(), property, var);
  2458 nsresult
  2459 nsXULTemplateBuilder::IsSystemPrincipal(nsIPrincipal *principal, bool *result)
  2461   if (!gSystemPrincipal)
  2462     return NS_ERROR_UNEXPECTED;
  2464   *result = (principal == gSystemPrincipal);
  2465   return NS_OK;
  2468 bool
  2469 nsXULTemplateBuilder::IsActivated(nsIRDFResource *aResource)
  2471     for (ActivationEntry *entry = mTop;
  2472          entry != nullptr;
  2473          entry = entry->mPrevious) {
  2474         if (entry->mResource == aResource)
  2475             return true;
  2477     return false;
  2480 nsresult
  2481 nsXULTemplateBuilder::GetResultResource(nsIXULTemplateResult* aResult,
  2482                                         nsIRDFResource** aResource)
  2484     // get the resource for a result by checking its resource property. If it
  2485     // is not set, check the id. This allows non-chrome implementations to
  2486     // avoid having to use RDF.
  2487     nsresult rv = aResult->GetResource(aResource);
  2488     if (NS_FAILED(rv))
  2489         return rv;
  2491     if (! *aResource) {
  2492         nsAutoString id;
  2493         rv = aResult->GetId(id);
  2494         if (NS_FAILED(rv))
  2495             return rv;
  2497         return gRDFService->GetUnicodeResource(id, aResource);
  2500     return rv;
  2504 void
  2505 nsXULTemplateBuilder::OutputMatchToLog(nsIRDFResource* aId,
  2506                                        nsTemplateMatch* aMatch,
  2507                                        bool aIsNew)
  2509     int32_t priority = aMatch->QuerySetPriority() + 1;
  2510     int32_t activePriority = -1;
  2512     nsAutoString msg;
  2514     nsAutoString templateid;
  2515     mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::id, templateid);
  2516     msg.AppendLiteral("In template");
  2517     if (!templateid.IsEmpty()) {
  2518         msg.AppendLiteral(" with id ");
  2519         msg.Append(templateid);
  2522     nsAutoString refstring;
  2523     aMatch->mResult->GetBindingFor(mRefVariable, refstring);
  2524     if (!refstring.IsEmpty()) {
  2525         msg.AppendLiteral(" using ref ");
  2526         msg.Append(refstring);
  2529     msg.AppendLiteral("\n    ");
  2531     nsTemplateMatch* match = nullptr;
  2532     if (mMatchMap.Get(aId, &match)){
  2533         while (match) {
  2534             if (match == aMatch)
  2535                 break;
  2536             if (match->IsActive() &&
  2537                 match->GetContainer() == aMatch->GetContainer()) {
  2538                 activePriority = match->QuerySetPriority() + 1;
  2539                 break;
  2541             match = match->mNext;
  2545     if (aMatch->IsActive()) {
  2546         if (aIsNew) {
  2547             msg.AppendLiteral("New active result for query ");
  2548             msg.AppendInt(priority);
  2549             msg.AppendLiteral(" matching rule ");
  2550             msg.AppendInt(aMatch->RuleIndex() + 1);
  2552         else {
  2553             msg.AppendLiteral("Removed active result for query ");
  2554             msg.AppendInt(priority);
  2555             if (activePriority > 0) {
  2556                 msg.AppendLiteral(" (new active query is ");
  2557                 msg.AppendInt(activePriority);
  2558                 msg.Append(')');
  2560             else {
  2561                 msg.AppendLiteral(" (no new active query)");
  2565     else {
  2566         if (aIsNew) {
  2567             msg.AppendLiteral("New inactive result for query ");
  2568             msg.AppendInt(priority);
  2569             if (activePriority > 0) {
  2570                 msg.AppendLiteral(" (overridden by query ");
  2571                 msg.AppendInt(activePriority);
  2572                 msg.Append(')');
  2574             else {
  2575                 msg.AppendLiteral(" (didn't match a rule)");
  2578         else {
  2579             msg.AppendLiteral("Removed inactive result for query ");
  2580             msg.AppendInt(priority);
  2581             if (activePriority > 0) {
  2582                 msg.AppendLiteral(" (active query is ");
  2583                 msg.AppendInt(activePriority);
  2584                 msg.Append(')');
  2586             else {
  2587                 msg.AppendLiteral(" (no active query)");
  2592     nsAutoString idstring;
  2593     nsXULContentUtils::GetTextForNode(aId, idstring);
  2594     msg.AppendLiteral(": ");
  2595     msg.Append(idstring);
  2597     nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
  2598     if (cs)
  2599       cs->LogStringMessage(msg.get());

mercurial