LCOV - code coverage report
Current view: directory - content/xul/templates/src - nsXULTemplateBuilder.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 1167 2 0.2 %
Date: 2012-06-02 Functions: 69 2 2.9 %

       1                 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2                 : /* ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is Mozilla Communicator client code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * Netscape Communications Corporation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 1998
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Robert Churchill <rjc@netscape.com>
      24                 :  *   David Hyatt <hyatt@netscape.com>
      25                 :  *   Chris Waterson <waterson@netscape.com>
      26                 :  *   Pierre Phaneuf <pp@ludusdesign.com>
      27                 :  *   Joe Hewitt <hewitt@netscape.com>
      28                 :  *   Neil Deakin <enndeakin@sympatico.ca>
      29                 :  *   Laurent Jouanneau <laurent.jouanneau@disruptive-innovations.com>
      30                 :  *
      31                 :  * Alternatively, the contents of this file may be used under the terms of
      32                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      33                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      34                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      35                 :  * of those above. If you wish to allow use of your version of this file only
      36                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      37                 :  * use your version of this file under the terms of the MPL, indicate your
      38                 :  * decision by deleting the provisions above and replace them with the notice
      39                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      40                 :  * the provisions above, a recipient may use your version of this file under
      41                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      42                 :  *
      43                 :  * ***** END LICENSE BLOCK ***** */
      44                 : 
      45                 : /*
      46                 : 
      47                 :   Builds content from a datasource using the XUL <template> tag.
      48                 : 
      49                 :   TO DO
      50                 : 
      51                 :   . Fix ContentTagTest's location in the network construction
      52                 : 
      53                 :   To turn on logging for this module, set:
      54                 : 
      55                 :     NSPR_LOG_MODULES nsXULTemplateBuilder:5
      56                 : 
      57                 :  */
      58                 : 
      59                 : #include "nsCOMPtr.h"
      60                 : #include "nsCRT.h"
      61                 : #include "nsFixedSizeAllocator.h"
      62                 : #include "nsIContent.h"
      63                 : #include "nsIDOMElement.h"
      64                 : #include "nsIDOMNode.h"
      65                 : #include "nsIDOMDocument.h"
      66                 : #include "nsIDOMXMLDocument.h"
      67                 : #include "nsIDOMXULElement.h"
      68                 : #include "nsIDocument.h"
      69                 : #include "nsBindingManager.h"
      70                 : #include "nsIDOMNodeList.h"
      71                 : #include "nsINameSpaceManager.h"
      72                 : #include "nsIObserverService.h"
      73                 : #include "nsIRDFCompositeDataSource.h"
      74                 : #include "nsIRDFInferDataSource.h"
      75                 : #include "nsIRDFContainerUtils.h"
      76                 : #include "nsIXULDocument.h"
      77                 : #include "nsIXULTemplateBuilder.h"
      78                 : #include "nsIXULBuilderListener.h"
      79                 : #include "nsIRDFRemoteDataSource.h"
      80                 : #include "nsIRDFService.h"
      81                 : #include "nsIScriptGlobalObject.h"
      82                 : #include "nsIServiceManager.h"
      83                 : #include "nsISimpleEnumerator.h"
      84                 : #include "nsISupportsArray.h"
      85                 : #include "nsIMutableArray.h"
      86                 : #include "nsIURL.h"
      87                 : #include "nsIXPConnect.h"
      88                 : #include "nsContentCID.h"
      89                 : #include "nsRDFCID.h"
      90                 : #include "nsXULContentUtils.h"
      91                 : #include "nsString.h"
      92                 : #include "nsTArray.h"
      93                 : #include "nsXPIDLString.h"
      94                 : #include "nsWhitespaceTokenizer.h"
      95                 : #include "nsGkAtoms.h"
      96                 : #include "nsXULElement.h"
      97                 : #include "jsapi.h"
      98                 : #include "prlog.h"
      99                 : #include "rdf.h"
     100                 : #include "pldhash.h"
     101                 : #include "plhash.h"
     102                 : #include "nsDOMClassInfoID.h"
     103                 : #include "nsPIDOMWindow.h"
     104                 : #include "nsIConsoleService.h" 
     105                 : #include "nsNetUtil.h"
     106                 : #include "nsXULTemplateBuilder.h"
     107                 : #include "nsXULTemplateQueryProcessorRDF.h"
     108                 : #include "nsXULTemplateQueryProcessorXML.h"
     109                 : #include "nsXULTemplateQueryProcessorStorage.h"
     110                 : #include "nsContentUtils.h"
     111                 : 
     112                 : using namespace mozilla::dom;
     113                 : 
     114                 : //----------------------------------------------------------------------
     115                 : 
     116                 : static NS_DEFINE_CID(kRDFContainerUtilsCID,      NS_RDFCONTAINERUTILS_CID);
     117                 : static NS_DEFINE_CID(kRDFServiceCID,             NS_RDFSERVICE_CID);
     118                 : 
     119                 : //----------------------------------------------------------------------
     120                 : //
     121                 : // nsXULTemplateBuilder
     122                 : //
     123                 : 
     124                 : nsrefcnt                  nsXULTemplateBuilder::gRefCnt = 0;
     125                 : nsIRDFService*            nsXULTemplateBuilder::gRDFService;
     126                 : nsIRDFContainerUtils*     nsXULTemplateBuilder::gRDFContainerUtils;
     127                 : nsIScriptSecurityManager* nsXULTemplateBuilder::gScriptSecurityManager;
     128                 : nsIPrincipal*             nsXULTemplateBuilder::gSystemPrincipal;
     129                 : nsIObserverService*       nsXULTemplateBuilder::gObserverService;
     130                 : 
     131                 : #ifdef PR_LOGGING
     132                 : PRLogModuleInfo* gXULTemplateLog;
     133                 : #endif
     134                 : 
     135                 : #define NS_QUERY_PROCESSOR_CONTRACTID_PREFIX "@mozilla.org/xul/xul-query-processor;1?name="
     136                 : 
     137                 : //----------------------------------------------------------------------
     138                 : //
     139                 : // nsXULTemplateBuilder methods
     140                 : //
     141                 : 
     142               0 : nsXULTemplateBuilder::nsXULTemplateBuilder(void)
     143                 :     : mQueriesCompiled(false),
     144                 :       mFlags(0),
     145                 :       mTop(nsnull),
     146               0 :       mObservedDocument(nsnull)
     147                 : {
     148               0 : }
     149                 : 
     150                 : static PLDHashOperator
     151               0 : DestroyMatchList(nsISupports* aKey, nsTemplateMatch*& aMatch, void* aContext)
     152                 : {
     153               0 :     nsFixedSizeAllocator* pool = static_cast<nsFixedSizeAllocator *>(aContext);
     154                 : 
     155                 :     // delete all the matches in the list
     156               0 :     while (aMatch) {
     157               0 :         nsTemplateMatch* next = aMatch->mNext;
     158               0 :         nsTemplateMatch::Destroy(*pool, aMatch, true);
     159               0 :         aMatch = next;
     160                 :     }
     161                 : 
     162               0 :     return PL_DHASH_REMOVE;
     163                 : }
     164                 : 
     165               0 : nsXULTemplateBuilder::~nsXULTemplateBuilder(void)
     166                 : {
     167               0 :     Uninit(true);
     168                 : 
     169               0 :     if (--gRefCnt == 0) {
     170               0 :         NS_IF_RELEASE(gRDFService);
     171               0 :         NS_IF_RELEASE(gRDFContainerUtils);
     172               0 :         NS_IF_RELEASE(gSystemPrincipal);
     173               0 :         NS_IF_RELEASE(gScriptSecurityManager);
     174               0 :         NS_IF_RELEASE(gObserverService);
     175                 :     }
     176               0 : }
     177                 : 
     178                 : 
     179                 : nsresult
     180               0 : nsXULTemplateBuilder::InitGlobals()
     181                 : {
     182                 :     nsresult rv;
     183                 : 
     184               0 :     if (gRefCnt++ == 0) {
     185                 :         // Initialize the global shared reference to the service
     186                 :         // manager and get some shared resource objects.
     187               0 :         rv = CallGetService(kRDFServiceCID, &gRDFService);
     188               0 :         if (NS_FAILED(rv))
     189               0 :             return rv;
     190                 : 
     191               0 :         rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils);
     192               0 :         if (NS_FAILED(rv))
     193               0 :             return rv;
     194                 : 
     195                 :         rv = CallGetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID,
     196               0 :                             &gScriptSecurityManager);
     197               0 :         if (NS_FAILED(rv))
     198               0 :             return rv;
     199                 : 
     200               0 :         rv = gScriptSecurityManager->GetSystemPrincipal(&gSystemPrincipal);
     201               0 :         if (NS_FAILED(rv))
     202               0 :             return rv;
     203                 : 
     204               0 :         rv = CallGetService(NS_OBSERVERSERVICE_CONTRACTID, &gObserverService);
     205               0 :         if (NS_FAILED(rv))
     206               0 :             return rv;
     207                 :     }
     208                 : 
     209                 : #ifdef PR_LOGGING
     210               0 :     if (! gXULTemplateLog)
     211               0 :         gXULTemplateLog = PR_NewLogModule("nsXULTemplateBuilder");
     212                 : #endif
     213                 : 
     214               0 :     if (!mMatchMap.IsInitialized() && !mMatchMap.Init())
     215               0 :         return NS_ERROR_OUT_OF_MEMORY;
     216                 : 
     217               0 :     const size_t bucketsizes[] = { sizeof(nsTemplateMatch) };
     218               0 :     return mPool.Init("nsXULTemplateBuilder", bucketsizes, 1, 256);
     219                 : }
     220                 : 
     221                 : 
     222                 : void
     223               0 : nsXULTemplateBuilder::Uninit(bool aIsFinal)
     224                 : {
     225               0 :     if (mObservedDocument && aIsFinal) {
     226               0 :         gObserverService->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
     227               0 :         gObserverService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
     228               0 :         mObservedDocument->RemoveObserver(this);
     229               0 :         mObservedDocument = nsnull;
     230                 :     }
     231                 : 
     232               0 :     if (mQueryProcessor)
     233               0 :         mQueryProcessor->Done();
     234                 : 
     235               0 :     for (PRInt32 q = mQuerySets.Length() - 1; q >= 0; q--) {
     236               0 :         nsTemplateQuerySet* qs = mQuerySets[q];
     237               0 :         delete qs;
     238                 :     }
     239                 : 
     240               0 :     mQuerySets.Clear();
     241                 : 
     242               0 :     mMatchMap.Enumerate(DestroyMatchList, &mPool);
     243                 : 
     244               0 :     mRootResult = nsnull;
     245               0 :     mRefVariable = nsnull;
     246               0 :     mMemberVariable = nsnull;
     247                 : 
     248               0 :     mQueriesCompiled = false;
     249               0 : }
     250                 : 
     251                 : static PLDHashOperator
     252               0 : TraverseMatchList(nsISupports* aKey, nsTemplateMatch* aMatch, void* aContext)
     253                 : {
     254                 :     nsCycleCollectionTraversalCallback *cb =
     255               0 :         static_cast<nsCycleCollectionTraversalCallback*>(aContext);
     256                 : 
     257               0 :     cb->NoteXPCOMChild(aKey);
     258               0 :     nsTemplateMatch* match = aMatch;
     259               0 :     while (match) {
     260               0 :         cb->NoteXPCOMChild(match->GetContainer());
     261               0 :         cb->NoteXPCOMChild(match->mResult);
     262               0 :         match = match->mNext;
     263                 :     }
     264                 : 
     265               0 :     return PL_DHASH_NEXT;
     266                 : }
     267                 : 
     268            1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULTemplateBuilder)
     269               0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULTemplateBuilder)
     270               0 :     NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDataSource)
     271               0 :     NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDB)
     272               0 :     NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCompDB)
     273               0 :     NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRoot)
     274               0 :     NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mRootResult)
     275               0 :     NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMARRAY(mListeners)
     276               0 :     NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mQueryProcessor)
     277               0 :     if (tmp->mMatchMap.IsInitialized()) {
     278               0 :       tmp->mMatchMap.Enumerate(DestroyMatchList, &(tmp->mPool));
     279                 :     }
     280               0 :     for (PRUint32 i = 0; i < tmp->mQuerySets.Length(); ++i) {
     281               0 :         nsTemplateQuerySet* qs = tmp->mQuerySets[i];
     282               0 :         delete qs;
     283                 :     }
     284               0 :     tmp->mQuerySets.Clear();
     285               0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
     286               0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULTemplateBuilder)
     287               0 :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDataSource)
     288               0 :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDB)
     289               0 :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCompDB)
     290               0 :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mRoot)
     291               0 :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mRootResult)
     292               0 :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mListeners)
     293               0 :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mQueryProcessor)
     294               0 :     if (tmp->mMatchMap.IsInitialized())
     295               0 :         tmp->mMatchMap.EnumerateRead(TraverseMatchList, &cb);
     296                 :     {
     297               0 :       PRUint32 i, count = tmp->mQuerySets.Length();
     298               0 :       for (i = 0; i < count; ++i) {
     299               0 :         nsTemplateQuerySet *set = tmp->mQuerySets[i];
     300               0 :         cb.NoteXPCOMChild(set->mQueryNode);
     301               0 :         cb.NoteXPCOMChild(set->mCompiledQuery);
     302               0 :         PRUint16 j, rulesCount = set->RuleCount();
     303               0 :         for (j = 0; j < rulesCount; ++j) {
     304               0 :           set->GetRuleAt(j)->Traverse(cb);
     305                 :         }
     306                 :       }
     307                 :     }
     308               0 :     tmp->Traverse(cb);
     309               0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     310                 : 
     311               0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULTemplateBuilder)
     312               0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULTemplateBuilder)
     313                 : 
     314                 : DOMCI_DATA(XULTemplateBuilder, nsXULTemplateBuilder)
     315                 : 
     316               0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULTemplateBuilder)
     317               0 :   NS_INTERFACE_MAP_ENTRY(nsIXULTemplateBuilder)
     318               0 :   NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
     319               0 :   NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
     320               0 :   NS_INTERFACE_MAP_ENTRY(nsIObserver)
     321               0 :   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULTemplateBuilder)
     322               0 :   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XULTemplateBuilder)
     323               0 : NS_INTERFACE_MAP_END
     324                 : 
     325                 : //----------------------------------------------------------------------
     326                 : //
     327                 : // nsIXULTemplateBuilder methods
     328                 : //
     329                 : 
     330                 : NS_IMETHODIMP
     331               0 : nsXULTemplateBuilder::GetRoot(nsIDOMElement** aResult)
     332                 : {
     333               0 :     if (mRoot) {
     334               0 :         return CallQueryInterface(mRoot, aResult);
     335                 :     }
     336               0 :     *aResult = nsnull;
     337               0 :     return NS_OK;
     338                 : }
     339                 : 
     340                 : NS_IMETHODIMP
     341               0 : nsXULTemplateBuilder::GetDatasource(nsISupports** aResult)
     342                 : {
     343               0 :     if (mCompDB)
     344               0 :         NS_ADDREF(*aResult = mCompDB);
     345                 :     else
     346               0 :         NS_IF_ADDREF(*aResult = mDataSource);
     347               0 :     return NS_OK;
     348                 : }
     349                 : 
     350                 : NS_IMETHODIMP
     351               0 : nsXULTemplateBuilder::SetDatasource(nsISupports* aResult)
     352                 : {
     353               0 :     mDataSource = aResult;
     354               0 :     mCompDB = do_QueryInterface(mDataSource);
     355                 : 
     356               0 :     return Rebuild();
     357                 : }
     358                 : 
     359                 : NS_IMETHODIMP
     360               0 : nsXULTemplateBuilder::GetDatabase(nsIRDFCompositeDataSource** aResult)
     361                 : {
     362               0 :     NS_IF_ADDREF(*aResult = mCompDB);
     363               0 :     return NS_OK;
     364                 : }
     365                 : 
     366                 : NS_IMETHODIMP
     367               0 : nsXULTemplateBuilder::GetQueryProcessor(nsIXULTemplateQueryProcessor** aResult)
     368                 : {
     369               0 :     NS_IF_ADDREF(*aResult = mQueryProcessor.get());
     370               0 :     return NS_OK;
     371                 : }
     372                 : 
     373                 : NS_IMETHODIMP
     374               0 : nsXULTemplateBuilder::AddRuleFilter(nsIDOMNode* aRule, nsIXULTemplateRuleFilter* aFilter)
     375                 : {
     376               0 :     if (!aRule || !aFilter)
     377               0 :         return NS_ERROR_NULL_POINTER;
     378                 : 
     379                 :     // a custom rule filter may be added, one for each rule. If a new one is
     380                 :     // added, it replaces the old one. Look for the right rule and set its
     381                 :     // filter
     382                 : 
     383               0 :     PRInt32 count = mQuerySets.Length();
     384               0 :     for (PRInt32 q = 0; q < count; q++) {
     385               0 :         nsTemplateQuerySet* queryset = mQuerySets[q];
     386                 : 
     387               0 :         PRInt16 rulecount = queryset->RuleCount();
     388               0 :         for (PRInt16 r = 0; r < rulecount; r++) {
     389               0 :             nsTemplateRule* rule = queryset->GetRuleAt(r);
     390                 : 
     391               0 :             nsCOMPtr<nsIDOMNode> rulenode;
     392               0 :             rule->GetRuleNode(getter_AddRefs(rulenode));
     393               0 :             if (aRule == rulenode) {
     394               0 :                 rule->SetRuleFilter(aFilter);
     395               0 :                 return NS_OK;
     396                 :             }
     397                 :         }
     398                 :     }
     399                 : 
     400               0 :     return NS_OK;
     401                 : }
     402                 : 
     403                 : NS_IMETHODIMP
     404               0 : nsXULTemplateBuilder::Rebuild()
     405                 : {
     406                 :     PRInt32 i;
     407                 : 
     408               0 :     for (i = mListeners.Count() - 1; i >= 0; --i) {
     409               0 :         mListeners[i]->WillRebuild(this);
     410                 :     }
     411                 : 
     412               0 :     nsresult rv = RebuildAll();
     413                 : 
     414               0 :     for (i = mListeners.Count() - 1; i >= 0; --i) {
     415               0 :         mListeners[i]->DidRebuild(this);
     416                 :     }
     417                 : 
     418               0 :     return rv;
     419                 : }
     420                 : 
     421                 : NS_IMETHODIMP
     422               0 : nsXULTemplateBuilder::Refresh()
     423                 : {
     424                 :     nsresult rv;
     425                 : 
     426               0 :     if (!mCompDB)
     427               0 :         return NS_ERROR_FAILURE;
     428                 : 
     429               0 :     nsCOMPtr<nsISimpleEnumerator> dslist;
     430               0 :     rv = mCompDB->GetDataSources(getter_AddRefs(dslist));
     431               0 :     NS_ENSURE_SUCCESS(rv, rv);
     432                 : 
     433                 :     bool hasMore;
     434               0 :     nsCOMPtr<nsISupports> next;
     435               0 :     nsCOMPtr<nsIRDFRemoteDataSource> rds;
     436                 : 
     437               0 :     while(NS_SUCCEEDED(dslist->HasMoreElements(&hasMore)) && hasMore) {
     438               0 :         dslist->GetNext(getter_AddRefs(next));
     439               0 :         if (next && (rds = do_QueryInterface(next))) {
     440               0 :             rds->Refresh(false);
     441                 :         }
     442                 :     }
     443                 : 
     444                 :     // XXXbsmedberg: it would be kinda nice to install an async nsIRDFXMLSink
     445                 :     // observer and call rebuild() once the load is complete. See bug 254600.
     446                 : 
     447               0 :     return NS_OK;
     448                 : }
     449                 : 
     450                 : NS_IMETHODIMP
     451               0 : nsXULTemplateBuilder::Init(nsIContent* aElement)
     452                 : {
     453               0 :     NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
     454               0 :     mRoot = aElement;
     455                 : 
     456               0 :     nsCOMPtr<nsIDocument> doc = mRoot->GetDocument();
     457               0 :     NS_ASSERTION(doc, "element has no document");
     458               0 :     if (! doc)
     459               0 :         return NS_ERROR_UNEXPECTED;
     460                 : 
     461                 :     bool shouldDelay;
     462               0 :     nsresult rv = LoadDataSources(doc, &shouldDelay);
     463                 : 
     464               0 :     if (NS_SUCCEEDED(rv)) {
     465                 :         // Add ourselves as a document observer
     466               0 :         doc->AddObserver(this);
     467                 : 
     468               0 :         mObservedDocument = doc;
     469                 :         gObserverService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
     470               0 :                                       false);
     471                 :         gObserverService->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC,
     472               0 :                                       false);
     473                 :     }
     474                 : 
     475               0 :     return rv;
     476                 : }
     477                 : 
     478                 : NS_IMETHODIMP
     479               0 : nsXULTemplateBuilder::CreateContents(nsIContent* aElement, bool aForceCreation)
     480                 : {
     481               0 :     return NS_OK;
     482                 : }
     483                 : 
     484                 : NS_IMETHODIMP
     485               0 : nsXULTemplateBuilder::HasGeneratedContent(nsIRDFResource* aResource,
     486                 :                                           nsIAtom* aTag,
     487                 :                                           bool* aGenerated)
     488                 : {
     489               0 :     *aGenerated = false;
     490               0 :     return NS_OK;
     491                 : }
     492                 : 
     493                 : NS_IMETHODIMP
     494               0 : nsXULTemplateBuilder::AddResult(nsIXULTemplateResult* aResult,
     495                 :                                 nsIDOMNode* aQueryNode)
     496                 : {
     497               0 :     NS_ENSURE_ARG_POINTER(aResult);
     498               0 :     NS_ENSURE_ARG_POINTER(aQueryNode);
     499                 : 
     500               0 :     return UpdateResult(nsnull, aResult, aQueryNode);
     501                 : }
     502                 : 
     503                 : NS_IMETHODIMP
     504               0 : nsXULTemplateBuilder::RemoveResult(nsIXULTemplateResult* aResult)
     505                 : {
     506               0 :     NS_ENSURE_ARG_POINTER(aResult);
     507                 : 
     508               0 :     return UpdateResult(aResult, nsnull, nsnull);
     509                 : }
     510                 : 
     511                 : NS_IMETHODIMP
     512               0 : nsXULTemplateBuilder::ReplaceResult(nsIXULTemplateResult* aOldResult,
     513                 :                                     nsIXULTemplateResult* aNewResult,
     514                 :                                     nsIDOMNode* aQueryNode)
     515                 : {
     516               0 :     NS_ENSURE_ARG_POINTER(aOldResult);
     517               0 :     NS_ENSURE_ARG_POINTER(aNewResult);
     518               0 :     NS_ENSURE_ARG_POINTER(aQueryNode);
     519                 : 
     520                 :     // just remove the old result and then add a new result separately
     521                 : 
     522               0 :     nsresult rv = UpdateResult(aOldResult, nsnull, nsnull);
     523               0 :     if (NS_FAILED(rv))
     524               0 :         return rv;
     525                 : 
     526               0 :     return UpdateResult(nsnull, aNewResult, aQueryNode);
     527                 : }
     528                 : 
     529                 : nsresult
     530               0 : nsXULTemplateBuilder::UpdateResult(nsIXULTemplateResult* aOldResult,
     531                 :                                    nsIXULTemplateResult* aNewResult,
     532                 :                                    nsIDOMNode* aQueryNode)
     533                 : {
     534               0 :     PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
     535                 :            ("nsXULTemplateBuilder::UpdateResult %p %p %p",
     536                 :            aOldResult, aNewResult, aQueryNode));
     537                 : 
     538               0 :     if (!mRoot || !mQueriesCompiled)
     539               0 :       return NS_OK;
     540                 : 
     541                 :     // get the containers where content may be inserted. If
     542                 :     // GetInsertionLocations returns false, no container has generated
     543                 :     // any content yet so new content should not be generated either. This
     544                 :     // will be false if the result applies to content that is in a closed menu
     545                 :     // or treeitem for example.
     546                 : 
     547               0 :     nsAutoPtr<nsCOMArray<nsIContent> > insertionPoints;
     548                 :     bool mayReplace = GetInsertionLocations(aOldResult ? aOldResult : aNewResult,
     549               0 :                                               getter_Transfers(insertionPoints));
     550               0 :     if (! mayReplace)
     551               0 :         return NS_OK;
     552                 : 
     553               0 :     nsresult rv = NS_OK;
     554                 : 
     555               0 :     nsCOMPtr<nsIRDFResource> oldId, newId;
     556               0 :     nsTemplateQuerySet* queryset = nsnull;
     557                 : 
     558               0 :     if (aOldResult) {
     559               0 :         rv = GetResultResource(aOldResult, getter_AddRefs(oldId));
     560               0 :         if (NS_FAILED(rv))
     561               0 :             return rv;
     562                 : 
     563                 :         // Ignore re-entrant builds for content that is currently in our
     564                 :         // activation stack.
     565               0 :         if (IsActivated(oldId))
     566               0 :             return NS_OK;
     567                 :     }
     568                 : 
     569               0 :     if (aNewResult) {
     570               0 :         rv = GetResultResource(aNewResult, getter_AddRefs(newId));
     571               0 :         if (NS_FAILED(rv))
     572               0 :             return rv;
     573                 : 
     574                 :         // skip results that don't have ids
     575               0 :         if (! newId)
     576               0 :             return NS_OK;
     577                 : 
     578                 :         // Ignore re-entrant builds for content that is currently in our
     579                 :         // activation stack.
     580               0 :         if (IsActivated(newId))
     581               0 :             return NS_OK;
     582                 : 
     583                 :         // look for the queryset associated with the supplied query node
     584               0 :         nsCOMPtr<nsIContent> querycontent = do_QueryInterface(aQueryNode);
     585                 : 
     586               0 :         PRInt32 count = mQuerySets.Length();
     587               0 :         for (PRInt32 q = 0; q < count; q++) {
     588               0 :             nsTemplateQuerySet* qs = mQuerySets[q];
     589               0 :             if (qs->mQueryNode == querycontent) {
     590               0 :                 queryset = qs;
     591               0 :                 break;
     592                 :             }
     593                 :         }
     594                 : 
     595               0 :         if (! queryset)
     596               0 :             return NS_OK;
     597                 :     }
     598                 : 
     599               0 :     if (insertionPoints) {
     600                 :         // iterate over each insertion point and add or remove the result from
     601                 :         // that container
     602               0 :         PRUint32 count = insertionPoints->Count();
     603               0 :         for (PRUint32 t = 0; t < count; t++) {
     604               0 :             nsCOMPtr<nsIContent> insertionPoint = insertionPoints->SafeObjectAt(t);
     605               0 :             if (insertionPoint) {
     606                 :                 rv = UpdateResultInContainer(aOldResult, aNewResult, queryset,
     607               0 :                                              oldId, newId, insertionPoint);
     608               0 :                 if (NS_FAILED(rv))
     609               0 :                     return rv;
     610                 :             }
     611                 :         }
     612                 :     }
     613                 :     else {
     614                 :         // The tree builder doesn't use insertion points, so no insertion
     615                 :         // points will be set. In this case, just update the one result.
     616                 :         rv = UpdateResultInContainer(aOldResult, aNewResult, queryset,
     617               0 :                                      oldId, newId, nsnull);
     618                 :     }
     619                 : 
     620               0 :     return NS_OK;
     621                 : }
     622                 : 
     623                 : nsresult
     624               0 : nsXULTemplateBuilder::UpdateResultInContainer(nsIXULTemplateResult* aOldResult,
     625                 :                                               nsIXULTemplateResult* aNewResult,
     626                 :                                               nsTemplateQuerySet* aQuerySet,
     627                 :                                               nsIRDFResource* aOldId,
     628                 :                                               nsIRDFResource* aNewId,
     629                 :                                               nsIContent* aInsertionPoint)
     630                 : {
     631                 :     // This method takes a result that no longer applies (aOldResult) and
     632                 :     // replaces it with a new result (aNewResult). Either may be null
     633                 :     // indicating to just remove a result or add a new one without replacing.
     634                 :     //
     635                 :     // Matches are stored in the hashtable mMatchMap, keyed by result id. If
     636                 :     // there is more than one query, or the same id is found in different
     637                 :     // containers, the values in the hashtable will be a linked list of all
     638                 :     // the matches for that id. The matches are sorted according to the
     639                 :     // queries they are associated with. Matches for earlier queries in the
     640                 :     // template take priority over matches from later queries. The priority
     641                 :     // for a match is determined from the match's QuerySetPriority method.
     642                 :     // The first query has a priority 0, and higher numbers are for later
     643                 :     // queries with successively higher priorities. Thus, a match takes
     644                 :     // precedence if it has a lower priority than another. If there is only
     645                 :     // one query or container, then the match doesn't have any linked items.
     646                 :     //
     647                 :     // Matches are nsTemplateMatch objects. They are wrappers around
     648                 :     // nsIXULTemplateResult result objects and are created with
     649                 :     // nsTemplateMatch::Create below. The aQuerySet argument specifies which
     650                 :     // query the match is associated with.
     651                 :     //
     652                 :     // When a result id exists in multiple containers, the match's mContainer
     653                 :     // field is set to the container it corresponds to. The aInsertionPoint
     654                 :     // argument specifies which container is being updated. Even though they
     655                 :     // are stored in the same linked list as other matches of the same id, the
     656                 :     // matches for different containers are treated separately. They are only
     657                 :     // stored in the same hashtable to avoid a more complex data structure, as
     658                 :     // the use of the same id in multiple containers isn't a common occurance.
     659                 :     //
     660                 :     // Only one match with a given id per container is active at a time. When
     661                 :     // a match is active, content is generated for it. When a match is
     662                 :     // inactive, content is not generated for it. A match becomes active if
     663                 :     // another match with the same id and container with a lower priority
     664                 :     // isn't already active, and the match has a rule or conditions clause
     665                 :     // which evaluates to true. The former is checked by comparing the value
     666                 :     // of the QuerySetPriority method of the match with earlier matches. The
     667                 :     // latter is checked with the DetermineMatchedRule method.
     668                 :     //
     669                 :     // Naturally, if a match with a lower priority is active, it overrides
     670                 :     // the new match, so the new match is hooked up into the match linked
     671                 :     // list as inactive, and no content is generated for it. If a match with a
     672                 :     // higher priority is active, and the new match's conditions evaluate
     673                 :     // to true, then this existing match with the higher priority needs to have
     674                 :     // its generated content removed and replaced with the new match's
     675                 :     // generated content.
     676                 :     //
     677                 :     // Similar situations apply when removing an existing match. If the match
     678                 :     // is active, the existing generated content will need to be removed, and
     679                 :     // a match of higher priority that is revealed may become active and need
     680                 :     // to have content generated.
     681                 :     //
     682                 :     // Content removal and generation is done by the ReplaceMatch method which
     683                 :     // is overridden for the content builder and tree builder to update the
     684                 :     // generated output for each type.
     685                 :     //
     686                 :     // The code below handles all of the various cases and ensures that the
     687                 :     // match lists are maintained properly.
     688                 : 
     689               0 :     nsresult rv = NS_OK;
     690                 :     PRInt16 ruleindex;
     691               0 :     nsTemplateRule* matchedrule = nsnull;
     692                 : 
     693                 :     // Indicates that the old match was active and must have its content
     694                 :     // removed
     695               0 :     bool oldMatchWasActive = false;
     696                 : 
     697                 :     // acceptedmatch will be set to a new match that has to have new content
     698                 :     // generated for it. If a new match doesn't need to have content
     699                 :     // generated, (because for example, a match with a lower priority
     700                 :     // already applies), then acceptedmatch will be null, but the match will
     701                 :     // be still hooked up into the chain, since it may become active later
     702                 :     // as other results are updated.
     703               0 :     nsTemplateMatch* acceptedmatch = nsnull;
     704                 : 
     705                 :     // When aOldResult is specified, removematch will be set to the
     706                 :     // corresponding match. This match needs to be deleted as it no longer
     707                 :     // applies. However, removedmatch will be null when aOldResult is null, or
     708                 :     // when no match was found corresponding to aOldResult.
     709               0 :     nsTemplateMatch* removedmatch = nsnull;
     710                 : 
     711                 :     // These will be set when aNewResult is specified indicating to add a
     712                 :     // result, but will end up replacing an existing match. The former
     713                 :     // indicates a match being replaced that was active and had content
     714                 :     // generated for it, while the latter indicates a match that wasn't active
     715                 :     // and just needs to be deleted. Both may point to different matches. For
     716                 :     // example, if the new match becomes active, replacing an inactive match,
     717                 :     // the inactive match will need to be deleted. However, if another match
     718                 :     // with a higher priority is active, the new match will override it, so
     719                 :     // content will need to be generated for the new match and removed for
     720                 :     // this existing active match.
     721               0 :     nsTemplateMatch* replacedmatch = nsnull, * replacedmatchtodelete = nsnull;
     722                 : 
     723               0 :     if (aOldResult) {
     724                 :         nsTemplateMatch* firstmatch;
     725               0 :         if (mMatchMap.Get(aOldId, &firstmatch)) {
     726               0 :             nsTemplateMatch* oldmatch = firstmatch;
     727               0 :             nsTemplateMatch* prevmatch = nsnull;
     728                 : 
     729                 :             // look for the right match if there was more than one
     730               0 :             while (oldmatch && (oldmatch->mResult != aOldResult)) {
     731               0 :                 prevmatch = oldmatch;
     732               0 :                 oldmatch = oldmatch->mNext;
     733                 :             }
     734                 : 
     735               0 :             if (oldmatch) {
     736               0 :                 nsTemplateMatch* findmatch = oldmatch->mNext;
     737                 : 
     738                 :                 // Keep a reference so that linked list can be hooked up at
     739                 :                 // the end in case an error occurs.
     740               0 :                 nsTemplateMatch* nextmatch = findmatch;
     741                 : 
     742               0 :                 if (oldmatch->IsActive()) {
     743                 :                     // Indicate that the old match was active so its content
     744                 :                     // will be removed later.
     745               0 :                     oldMatchWasActive = true;
     746                 : 
     747                 :                     // The match being removed is the active match, so scan
     748                 :                     // through the later matches to determine if one should
     749                 :                     // now become the active match.
     750               0 :                     while (findmatch) {
     751                 :                         // only other matches with the same container should
     752                 :                         // now match, leave other containers alone
     753               0 :                         if (findmatch->GetContainer() == aInsertionPoint) {
     754                 :                             nsTemplateQuerySet* qs =
     755               0 :                                 mQuerySets[findmatch->QuerySetPriority()];
     756                 :                         
     757                 :                             DetermineMatchedRule(aInsertionPoint, findmatch->mResult,
     758               0 :                                                  qs, &matchedrule, &ruleindex);
     759                 : 
     760               0 :                             if (matchedrule) {
     761                 :                                 rv = findmatch->RuleMatched(qs,
     762                 :                                                             matchedrule, ruleindex,
     763               0 :                                                             findmatch->mResult);
     764               0 :                                 if (NS_FAILED(rv))
     765               0 :                                     return rv;
     766                 : 
     767               0 :                                 acceptedmatch = findmatch;
     768               0 :                                 break;
     769                 :                             }
     770                 :                         }
     771                 : 
     772               0 :                         findmatch = findmatch->mNext;
     773                 :                     }
     774                 :                 }
     775                 : 
     776               0 :                 if (oldmatch == firstmatch) {
     777                 :                     // the match to remove is at the beginning
     778               0 :                     if (oldmatch->mNext) {
     779               0 :                         if (!mMatchMap.Put(aOldId, oldmatch->mNext))
     780               0 :                             return NS_ERROR_OUT_OF_MEMORY;
     781                 :                     }
     782                 :                     else {
     783               0 :                         mMatchMap.Remove(aOldId);
     784                 :                     }
     785                 :                 }
     786                 : 
     787               0 :                 if (prevmatch)
     788               0 :                     prevmatch->mNext = nextmatch;
     789                 : 
     790               0 :                 removedmatch = oldmatch;
     791               0 :                 if (mFlags & eLoggingEnabled)
     792               0 :                     OutputMatchToLog(aOldId, removedmatch, false);
     793                 :             }
     794                 :         }
     795                 :     }
     796                 : 
     797               0 :     nsTemplateMatch *newmatch = nsnull;
     798               0 :     if (aNewResult) {
     799                 :         // only allow a result to be inserted into containers with a matching tag
     800               0 :         nsIAtom* tag = aQuerySet->GetTag();
     801               0 :         if (aInsertionPoint && tag && tag != aInsertionPoint->Tag())
     802               0 :             return NS_OK;
     803                 : 
     804               0 :         PRInt32 findpriority = aQuerySet->Priority();
     805                 : 
     806                 :         newmatch = nsTemplateMatch::Create(mPool, findpriority,
     807               0 :                                            aNewResult, aInsertionPoint);
     808               0 :         if (!newmatch)
     809               0 :             return NS_ERROR_OUT_OF_MEMORY;
     810                 : 
     811                 :         nsTemplateMatch* firstmatch;
     812               0 :         if (mMatchMap.Get(aNewId, &firstmatch)) {
     813               0 :             bool hasEarlierActiveMatch = false;
     814                 : 
     815                 :             // Scan through the existing matches to find where the new one
     816                 :             // should be inserted. oldmatch will be set to the old match for
     817                 :             // the same query and prevmatch will be set to the match before it.
     818               0 :             nsTemplateMatch* prevmatch = nsnull;
     819               0 :             nsTemplateMatch* oldmatch = firstmatch;
     820               0 :             while (oldmatch) {
     821                 :                 // Break out once we've reached a query in the list with a
     822                 :                 // lower priority. The new match will be inserted at this
     823                 :                 // location so that the match list is sorted by priority.
     824               0 :                 PRInt32 priority = oldmatch->QuerySetPriority();
     825               0 :                 if (priority > findpriority) {
     826               0 :                     oldmatch = nsnull;
     827               0 :                     break;
     828                 :                 }
     829                 : 
     830                 :                 // look for matches that belong in the same container
     831               0 :                 if (oldmatch->GetContainer() == aInsertionPoint) {
     832               0 :                     if (priority == findpriority)
     833               0 :                         break;
     834                 : 
     835                 :                     // If a match with a lower priority is active, the new
     836                 :                     // match can't replace it.
     837               0 :                     if (oldmatch->IsActive())
     838               0 :                         hasEarlierActiveMatch = true;
     839                 :                 }
     840                 : 
     841               0 :                 prevmatch = oldmatch;
     842               0 :                 oldmatch = oldmatch->mNext;
     843                 :             }
     844                 : 
     845                 :             // At this point, oldmatch will either be null, or set to a match
     846                 :             // with the same container and priority. If set, oldmatch will
     847                 :             // need to be replaced by newmatch.
     848                 : 
     849               0 :             if (oldmatch)
     850               0 :                 newmatch->mNext = oldmatch->mNext;
     851               0 :             else if (prevmatch)
     852               0 :                 newmatch->mNext = prevmatch->mNext;
     853                 :             else
     854               0 :                 newmatch->mNext = firstmatch;
     855                 : 
     856                 :             // hasEarlierActiveMatch will be set to true if a match with a
     857                 :             // lower priority was found. The new match won't replace it in
     858                 :             // this case. If hasEarlierActiveMatch is false, then the new match
     859                 :             // may be become active if it matches one of the rules, and will
     860                 :             // generate output. It's also possible however, that a match with
     861                 :             // the same priority already exists, which means that the new match
     862                 :             // will replace the old one. In this case, oldmatch will be set to
     863                 :             // the old match. The content for the old match must be removed and
     864                 :             // content for the new match generated in its place.
     865               0 :             if (! hasEarlierActiveMatch) {
     866                 :                 // If the old match was the active match, set replacedmatch to
     867                 :                 // indicate that it needs its content removed.
     868               0 :                 if (oldmatch) {
     869               0 :                     if (oldmatch->IsActive())
     870               0 :                         replacedmatch = oldmatch;
     871               0 :                     replacedmatchtodelete = oldmatch;
     872                 :                 }
     873                 : 
     874                 :                 // check if the new result matches the rules
     875                 :                 rv = DetermineMatchedRule(aInsertionPoint, newmatch->mResult,
     876               0 :                                           aQuerySet, &matchedrule, &ruleindex);
     877               0 :                 if (NS_FAILED(rv)) {
     878               0 :                     nsTemplateMatch::Destroy(mPool, newmatch, false);
     879               0 :                     return rv;
     880                 :                 }
     881                 : 
     882               0 :                 if (matchedrule) {
     883                 :                     rv = newmatch->RuleMatched(aQuerySet,
     884                 :                                                matchedrule, ruleindex,
     885               0 :                                                newmatch->mResult);
     886               0 :                     if (NS_FAILED(rv)) {
     887               0 :                         nsTemplateMatch::Destroy(mPool, newmatch, false);
     888               0 :                         return rv;
     889                 :                     }
     890                 : 
     891                 :                     // acceptedmatch may have been set in the block handling
     892                 :                     // aOldResult earlier. If so, we would only get here when
     893                 :                     // that match has a higher priority than this new match.
     894                 :                     // As only one match can have content generated for it, it
     895                 :                     // is OK to set acceptedmatch here to the new match,
     896                 :                     // ignoring the other one.
     897               0 :                     acceptedmatch = newmatch;
     898                 : 
     899                 :                     // Clear the matched state of the later results for the
     900                 :                     // same container.
     901               0 :                     nsTemplateMatch* clearmatch = newmatch->mNext;
     902               0 :                     while (clearmatch) {
     903               0 :                         if (clearmatch->GetContainer() == aInsertionPoint &&
     904               0 :                             clearmatch->IsActive()) {
     905               0 :                             clearmatch->SetInactive();
     906                 :                             // Replacedmatch should be null here. If not, it
     907                 :                             // means that two matches were active which isn't
     908                 :                             // a valid state
     909               0 :                             NS_ASSERTION(!replacedmatch,
     910                 :                                          "replaced match already set");
     911               0 :                             replacedmatch = clearmatch;
     912               0 :                             break;
     913                 :                         }
     914               0 :                         clearmatch = clearmatch->mNext;
     915                 :                     }
     916                 :                 }
     917               0 :                 else if (oldmatch && oldmatch->IsActive()) {
     918                 :                     // The result didn't match the rules, so look for a later
     919                 :                     // one. However, only do this if the old match was the
     920                 :                     // active match.
     921               0 :                     newmatch = newmatch->mNext;
     922               0 :                     while (newmatch) {
     923               0 :                         if (newmatch->GetContainer() == aInsertionPoint) {
     924                 :                             rv = DetermineMatchedRule(aInsertionPoint, newmatch->mResult,
     925               0 :                                                       aQuerySet, &matchedrule, &ruleindex);
     926               0 :                             if (NS_FAILED(rv)) {
     927                 :                                 nsTemplateMatch::Destroy(mPool, newmatch,
     928               0 :                                                          false);
     929               0 :                                 return rv;
     930                 :                             }
     931                 : 
     932               0 :                             if (matchedrule) {
     933                 :                                 rv = newmatch->RuleMatched(aQuerySet,
     934                 :                                                            matchedrule, ruleindex,
     935               0 :                                                            newmatch->mResult);
     936               0 :                                 if (NS_FAILED(rv)) {
     937                 :                                     nsTemplateMatch::Destroy(mPool, newmatch,
     938               0 :                                                              false);
     939               0 :                                     return rv;
     940                 :                                 }
     941                 : 
     942               0 :                                 acceptedmatch = newmatch;
     943               0 :                                 break;
     944                 :                             }
     945                 :                         }
     946                 : 
     947               0 :                         newmatch = newmatch->mNext;
     948                 :                     }
     949                 :                 }
     950                 : 
     951                 :                 // put the match in the map if there isn't a previous match
     952               0 :                 if (! prevmatch) {
     953               0 :                     if (!mMatchMap.Put(aNewId, newmatch)) {
     954                 :                         // The match may have already matched a rule above, so
     955                 :                         // HasBeenRemoved should be called to indicate that it
     956                 :                         // is being removed again.
     957               0 :                         nsTemplateMatch::Destroy(mPool, newmatch, true);
     958               0 :                         return rv;
     959                 :                     }
     960                 :                 }
     961                 :             }
     962                 : 
     963                 :             // hook up the match last in case an error occurs
     964               0 :             if (prevmatch)
     965               0 :                 prevmatch->mNext = newmatch;
     966                 :         }
     967                 :         else {
     968                 :             // The id is not used in the hashtable yet so create a new match
     969                 :             // and add it to the hashtable.
     970                 :             rv = DetermineMatchedRule(aInsertionPoint, aNewResult,
     971               0 :                                       aQuerySet, &matchedrule, &ruleindex);
     972               0 :             if (NS_FAILED(rv)) {
     973               0 :                 nsTemplateMatch::Destroy(mPool, newmatch, false);
     974               0 :                 return rv;
     975                 :             }
     976                 : 
     977               0 :             if (matchedrule) {
     978                 :                 rv = newmatch->RuleMatched(aQuerySet, matchedrule,
     979               0 :                                            ruleindex, aNewResult);
     980               0 :                 if (NS_FAILED(rv)) {
     981               0 :                     nsTemplateMatch::Destroy(mPool, newmatch, false);
     982               0 :                     return rv;
     983                 :                 }
     984                 : 
     985               0 :                 acceptedmatch = newmatch;
     986                 :             }
     987                 : 
     988               0 :             if (!mMatchMap.Put(aNewId, newmatch)) {
     989               0 :                 nsTemplateMatch::Destroy(mPool, newmatch, true);
     990               0 :                 return NS_ERROR_OUT_OF_MEMORY;
     991                 :             }
     992                 :         }
     993                 :     }
     994                 : 
     995                 :     // The ReplaceMatch method is builder specific and removes the generated
     996                 :     // content for a match.
     997                 : 
     998                 :     // Remove the content for a match that was active and needs to be replaced.
     999               0 :     if (replacedmatch) {
    1000                 :         rv = ReplaceMatch(replacedmatch->mResult, nsnull, nsnull,
    1001               0 :                           aInsertionPoint);
    1002                 : 
    1003               0 :         if (mFlags & eLoggingEnabled)
    1004               0 :             OutputMatchToLog(aNewId, replacedmatch, false);
    1005                 :     }
    1006                 :  
    1007                 :     // remove a match that needs to be deleted.
    1008               0 :     if (replacedmatchtodelete)
    1009               0 :         nsTemplateMatch::Destroy(mPool, replacedmatchtodelete, true);
    1010                 : 
    1011                 :     // If the old match was active, the content for it needs to be removed.
    1012                 :     // If the old match was not active, it shouldn't have had any content,
    1013                 :     // so just pass null to ReplaceMatch. If acceptedmatch was set, then
    1014                 :     // content needs to be generated for a new match.
    1015               0 :     if (oldMatchWasActive || acceptedmatch)
    1016                 :         rv = ReplaceMatch(oldMatchWasActive ? aOldResult : nsnull,
    1017               0 :                           acceptedmatch, matchedrule, aInsertionPoint);
    1018                 : 
    1019                 :     // delete the old match that was replaced
    1020               0 :     if (removedmatch)
    1021               0 :         nsTemplateMatch::Destroy(mPool, removedmatch, true);
    1022                 : 
    1023               0 :     if (mFlags & eLoggingEnabled && newmatch)
    1024               0 :         OutputMatchToLog(aNewId, newmatch, true);
    1025                 : 
    1026               0 :     return rv;
    1027                 : }
    1028                 : 
    1029                 : NS_IMETHODIMP
    1030               0 : nsXULTemplateBuilder::ResultBindingChanged(nsIXULTemplateResult* aResult)
    1031                 : {
    1032                 :     // A binding update is used when only the values of the bindings have
    1033                 :     // changed, so the same rule still applies. Just synchronize the content.
    1034                 :     // The new result will have the new values.
    1035               0 :     NS_ENSURE_ARG_POINTER(aResult);
    1036                 : 
    1037               0 :     if (!mRoot || !mQueriesCompiled)
    1038               0 :       return NS_OK;
    1039                 : 
    1040               0 :     return SynchronizeResult(aResult);
    1041                 : }
    1042                 : 
    1043                 : NS_IMETHODIMP
    1044               0 : nsXULTemplateBuilder::GetRootResult(nsIXULTemplateResult** aResult)
    1045                 : {
    1046               0 :   *aResult = mRootResult;
    1047               0 :   NS_IF_ADDREF(*aResult);
    1048               0 :   return NS_OK;
    1049                 : }
    1050                 : 
    1051                 : NS_IMETHODIMP
    1052               0 : nsXULTemplateBuilder::GetResultForId(const nsAString& aId,
    1053                 :                                      nsIXULTemplateResult** aResult)
    1054                 : {
    1055               0 :     if (aId.IsEmpty())
    1056               0 :         return NS_ERROR_INVALID_ARG;
    1057                 : 
    1058               0 :     nsCOMPtr<nsIRDFResource> resource;
    1059               0 :     gRDFService->GetUnicodeResource(aId, getter_AddRefs(resource));
    1060                 : 
    1061               0 :     *aResult = nsnull;
    1062                 : 
    1063                 :     nsTemplateMatch* match;
    1064               0 :     if (mMatchMap.Get(resource, &match)) {
    1065                 :         // find the active match
    1066               0 :         while (match) {
    1067               0 :             if (match->IsActive()) {
    1068               0 :                 *aResult = match->mResult;
    1069               0 :                 NS_IF_ADDREF(*aResult);
    1070               0 :                 break;
    1071                 :             }
    1072               0 :             match = match->mNext;
    1073                 :         }
    1074                 :     }
    1075                 : 
    1076               0 :     return NS_OK;
    1077                 : }
    1078                 : 
    1079                 : NS_IMETHODIMP
    1080               0 : nsXULTemplateBuilder::GetResultForContent(nsIDOMElement* aContent,
    1081                 :                                           nsIXULTemplateResult** aResult)
    1082                 : {
    1083               0 :     *aResult = nsnull;
    1084               0 :     return NS_OK;
    1085                 : }
    1086                 : 
    1087                 : NS_IMETHODIMP
    1088               0 : nsXULTemplateBuilder::AddListener(nsIXULBuilderListener* aListener)
    1089                 : {
    1090               0 :     NS_ENSURE_ARG(aListener);
    1091                 : 
    1092               0 :     if (!mListeners.AppendObject(aListener))
    1093               0 :         return NS_ERROR_OUT_OF_MEMORY;
    1094                 : 
    1095               0 :     return NS_OK;
    1096                 : }
    1097                 : 
    1098                 : NS_IMETHODIMP
    1099               0 : nsXULTemplateBuilder::RemoveListener(nsIXULBuilderListener* aListener)
    1100                 : {
    1101               0 :     NS_ENSURE_ARG(aListener);
    1102                 : 
    1103               0 :     mListeners.RemoveObject(aListener);
    1104                 : 
    1105               0 :     return NS_OK;
    1106                 : }
    1107                 : 
    1108                 : NS_IMETHODIMP
    1109               0 : nsXULTemplateBuilder::Observe(nsISupports* aSubject,
    1110                 :                               const char* aTopic,
    1111                 :                               const PRUnichar* aData)
    1112                 : {
    1113                 :     // Uuuuber hack to clean up circular references that the cycle collector
    1114                 :     // doesn't know about. See bug 394514.
    1115               0 :     if (!strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC)) {
    1116               0 :         nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aSubject);
    1117               0 :         if (window) {
    1118                 :             nsCOMPtr<nsIDocument> doc =
    1119               0 :                 do_QueryInterface(window->GetExtantDocument());
    1120               0 :             if (doc && doc == mObservedDocument)
    1121               0 :                 NodeWillBeDestroyed(doc);
    1122                 :         }
    1123               0 :     } else if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
    1124               0 :         UninitTrue();
    1125                 :     }
    1126               0 :     return NS_OK;
    1127                 : }
    1128                 : //----------------------------------------------------------------------
    1129                 : //
    1130                 : // nsIDocumentOberver interface
    1131                 : //
    1132                 : 
    1133                 : void
    1134               0 : nsXULTemplateBuilder::AttributeChanged(nsIDocument* aDocument,
    1135                 :                                        Element*     aElement,
    1136                 :                                        PRInt32      aNameSpaceID,
    1137                 :                                        nsIAtom*     aAttribute,
    1138                 :                                        PRInt32      aModType)
    1139                 : {
    1140               0 :     if (aElement == mRoot && aNameSpaceID == kNameSpaceID_None) {
    1141                 :         // Check for a change to the 'ref' attribute on an atom, in which
    1142                 :         // case we may need to nuke and rebuild the entire content model
    1143                 :         // beneath the element.
    1144               0 :         if (aAttribute == nsGkAtoms::ref)
    1145                 :             nsContentUtils::AddScriptRunner(
    1146               0 :                 NS_NewRunnableMethod(this, &nsXULTemplateBuilder::RunnableRebuild));
    1147                 : 
    1148                 :         // Check for a change to the 'datasources' attribute. If so, setup
    1149                 :         // mDB by parsing the new value and rebuild.
    1150               0 :         else if (aAttribute == nsGkAtoms::datasources) {
    1151                 :             nsContentUtils::AddScriptRunner(
    1152               0 :                 NS_NewRunnableMethod(this, &nsXULTemplateBuilder::RunnableLoadAndRebuild));
    1153                 :         }
    1154                 :     }
    1155               0 : }
    1156                 : 
    1157                 : void
    1158               0 : nsXULTemplateBuilder::ContentRemoved(nsIDocument* aDocument,
    1159                 :                                      nsIContent* aContainer,
    1160                 :                                      nsIContent* aChild,
    1161                 :                                      PRInt32 aIndexInContainer,
    1162                 :                                      nsIContent* aPreviousSibling)
    1163                 : {
    1164               0 :     if (mRoot && nsContentUtils::ContentIsDescendantOf(mRoot, aChild)) {
    1165               0 :         nsRefPtr<nsXULTemplateBuilder> kungFuDeathGrip(this);
    1166                 : 
    1167               0 :         if (mQueryProcessor)
    1168               0 :             mQueryProcessor->Done();
    1169                 : 
    1170                 :         // Pass false to Uninit since content is going away anyway
    1171                 :         nsContentUtils::AddScriptRunner(
    1172               0 :             NS_NewRunnableMethod(this, &nsXULTemplateBuilder::UninitFalse));
    1173                 : 
    1174               0 :         aDocument->RemoveObserver(this);
    1175                 : 
    1176               0 :         nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(aDocument);
    1177               0 :         if (xuldoc)
    1178               0 :             xuldoc->SetTemplateBuilderFor(mRoot, nsnull);
    1179                 : 
    1180                 :         // clear the template state when removing content so that template
    1181                 :         // content will be regenerated again if the content is reinserted
    1182               0 :         nsXULElement *xulcontent = nsXULElement::FromContent(mRoot);
    1183               0 :         if (xulcontent)
    1184               0 :             xulcontent->ClearTemplateGenerated();
    1185                 : 
    1186               0 :         mDB = nsnull;
    1187               0 :         mCompDB = nsnull;
    1188               0 :         mDataSource = nsnull;
    1189                 :     }
    1190               0 : }
    1191                 : 
    1192                 : void
    1193               0 : nsXULTemplateBuilder::NodeWillBeDestroyed(const nsINode* aNode)
    1194                 : {
    1195                 :     // The call to RemoveObserver could release the last reference to
    1196                 :     // |this|, so hold another reference.
    1197               0 :     nsRefPtr<nsXULTemplateBuilder> kungFuDeathGrip(this);
    1198                 : 
    1199                 :     // Break circular references
    1200               0 :     if (mQueryProcessor)
    1201               0 :         mQueryProcessor->Done();
    1202                 : 
    1203               0 :     mDataSource = nsnull;
    1204               0 :     mDB = nsnull;
    1205               0 :     mCompDB = nsnull;
    1206                 : 
    1207                 :     nsContentUtils::AddScriptRunner(
    1208               0 :         NS_NewRunnableMethod(this, &nsXULTemplateBuilder::UninitTrue));
    1209               0 : }
    1210                 : 
    1211                 : 
    1212                 : 
    1213                 : 
    1214                 : //----------------------------------------------------------------------
    1215                 : //
    1216                 : // Implementation methods
    1217                 : //
    1218                 : 
    1219                 : nsresult
    1220               0 : nsXULTemplateBuilder::LoadDataSources(nsIDocument* aDocument,
    1221                 :                                       bool* aShouldDelayBuilding)
    1222                 : {
    1223               0 :     NS_PRECONDITION(mRoot != nsnull, "not initialized");
    1224                 : 
    1225                 :     nsresult rv;
    1226               0 :     bool isRDFQuery = false;
    1227                 :   
    1228                 :     // we'll set these again later, after we create a new composite ds
    1229               0 :     mDB = nsnull;
    1230               0 :     mCompDB = nsnull;
    1231               0 :     mDataSource = nsnull;
    1232                 : 
    1233               0 :     *aShouldDelayBuilding = false;
    1234                 : 
    1235               0 :     nsAutoString datasources;
    1236               0 :     mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::datasources, datasources);
    1237                 : 
    1238               0 :     nsAutoString querytype;
    1239               0 :     mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::querytype, querytype);
    1240                 : 
    1241                 :     // create the query processor. The querytype attribute on the root element
    1242                 :     // may be used to create one of a specific type.
    1243                 :   
    1244                 :     // XXX should non-chrome be restricted to specific names?
    1245               0 :     if (querytype.IsEmpty())
    1246               0 :         querytype.AssignLiteral("rdf");
    1247                 : 
    1248               0 :     if (querytype.EqualsLiteral("rdf")) {
    1249               0 :         isRDFQuery = true;
    1250               0 :         mQueryProcessor = new nsXULTemplateQueryProcessorRDF();
    1251               0 :         NS_ENSURE_TRUE(mQueryProcessor, NS_ERROR_OUT_OF_MEMORY);
    1252                 :     }
    1253               0 :     else if (querytype.EqualsLiteral("xml")) {
    1254               0 :         mQueryProcessor = new nsXULTemplateQueryProcessorXML();
    1255               0 :         NS_ENSURE_TRUE(mQueryProcessor, NS_ERROR_OUT_OF_MEMORY);
    1256                 :     }
    1257               0 :     else if (querytype.EqualsLiteral("storage")) {
    1258               0 :         mQueryProcessor = new nsXULTemplateQueryProcessorStorage();
    1259               0 :         NS_ENSURE_TRUE(mQueryProcessor, NS_ERROR_OUT_OF_MEMORY);
    1260                 :     }
    1261                 :     else {
    1262               0 :         nsCAutoString cid(NS_QUERY_PROCESSOR_CONTRACTID_PREFIX);
    1263               0 :         AppendUTF16toUTF8(querytype, cid);
    1264               0 :         mQueryProcessor = do_CreateInstance(cid.get(), &rv);
    1265                 : 
    1266               0 :         if (!mQueryProcessor) {
    1267               0 :             nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_INVALID_QUERYPROCESSOR);
    1268               0 :             return rv;
    1269                 :         }
    1270                 :     }
    1271                 : 
    1272                 :     rv = LoadDataSourceUrls(aDocument, datasources,
    1273               0 :                             isRDFQuery, aShouldDelayBuilding);
    1274               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1275                 : 
    1276                 :     // Now set the database on the element, so that script writers can
    1277                 :     // access it.
    1278               0 :     nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(aDocument);
    1279               0 :     if (xuldoc)
    1280               0 :         xuldoc->SetTemplateBuilderFor(mRoot, this);
    1281                 : 
    1282               0 :     if (!mRoot->IsXUL()) {
    1283                 :         // Hmm. This must be an HTML element. Try to set it as a
    1284                 :         // JS property "by hand".
    1285               0 :         InitHTMLTemplateRoot();
    1286                 :     }
    1287                 :   
    1288               0 :     return NS_OK;
    1289                 : }
    1290                 :   
    1291                 : nsresult
    1292               0 : nsXULTemplateBuilder::LoadDataSourceUrls(nsIDocument* aDocument,
    1293                 :                                          const nsAString& aDataSources,
    1294                 :                                          bool aIsRDFQuery,
    1295                 :                                          bool* aShouldDelayBuilding)
    1296                 : {
    1297                 :     // Grab the doc's principal...
    1298               0 :     nsIPrincipal *docPrincipal = aDocument->NodePrincipal();
    1299                 : 
    1300               0 :     NS_ASSERTION(docPrincipal == mRoot->NodePrincipal(),
    1301                 :                  "Principal mismatch?  Which one to use?");
    1302                 : 
    1303               0 :     bool isTrusted = false;
    1304               0 :     nsresult rv = IsSystemPrincipal(docPrincipal, &isTrusted);
    1305               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1306                 : 
    1307                 :     // Parse datasources: they are assumed to be a whitespace
    1308                 :     // separated list of URIs; e.g.,
    1309                 :     //
    1310                 :     //     rdf:bookmarks rdf:history http://foo.bar.com/blah.cgi?baz=9
    1311                 :     //
    1312               0 :     nsIURI *docurl = aDocument->GetDocumentURI();
    1313                 : 
    1314               0 :     nsCOMPtr<nsIMutableArray> uriList = do_CreateInstance(NS_ARRAY_CONTRACTID);
    1315               0 :     if (!uriList)
    1316               0 :         return NS_ERROR_FAILURE;
    1317                 : 
    1318               0 :     nsAutoString datasources(aDataSources);
    1319               0 :     PRUint32 first = 0;
    1320               0 :     while (1) {
    1321               0 :         while (first < datasources.Length() && nsCRT::IsAsciiSpace(datasources.CharAt(first)))
    1322               0 :             ++first;
    1323                 : 
    1324               0 :         if (first >= datasources.Length())
    1325                 :             break;
    1326                 : 
    1327               0 :         PRUint32 last = first;
    1328               0 :         while (last < datasources.Length() && !nsCRT::IsAsciiSpace(datasources.CharAt(last)))
    1329               0 :             ++last;
    1330                 : 
    1331               0 :         nsAutoString uriStr;
    1332               0 :         datasources.Mid(uriStr, first, last - first);
    1333               0 :         first = last + 1;
    1334                 : 
    1335                 :         // A special 'dummy' datasource
    1336               0 :         if (uriStr.EqualsLiteral("rdf:null"))
    1337               0 :             continue;
    1338                 : 
    1339               0 :         if (uriStr.CharAt(0) == '#') {
    1340                 :             // ok, the datasource is certainly a node of the current document
    1341               0 :             nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(aDocument);
    1342               0 :             nsCOMPtr<nsIDOMElement> dsnode;
    1343                 : 
    1344               0 :             domdoc->GetElementById(Substring(uriStr, 1),
    1345               0 :                                    getter_AddRefs(dsnode));
    1346                 : 
    1347               0 :             if (dsnode)
    1348               0 :                 uriList->AppendElement(dsnode, false);
    1349               0 :             continue;
    1350                 :         }
    1351                 : 
    1352                 :         // N.B. that `failure' (e.g., because it's an unknown
    1353                 :         // protocol) leaves uriStr unaltered.
    1354               0 :         NS_MakeAbsoluteURI(uriStr, uriStr, docurl);
    1355                 : 
    1356               0 :         nsCOMPtr<nsIURI> uri;
    1357               0 :         rv = NS_NewURI(getter_AddRefs(uri), uriStr);
    1358               0 :         if (NS_FAILED(rv) || !uri)
    1359               0 :             continue; // Necko will barf if our URI is weird
    1360                 : 
    1361                 :         // don't add the uri to the list if the document is not allowed to
    1362                 :         // load it
    1363               0 :         if (!isTrusted && NS_FAILED(docPrincipal->CheckMayLoad(uri, true)))
    1364               0 :           continue;
    1365                 : 
    1366               0 :         uriList->AppendElement(uri, false);
    1367                 :     }
    1368                 : 
    1369               0 :     nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(mRoot);
    1370               0 :     rv = mQueryProcessor->GetDatasource(uriList,
    1371                 :                                         rootNode,
    1372                 :                                         isTrusted,
    1373                 :                                         this,
    1374                 :                                         aShouldDelayBuilding,
    1375               0 :                                         getter_AddRefs(mDataSource));
    1376               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1377                 : 
    1378               0 :     if (aIsRDFQuery && mDataSource) {  
    1379                 :         // check if we were given an inference engine type
    1380               0 :         nsCOMPtr<nsIRDFInferDataSource> inferDB = do_QueryInterface(mDataSource);
    1381               0 :         if (inferDB) {
    1382               0 :             nsCOMPtr<nsIRDFDataSource> ds;
    1383               0 :             inferDB->GetBaseDataSource(getter_AddRefs(ds));
    1384               0 :             if (ds)
    1385               0 :                 mCompDB = do_QueryInterface(ds);
    1386                 :         }
    1387                 : 
    1388               0 :         if (!mCompDB)
    1389               0 :             mCompDB = do_QueryInterface(mDataSource);
    1390                 : 
    1391               0 :         mDB = do_QueryInterface(mDataSource);
    1392                 :     }
    1393                 : 
    1394               0 :     if (!mDB && isTrusted) {
    1395               0 :         gRDFService->GetDataSource("rdf:local-store", getter_AddRefs(mDB));
    1396                 :     }
    1397                 : 
    1398               0 :     return NS_OK;
    1399                 : }
    1400                 : 
    1401                 : nsresult
    1402               0 : nsXULTemplateBuilder::InitHTMLTemplateRoot()
    1403                 : {
    1404                 :     // Use XPConnect and the JS APIs to whack mDB and this as the
    1405                 :     // 'database' and 'builder' properties onto aElement.
    1406                 :     nsresult rv;
    1407                 : 
    1408               0 :     nsCOMPtr<nsIDocument> doc = mRoot->GetDocument();
    1409               0 :     NS_ASSERTION(doc, "no document");
    1410               0 :     if (! doc)
    1411               0 :         return NS_ERROR_UNEXPECTED;
    1412                 : 
    1413               0 :     nsIScriptGlobalObject *global = doc->GetScriptGlobalObject();
    1414               0 :     if (! global)
    1415               0 :         return NS_ERROR_UNEXPECTED;
    1416                 : 
    1417               0 :     JSObject *scope = global->GetGlobalJSObject();
    1418                 : 
    1419               0 :     nsIScriptContext *context = global->GetContext();
    1420               0 :     if (! context)
    1421               0 :         return NS_ERROR_UNEXPECTED;
    1422                 : 
    1423               0 :     JSContext* jscontext = context->GetNativeContext();
    1424               0 :     NS_ASSERTION(context != nsnull, "no jscontext");
    1425               0 :     if (! jscontext)
    1426               0 :         return NS_ERROR_UNEXPECTED;
    1427                 : 
    1428               0 :     JSAutoRequest ar(jscontext);
    1429                 : 
    1430                 :     jsval v;
    1431               0 :     nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
    1432                 :     rv = nsContentUtils::WrapNative(jscontext, scope, mRoot, mRoot, &v,
    1433               0 :                                     getter_AddRefs(wrapper));
    1434               0 :     NS_ENSURE_SUCCESS(rv, rv);
    1435                 : 
    1436               0 :     JSObject* jselement = JSVAL_TO_OBJECT(v);
    1437                 : 
    1438               0 :     if (mDB) {
    1439                 :         // database
    1440                 :         jsval jsdatabase;
    1441                 :         rv = nsContentUtils::WrapNative(jscontext, scope, mDB,
    1442                 :                                         &NS_GET_IID(nsIRDFCompositeDataSource),
    1443               0 :                                         &jsdatabase, getter_AddRefs(wrapper));
    1444               0 :         NS_ENSURE_SUCCESS(rv, rv);
    1445                 : 
    1446                 :         bool ok;
    1447               0 :         ok = JS_SetProperty(jscontext, jselement, "database", &jsdatabase);
    1448               0 :         NS_ASSERTION(ok, "unable to set database property");
    1449               0 :         if (! ok)
    1450               0 :             return NS_ERROR_FAILURE;
    1451                 :     }
    1452                 : 
    1453                 :     {
    1454                 :         // builder
    1455                 :         jsval jsbuilder;
    1456               0 :         nsCOMPtr<nsIXPConnectJSObjectHolder> wrapper;
    1457                 :         rv = nsContentUtils::WrapNative(jscontext, jselement,
    1458                 :                                         static_cast<nsIXULTemplateBuilder*>(this),
    1459                 :                                         &NS_GET_IID(nsIXULTemplateBuilder),
    1460               0 :                                         &jsbuilder, getter_AddRefs(wrapper));
    1461               0 :         NS_ENSURE_SUCCESS(rv, rv);
    1462                 : 
    1463                 :         bool ok;
    1464               0 :         ok = JS_SetProperty(jscontext, jselement, "builder", &jsbuilder);
    1465               0 :         if (! ok)
    1466               0 :             return NS_ERROR_FAILURE;
    1467                 :     }
    1468                 : 
    1469               0 :     return NS_OK;
    1470                 : }
    1471                 : 
    1472                 : nsresult
    1473               0 : nsXULTemplateBuilder::DetermineMatchedRule(nsIContent *aContainer,
    1474                 :                                            nsIXULTemplateResult* aResult,
    1475                 :                                            nsTemplateQuerySet* aQuerySet,
    1476                 :                                            nsTemplateRule** aMatchedRule,
    1477                 :                                            PRInt16 *aRuleIndex)
    1478                 : {
    1479                 :     // iterate through the rules and look for one that the result matches
    1480               0 :     PRInt16 count = aQuerySet->RuleCount();
    1481               0 :     for (PRInt16 r = 0; r < count; r++) {
    1482               0 :         nsTemplateRule* rule = aQuerySet->GetRuleAt(r);
    1483                 :         // If a tag was specified, it must match the tag of the container
    1484                 :         // where content is being inserted.
    1485               0 :         nsIAtom* tag = rule->GetTag();
    1486               0 :         if ((!aContainer || !tag || tag == aContainer->Tag()) &&
    1487               0 :             rule->CheckMatch(aResult)) {
    1488               0 :             *aMatchedRule = rule;
    1489               0 :             *aRuleIndex = r;
    1490               0 :             return NS_OK;
    1491                 :         }
    1492                 :     }
    1493                 : 
    1494               0 :     *aRuleIndex = -1;
    1495               0 :     *aMatchedRule = nsnull;
    1496               0 :     return NS_OK;
    1497                 : }
    1498                 : 
    1499                 : void
    1500               0 : nsXULTemplateBuilder::ParseAttribute(const nsAString& aAttributeValue,
    1501                 :                                      void (*aVariableCallback)(nsXULTemplateBuilder*, const nsAString&, void*),
    1502                 :                                      void (*aTextCallback)(nsXULTemplateBuilder*, const nsAString&, void*),
    1503                 :                                      void* aClosure)
    1504                 : {
    1505               0 :     nsAString::const_iterator done_parsing;
    1506               0 :     aAttributeValue.EndReading(done_parsing);
    1507                 : 
    1508               0 :     nsAString::const_iterator iter;
    1509               0 :     aAttributeValue.BeginReading(iter);
    1510                 : 
    1511               0 :     nsAString::const_iterator mark(iter), backup(iter);
    1512                 : 
    1513               0 :     for (; iter != done_parsing; backup = ++iter) {
    1514                 :         // A variable is either prefixed with '?' (in the extended
    1515                 :         // syntax) or "rdf:" (in the simple syntax).
    1516                 :         bool isvar;
    1517               0 :         if (*iter == PRUnichar('?') && (++iter != done_parsing)) {
    1518               0 :             isvar = true;
    1519                 :         }
    1520               0 :         else if ((*iter == PRUnichar('r') && (++iter != done_parsing)) &&
    1521               0 :                  (*iter == PRUnichar('d') && (++iter != done_parsing)) &&
    1522               0 :                  (*iter == PRUnichar('f') && (++iter != done_parsing)) &&
    1523               0 :                  (*iter == PRUnichar(':') && (++iter != done_parsing))) {
    1524               0 :             isvar = true;
    1525                 :         }
    1526                 :         else {
    1527               0 :             isvar = false;
    1528                 :         }
    1529                 : 
    1530               0 :         if (! isvar) {
    1531                 :             // It's not a variable, or we ran off the end of the
    1532                 :             // string after the initial variable prefix. Since we may
    1533                 :             // have slurped down some characters before realizing that
    1534                 :             // fact, back up to the point where we started.
    1535               0 :             iter = backup;
    1536               0 :             continue;
    1537                 :         }
    1538               0 :         else if (backup != mark && aTextCallback) {
    1539                 :             // Okay, we've found a variable, and there's some vanilla
    1540                 :             // text that's been buffered up. Flush it.
    1541               0 :             (*aTextCallback)(this, Substring(mark, backup), aClosure);
    1542                 :         }
    1543                 : 
    1544               0 :         if (*iter == PRUnichar('?')) {
    1545                 :             // Well, it was not really a variable, but "??". We use one
    1546                 :             // question mark (the second one, actually) literally.
    1547               0 :             mark = iter;
    1548               0 :             continue;
    1549                 :         }
    1550                 : 
    1551                 :         // Construct a substring that is the symbol we need to look up
    1552                 :         // in the rule's symbol table. The symbol is terminated by a
    1553                 :         // space character, a caret, or the end of the string,
    1554                 :         // whichever comes first.
    1555               0 :         nsAString::const_iterator first(backup);
    1556                 : 
    1557               0 :         PRUnichar c = 0;
    1558               0 :         while (iter != done_parsing) {
    1559               0 :             c = *iter;
    1560               0 :             if ((c == PRUnichar(' ')) || (c == PRUnichar('^')))
    1561               0 :                 break;
    1562                 : 
    1563               0 :             ++iter;
    1564                 :         }
    1565                 : 
    1566               0 :         nsAString::const_iterator last(iter);
    1567                 : 
    1568                 :         // Back up so we don't consume the terminating character
    1569                 :         // *unless* the terminating character was a caret: the caret
    1570                 :         // means "concatenate with no space in between".
    1571               0 :         if (c != PRUnichar('^'))
    1572               0 :             --iter;
    1573                 : 
    1574               0 :         (*aVariableCallback)(this, Substring(first, last), aClosure);
    1575               0 :         mark = iter;
    1576               0 :         ++mark;
    1577                 :     }
    1578                 : 
    1579               0 :     if (backup != mark && aTextCallback) {
    1580                 :         // If there's any text left over, then fire the text callback
    1581               0 :         (*aTextCallback)(this, Substring(mark, backup), aClosure);
    1582                 :     }
    1583               0 : }
    1584                 : 
    1585                 : 
    1586               0 : struct NS_STACK_CLASS SubstituteTextClosure {
    1587               0 :     SubstituteTextClosure(nsIXULTemplateResult* aResult, nsAString& aString)
    1588               0 :         : result(aResult), str(aString) {}
    1589                 : 
    1590                 :     // some datasources are lazily initialized or modified while values are
    1591                 :     // being retrieved, causing results to be removed. Due to this, hold a
    1592                 :     // strong reference to the result.
    1593                 :     nsCOMPtr<nsIXULTemplateResult> result;
    1594                 :     nsAString& str;
    1595                 : };
    1596                 : 
    1597                 : nsresult
    1598               0 : nsXULTemplateBuilder::SubstituteText(nsIXULTemplateResult* aResult,
    1599                 :                                      const nsAString& aAttributeValue,
    1600                 :                                      nsAString& aString)
    1601                 : {
    1602                 :     // See if it's the special value "..."
    1603               0 :     if (aAttributeValue.EqualsLiteral("...")) {
    1604               0 :         aResult->GetId(aString);
    1605               0 :         return NS_OK;
    1606                 :     }
    1607                 : 
    1608                 :     // Reasonable guess at how big it should be
    1609               0 :     aString.SetCapacity(aAttributeValue.Length());
    1610                 : 
    1611               0 :     SubstituteTextClosure closure(aResult, aString);
    1612                 :     ParseAttribute(aAttributeValue,
    1613                 :                    SubstituteTextReplaceVariable,
    1614                 :                    SubstituteTextAppendText,
    1615               0 :                    &closure);
    1616                 : 
    1617               0 :     return NS_OK;
    1618                 : }
    1619                 : 
    1620                 : 
    1621                 : void
    1622               0 : nsXULTemplateBuilder::SubstituteTextAppendText(nsXULTemplateBuilder* aThis,
    1623                 :                                                const nsAString& aText,
    1624                 :                                                void* aClosure)
    1625                 : {
    1626                 :     // Append aString to the closure's result
    1627               0 :     SubstituteTextClosure* c = static_cast<SubstituteTextClosure*>(aClosure);
    1628               0 :     c->str.Append(aText);
    1629               0 : }
    1630                 : 
    1631                 : void
    1632               0 : nsXULTemplateBuilder::SubstituteTextReplaceVariable(nsXULTemplateBuilder* aThis,
    1633                 :                                                     const nsAString& aVariable,
    1634                 :                                                     void* aClosure)
    1635                 : {
    1636                 :     // Substitute the value for the variable and append to the
    1637                 :     // closure's result.
    1638               0 :     SubstituteTextClosure* c = static_cast<SubstituteTextClosure*>(aClosure);
    1639                 : 
    1640               0 :     nsAutoString replacementText;
    1641                 : 
    1642                 :     // The symbol "rdf:*" is special, and means "this guy's URI"
    1643               0 :     if (aVariable.EqualsLiteral("rdf:*")){
    1644               0 :         c->result->GetId(replacementText);
    1645                 :     }
    1646                 :     else {
    1647                 :         // Got a variable; get the value it's assigned to
    1648               0 :         nsCOMPtr<nsIAtom> var = do_GetAtom(aVariable);
    1649               0 :         c->result->GetBindingFor(var, replacementText);
    1650                 :     }
    1651                 : 
    1652               0 :     c->str += replacementText;
    1653               0 : }
    1654                 : 
    1655                 : bool
    1656               0 : nsXULTemplateBuilder::IsTemplateElement(nsIContent* aContent)
    1657                 : {
    1658                 :     return aContent->NodeInfo()->Equals(nsGkAtoms::_template,
    1659               0 :                                         kNameSpaceID_XUL);
    1660                 : }
    1661                 : 
    1662                 : nsresult
    1663               0 : nsXULTemplateBuilder::GetTemplateRoot(nsIContent** aResult)
    1664                 : {
    1665               0 :     NS_PRECONDITION(mRoot != nsnull, "not initialized");
    1666               0 :     if (! mRoot)
    1667               0 :         return NS_ERROR_NOT_INITIALIZED;
    1668                 : 
    1669                 :     // First, check and see if the root has a template attribute. This
    1670                 :     // allows a template to be specified "out of line"; e.g.,
    1671                 :     //
    1672                 :     //   <window>
    1673                 :     //     <foo template="MyTemplate">...</foo>
    1674                 :     //     <template id="MyTemplate">...</template>
    1675                 :     //   </window>
    1676                 :     //
    1677               0 :     nsAutoString templateID;
    1678               0 :     mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::_template, templateID);
    1679                 : 
    1680               0 :     if (! templateID.IsEmpty()) {
    1681               0 :         nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(mRoot->GetDocument());
    1682               0 :         if (! domDoc)
    1683               0 :             return NS_OK;
    1684                 : 
    1685               0 :         nsCOMPtr<nsIDOMElement> domElement;
    1686               0 :         domDoc->GetElementById(templateID, getter_AddRefs(domElement));
    1687                 : 
    1688               0 :         if (domElement) {
    1689               0 :             nsCOMPtr<nsIContent> content = do_QueryInterface(domElement);
    1690               0 :             NS_ENSURE_STATE(content &&
    1691                 :                             !nsContentUtils::ContentIsDescendantOf(mRoot,
    1692                 :                                                                    content));
    1693               0 :             content.forget(aResult);
    1694               0 :             return NS_OK;
    1695                 :         }
    1696                 :     }
    1697                 : 
    1698                 : #if 1 // XXX hack to workaround bug with XBL insertion/removal?
    1699                 :     {
    1700                 :         // If root node has no template attribute, then look for a child
    1701                 :         // node which is a template tag
    1702               0 :         for (nsIContent* child = mRoot->GetFirstChild();
    1703                 :              child;
    1704               0 :              child = child->GetNextSibling()) {
    1705                 : 
    1706               0 :             if (IsTemplateElement(child)) {
    1707               0 :                 NS_ADDREF(*aResult = child);
    1708               0 :                 return NS_OK;
    1709                 :             }
    1710                 :         }
    1711                 :     }
    1712                 : #endif
    1713                 : 
    1714                 :     // If we couldn't find a real child, look through the anonymous
    1715                 :     // kids, too.
    1716               0 :     nsCOMPtr<nsIDocument> doc = mRoot->GetDocument();
    1717               0 :     if (! doc)
    1718               0 :         return NS_OK;
    1719                 : 
    1720               0 :     nsCOMPtr<nsIDOMNodeList> kids;
    1721               0 :     doc->BindingManager()->GetXBLChildNodesFor(mRoot, getter_AddRefs(kids));
    1722                 : 
    1723               0 :     if (kids) {
    1724                 :         PRUint32 length;
    1725               0 :         kids->GetLength(&length);
    1726                 : 
    1727               0 :         for (PRUint32 i = 0; i < length; ++i) {
    1728               0 :             nsCOMPtr<nsIDOMNode> node;
    1729               0 :             kids->Item(i, getter_AddRefs(node));
    1730               0 :             if (! node)
    1731               0 :                 continue;
    1732                 : 
    1733               0 :             nsCOMPtr<nsIContent> child = do_QueryInterface(node);
    1734                 : 
    1735               0 :             if (IsTemplateElement(child)) {
    1736               0 :                 NS_ADDREF(*aResult = child.get());
    1737               0 :                 return NS_OK;
    1738                 :             }
    1739                 :         }
    1740                 :     }
    1741                 : 
    1742               0 :     *aResult = nsnull;
    1743               0 :     return NS_OK;
    1744                 : }
    1745                 : 
    1746                 : nsresult
    1747               0 : nsXULTemplateBuilder::CompileQueries()
    1748                 : {
    1749               0 :     nsCOMPtr<nsIContent> tmpl;
    1750               0 :     GetTemplateRoot(getter_AddRefs(tmpl));
    1751               0 :     if (! tmpl)
    1752               0 :         return NS_OK;
    1753                 : 
    1754               0 :     if (! mRoot)
    1755               0 :         return NS_ERROR_NOT_INITIALIZED;
    1756                 : 
    1757                 :     // Determine if there are any special settings we need to observe
    1758               0 :     mFlags = 0;
    1759                 : 
    1760               0 :     nsAutoString flags;
    1761               0 :     mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::flags, flags);
    1762                 : 
    1763                 :     // if the dont-test-empty flag is set, containers should not be checked to
    1764                 :     // see if they are empty. If dont-recurse is set, then don't process the
    1765                 :     // template recursively and only show one level of results. The logging
    1766                 :     // flag logs errors and results to the console, which is useful when
    1767                 :     // debugging templates.
    1768               0 :     nsWhitespaceTokenizer tokenizer(flags);
    1769               0 :     while (tokenizer.hasMoreTokens()) {
    1770               0 :       const nsDependentSubstring& token(tokenizer.nextToken());
    1771               0 :       if (token.EqualsLiteral("dont-test-empty"))
    1772               0 :         mFlags |= eDontTestEmpty;
    1773               0 :       else if (token.EqualsLiteral("dont-recurse"))
    1774               0 :         mFlags |= eDontRecurse;
    1775               0 :       else if (token.EqualsLiteral("logging"))
    1776               0 :         mFlags |= eLoggingEnabled;
    1777                 :     }
    1778                 : 
    1779                 : #ifdef PR_LOGGING
    1780                 :     // always enable logging if the debug setting is used
    1781               0 :     if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG))
    1782               0 :         mFlags |= eLoggingEnabled;
    1783                 : #endif
    1784                 : 
    1785               0 :     nsCOMPtr<nsIDOMNode> rootnode = do_QueryInterface(mRoot);
    1786                 :     nsresult rv =
    1787               0 :         mQueryProcessor->InitializeForBuilding(mDataSource, this, rootnode);
    1788               0 :     if (NS_FAILED(rv))
    1789               0 :         return rv;
    1790                 : 
    1791                 :     // Set the "container" and "member" variables, if the user has specified
    1792                 :     // them. The container variable may be specified with the container
    1793                 :     // attribute on the <template> and the member variable may be specified
    1794                 :     // using the member attribute or the value of the uri attribute inside the
    1795                 :     // first action body in the template. If not specified, the container
    1796                 :     // variable defaults to '?uri' and the member variable defaults to '?' or
    1797                 :     // 'rdf:*' for simple queries.
    1798                 : 
    1799                 :     // For RDF queries, the container variable may also be set via the
    1800                 :     // <content> tag.
    1801                 : 
    1802               0 :     nsAutoString containervar;
    1803               0 :     tmpl->GetAttr(kNameSpaceID_None, nsGkAtoms::container, containervar);
    1804                 : 
    1805               0 :     if (containervar.IsEmpty())
    1806               0 :         mRefVariable = do_GetAtom("?uri");
    1807                 :     else
    1808               0 :         mRefVariable = do_GetAtom(containervar);
    1809                 : 
    1810               0 :     nsAutoString membervar;
    1811               0 :     tmpl->GetAttr(kNameSpaceID_None, nsGkAtoms::member, membervar);
    1812                 : 
    1813               0 :     if (membervar.IsEmpty())
    1814               0 :         mMemberVariable = nsnull;
    1815                 :     else
    1816               0 :         mMemberVariable = do_GetAtom(membervar);
    1817                 : 
    1818               0 :     nsTemplateQuerySet* queryset = new nsTemplateQuerySet(0);
    1819               0 :     if (!queryset)
    1820               0 :         return NS_ERROR_OUT_OF_MEMORY;
    1821                 : 
    1822               0 :     if (!mQuerySets.AppendElement(queryset)) {
    1823               0 :         delete queryset;
    1824               0 :         return NS_ERROR_OUT_OF_MEMORY;
    1825                 :     }
    1826                 : 
    1827               0 :     bool canUseTemplate = false;
    1828               0 :     PRInt32 priority = 0;
    1829               0 :     rv = CompileTemplate(tmpl, queryset, false, &priority, &canUseTemplate);
    1830                 : 
    1831               0 :     if (NS_FAILED(rv) || !canUseTemplate) {
    1832               0 :         for (PRInt32 q = mQuerySets.Length() - 1; q >= 0; q--) {
    1833               0 :             nsTemplateQuerySet* qs = mQuerySets[q];
    1834               0 :             delete qs;
    1835                 :         }
    1836               0 :         mQuerySets.Clear();
    1837                 :     }
    1838                 : 
    1839               0 :     mQueriesCompiled = true;
    1840                 : 
    1841               0 :     return NS_OK;
    1842                 : }
    1843                 : 
    1844                 : nsresult
    1845               0 : nsXULTemplateBuilder::CompileTemplate(nsIContent* aTemplate,
    1846                 :                                       nsTemplateQuerySet* aQuerySet,
    1847                 :                                       bool aIsQuerySet,
    1848                 :                                       PRInt32* aPriority,
    1849                 :                                       bool* aCanUseTemplate)
    1850                 : {
    1851               0 :     NS_ASSERTION(aQuerySet, "No queryset supplied");
    1852                 : 
    1853               0 :     nsresult rv = NS_OK;
    1854                 : 
    1855               0 :     bool isQuerySetMode = false;
    1856               0 :     bool hasQuerySet = false, hasRule = false, hasQuery = false;
    1857                 : 
    1858               0 :     for (nsIContent* rulenode = aTemplate->GetFirstChild();
    1859                 :          rulenode;
    1860               0 :          rulenode = rulenode->GetNextSibling()) {
    1861                 : 
    1862               0 :         nsINodeInfo *ni = rulenode->NodeInfo();
    1863                 : 
    1864                 :         // don't allow more queries than can be supported
    1865               0 :         if (*aPriority == PR_INT16_MAX)
    1866               0 :             return NS_ERROR_FAILURE;
    1867                 : 
    1868                 :         // XXXndeakin queryset isn't a good name for this tag since it only
    1869                 :         //            ever contains one query
    1870               0 :         if (!aIsQuerySet && ni->Equals(nsGkAtoms::queryset, kNameSpaceID_XUL)) {
    1871               0 :             if (hasRule || hasQuery) {
    1872               0 :               nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_INVALID_QUERYSET);
    1873               0 :               continue;
    1874                 :             }
    1875                 : 
    1876               0 :             isQuerySetMode = true;
    1877                 : 
    1878                 :             // only create a queryset for those after the first since the
    1879                 :             // first one is always created by CompileQueries
    1880               0 :             if (hasQuerySet) {
    1881               0 :                 aQuerySet = new nsTemplateQuerySet(++*aPriority);
    1882               0 :                 if (!aQuerySet)
    1883               0 :                     return NS_ERROR_OUT_OF_MEMORY;
    1884                 : 
    1885                 :                 // once the queryset is appended to the mQuerySets list, it
    1886                 :                 // will be removed by CompileQueries if an error occurs
    1887               0 :                 if (!mQuerySets.AppendElement(aQuerySet)) {
    1888               0 :                     delete aQuerySet;
    1889               0 :                     return NS_ERROR_OUT_OF_MEMORY;
    1890                 :                 }
    1891                 :             }
    1892                 : 
    1893               0 :             hasQuerySet = true;
    1894                 : 
    1895               0 :             rv = CompileTemplate(rulenode, aQuerySet, true, aPriority, aCanUseTemplate);
    1896               0 :             if (NS_FAILED(rv))
    1897               0 :                 return rv;
    1898                 :         }
    1899                 : 
    1900                 :         // once a queryset is used, everything must be a queryset
    1901               0 :         if (isQuerySetMode)
    1902               0 :             continue;
    1903                 : 
    1904               0 :         if (ni->Equals(nsGkAtoms::rule, kNameSpaceID_XUL)) {
    1905               0 :             nsCOMPtr<nsIContent> action;
    1906                 :             nsXULContentUtils::FindChildByTag(rulenode,
    1907                 :                                               kNameSpaceID_XUL,
    1908                 :                                               nsGkAtoms::action,
    1909               0 :                                               getter_AddRefs(action));
    1910                 : 
    1911               0 :             if (action){
    1912               0 :                 nsCOMPtr<nsIAtom> memberVariable = mMemberVariable;
    1913               0 :                 if (!memberVariable) {
    1914               0 :                     memberVariable = DetermineMemberVariable(action);
    1915               0 :                     if (!memberVariable) {
    1916               0 :                         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_NO_MEMBERVAR);
    1917               0 :                         continue;
    1918                 :                     }
    1919                 :                 }
    1920                 : 
    1921               0 :                 if (hasQuery) {
    1922               0 :                     nsCOMPtr<nsIAtom> tag;
    1923                 :                     DetermineRDFQueryRef(aQuerySet->mQueryNode,
    1924               0 :                                          getter_AddRefs(tag));
    1925               0 :                     if (tag)
    1926               0 :                         aQuerySet->SetTag(tag);
    1927                 : 
    1928               0 :                     if (! aQuerySet->mCompiledQuery) {
    1929               0 :                         nsCOMPtr<nsIDOMNode> query(do_QueryInterface(aQuerySet->mQueryNode));
    1930                 : 
    1931               0 :                         rv = mQueryProcessor->CompileQuery(this, query,
    1932                 :                                                            mRefVariable, memberVariable,
    1933               0 :                                                            getter_AddRefs(aQuerySet->mCompiledQuery));
    1934               0 :                         if (NS_FAILED(rv))
    1935               0 :                             return rv;
    1936                 :                     }
    1937                 : 
    1938               0 :                     if (aQuerySet->mCompiledQuery) {
    1939                 :                         rv = CompileExtendedQuery(rulenode, action, memberVariable,
    1940               0 :                                                   aQuerySet);
    1941               0 :                         if (NS_FAILED(rv))
    1942               0 :                             return rv;
    1943                 : 
    1944               0 :                         *aCanUseTemplate = true;
    1945                 :                     }
    1946                 :                 }
    1947                 :                 else {
    1948                 :                     // backwards-compatible RDF template syntax where there is
    1949                 :                     // an <action> node but no <query> node. In this case,
    1950                 :                     // use the conditions as if it was the query.
    1951                 : 
    1952               0 :                     nsCOMPtr<nsIContent> conditions;
    1953                 :                     nsXULContentUtils::FindChildByTag(rulenode,
    1954                 :                                                       kNameSpaceID_XUL,
    1955                 :                                                       nsGkAtoms::conditions,
    1956               0 :                                                       getter_AddRefs(conditions));
    1957                 : 
    1958               0 :                     if (conditions) {
    1959                 :                         // create a new queryset if one hasn't been created already
    1960               0 :                         if (hasQuerySet) {
    1961               0 :                             aQuerySet = new nsTemplateQuerySet(++*aPriority);
    1962               0 :                             if (! aQuerySet)
    1963               0 :                                 return NS_ERROR_OUT_OF_MEMORY;
    1964                 : 
    1965               0 :                             if (!mQuerySets.AppendElement(aQuerySet)) {
    1966               0 :                                 delete aQuerySet;
    1967               0 :                                 return NS_ERROR_OUT_OF_MEMORY;
    1968                 :                             }
    1969                 :                         }
    1970                 : 
    1971               0 :                         nsCOMPtr<nsIAtom> tag;
    1972               0 :                         DetermineRDFQueryRef(conditions, getter_AddRefs(tag));
    1973               0 :                         if (tag)
    1974               0 :                             aQuerySet->SetTag(tag);
    1975                 : 
    1976               0 :                         hasQuerySet = true;
    1977                 : 
    1978               0 :                         nsCOMPtr<nsIDOMNode> conditionsnode(do_QueryInterface(conditions));
    1979                 : 
    1980               0 :                         aQuerySet->mQueryNode = conditions;
    1981               0 :                         rv = mQueryProcessor->CompileQuery(this, conditionsnode,
    1982                 :                                                            mRefVariable,
    1983                 :                                                            memberVariable,
    1984               0 :                                                            getter_AddRefs(aQuerySet->mCompiledQuery));
    1985               0 :                         if (NS_FAILED(rv))
    1986               0 :                             return rv;
    1987                 : 
    1988               0 :                         if (aQuerySet->mCompiledQuery) {
    1989                 :                             rv = CompileExtendedQuery(rulenode, action, memberVariable,
    1990               0 :                                                       aQuerySet);
    1991               0 :                             if (NS_FAILED(rv))
    1992               0 :                                 return rv;
    1993                 : 
    1994               0 :                             *aCanUseTemplate = true;
    1995                 :                         }
    1996                 :                     }
    1997                 :                 }
    1998                 :             }
    1999                 :             else {
    2000               0 :                 if (hasQuery)
    2001               0 :                     continue;
    2002                 : 
    2003                 :                 // a new queryset must always be created in this case
    2004               0 :                 if (hasQuerySet) {
    2005               0 :                     aQuerySet = new nsTemplateQuerySet(++*aPriority);
    2006               0 :                     if (! aQuerySet)
    2007               0 :                         return NS_ERROR_OUT_OF_MEMORY;
    2008                 : 
    2009               0 :                     if (!mQuerySets.AppendElement(aQuerySet)) {
    2010               0 :                         delete aQuerySet;
    2011               0 :                         return NS_ERROR_OUT_OF_MEMORY;
    2012                 :                     }
    2013                 :                 }
    2014                 : 
    2015               0 :                 hasQuerySet = true;
    2016                 : 
    2017               0 :                 rv = CompileSimpleQuery(rulenode, aQuerySet, aCanUseTemplate);
    2018               0 :                 if (NS_FAILED(rv))
    2019               0 :                     return rv;
    2020                 :             }
    2021                 : 
    2022               0 :             hasRule = true;
    2023                 :         }
    2024               0 :         else if (ni->Equals(nsGkAtoms::query, kNameSpaceID_XUL)) {
    2025               0 :             if (hasQuery)
    2026               0 :               continue;
    2027                 : 
    2028               0 :             aQuerySet->mQueryNode = rulenode;
    2029               0 :             hasQuery = true;
    2030                 :         }
    2031               0 :         else if (ni->Equals(nsGkAtoms::action, kNameSpaceID_XUL)) {
    2032                 :             // the query must appear before the action
    2033               0 :             if (! hasQuery)
    2034               0 :                 continue;
    2035                 : 
    2036               0 :             nsCOMPtr<nsIAtom> tag;
    2037               0 :             DetermineRDFQueryRef(aQuerySet->mQueryNode, getter_AddRefs(tag));
    2038               0 :             if (tag)
    2039               0 :                 aQuerySet->SetTag(tag);
    2040                 : 
    2041               0 :             nsCOMPtr<nsIAtom> memberVariable = mMemberVariable;
    2042               0 :             if (!memberVariable) {
    2043               0 :                 memberVariable = DetermineMemberVariable(rulenode);
    2044               0 :                 if (!memberVariable) {
    2045               0 :                     nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_NO_MEMBERVAR);
    2046               0 :                     continue;
    2047                 :                 }
    2048                 :             }
    2049                 : 
    2050               0 :             nsCOMPtr<nsIDOMNode> query(do_QueryInterface(aQuerySet->mQueryNode));
    2051                 : 
    2052               0 :             rv = mQueryProcessor->CompileQuery(this, query,
    2053                 :                                                mRefVariable, memberVariable,
    2054               0 :                                                getter_AddRefs(aQuerySet->mCompiledQuery));
    2055                 : 
    2056               0 :             if (aQuerySet->mCompiledQuery) {
    2057               0 :                 nsTemplateRule* rule = aQuerySet->NewRule(aTemplate, rulenode, aQuerySet);
    2058               0 :                 if (! rule)
    2059               0 :                     return NS_ERROR_OUT_OF_MEMORY;
    2060                 : 
    2061               0 :                 rule->SetVars(mRefVariable, memberVariable);
    2062                 : 
    2063               0 :                 *aCanUseTemplate = true;
    2064                 : 
    2065               0 :                 return NS_OK;
    2066                 :             }
    2067                 :         }
    2068                 :     }
    2069                 : 
    2070               0 :     if (! hasRule && ! hasQuery && ! hasQuerySet) {
    2071                 :         // if no rules are specified in the template, then the contents of the
    2072                 :         // <template> tag are the one-and-only template.
    2073               0 :         rv = CompileSimpleQuery(aTemplate, aQuerySet, aCanUseTemplate);
    2074                 :      }
    2075                 : 
    2076               0 :     return rv;
    2077                 : }
    2078                 : 
    2079                 : nsresult
    2080               0 : nsXULTemplateBuilder::CompileExtendedQuery(nsIContent* aRuleElement,
    2081                 :                                            nsIContent* aActionElement,
    2082                 :                                            nsIAtom* aMemberVariable,
    2083                 :                                            nsTemplateQuerySet* aQuerySet)
    2084                 : {
    2085                 :     // Compile an "extended" <template> rule. An extended rule may have
    2086                 :     // a <conditions> child, an <action> child, and a <bindings> child.
    2087                 :     nsresult rv;
    2088                 : 
    2089               0 :     nsTemplateRule* rule = aQuerySet->NewRule(aRuleElement, aActionElement, aQuerySet);
    2090               0 :     if (! rule)
    2091               0 :          return NS_ERROR_OUT_OF_MEMORY;
    2092                 : 
    2093               0 :     nsCOMPtr<nsIContent> conditions;
    2094                 :     nsXULContentUtils::FindChildByTag(aRuleElement,
    2095                 :                                       kNameSpaceID_XUL,
    2096                 :                                       nsGkAtoms::conditions,
    2097               0 :                                       getter_AddRefs(conditions));
    2098                 : 
    2099                 :     // allow the conditions to be placed directly inside the rule
    2100               0 :     if (!conditions)
    2101               0 :         conditions = aRuleElement;
    2102                 :   
    2103               0 :     rv = CompileConditions(rule, conditions);
    2104                 :     // If the rule compilation failed, then we have to bail.
    2105               0 :     if (NS_FAILED(rv)) {
    2106               0 :         aQuerySet->RemoveRule(rule);
    2107               0 :         return rv;
    2108                 :     }
    2109                 : 
    2110               0 :     rule->SetVars(mRefVariable, aMemberVariable);
    2111                 : 
    2112                 :     // If we've got bindings, add 'em.
    2113               0 :     nsCOMPtr<nsIContent> bindings;
    2114                 :     nsXULContentUtils::FindChildByTag(aRuleElement,
    2115                 :                                       kNameSpaceID_XUL,
    2116                 :                                       nsGkAtoms::bindings,
    2117               0 :                                       getter_AddRefs(bindings));
    2118                 : 
    2119                 :     // allow bindings to be placed directly inside rule
    2120               0 :     if (!bindings)
    2121               0 :         bindings = aRuleElement;
    2122                 : 
    2123               0 :     rv = CompileBindings(rule, bindings);
    2124               0 :     NS_ENSURE_SUCCESS(rv, rv);
    2125                 : 
    2126               0 :     return NS_OK;
    2127                 : }
    2128                 : 
    2129                 : already_AddRefed<nsIAtom>
    2130               0 : nsXULTemplateBuilder::DetermineMemberVariable(nsIContent* aElement)
    2131                 : {
    2132                 :     // recursively iterate over the children looking for an element
    2133                 :     // with uri="?..."
    2134               0 :     for (nsIContent* child = aElement->GetFirstChild();
    2135                 :          child;
    2136               0 :          child = child->GetNextSibling()) {
    2137               0 :         nsAutoString uri;
    2138               0 :         child->GetAttr(kNameSpaceID_None, nsGkAtoms::uri, uri);
    2139               0 :         if (!uri.IsEmpty() && uri[0] == PRUnichar('?')) {
    2140               0 :             return NS_NewAtom(uri);
    2141                 :         }
    2142                 : 
    2143               0 :         nsCOMPtr<nsIAtom> result = DetermineMemberVariable(child);
    2144               0 :         if (result) {
    2145               0 :             return result.forget();
    2146                 :         }
    2147                 :     }
    2148                 : 
    2149               0 :     return nsnull;
    2150                 : }
    2151                 : 
    2152                 : void
    2153               0 : nsXULTemplateBuilder::DetermineRDFQueryRef(nsIContent* aQueryElement, nsIAtom** aTag)
    2154                 : {
    2155                 :     // check for a tag
    2156               0 :     nsCOMPtr<nsIContent> content;
    2157                 :     nsXULContentUtils::FindChildByTag(aQueryElement,
    2158                 :                                       kNameSpaceID_XUL,
    2159                 :                                       nsGkAtoms::content,
    2160               0 :                                       getter_AddRefs(content));
    2161                 : 
    2162               0 :     if (! content) {
    2163                 :         // look for older treeitem syntax as well
    2164                 :         nsXULContentUtils::FindChildByTag(aQueryElement,
    2165                 :                                           kNameSpaceID_XUL,
    2166                 :                                           nsGkAtoms::treeitem,
    2167               0 :                                           getter_AddRefs(content));
    2168                 :     }
    2169                 : 
    2170               0 :     if (content) {
    2171               0 :         nsAutoString uri;
    2172               0 :         content->GetAttr(kNameSpaceID_None, nsGkAtoms::uri, uri);
    2173                 : 
    2174               0 :         if (!uri.IsEmpty())
    2175               0 :             mRefVariable = do_GetAtom(uri);
    2176                 : 
    2177               0 :         nsAutoString tag;
    2178               0 :         content->GetAttr(kNameSpaceID_None, nsGkAtoms::tag, tag);
    2179                 : 
    2180               0 :         if (!tag.IsEmpty())
    2181               0 :             *aTag = NS_NewAtom(tag);
    2182                 :     }
    2183               0 : }
    2184                 : 
    2185                 : nsresult
    2186               0 : nsXULTemplateBuilder::CompileSimpleQuery(nsIContent* aRuleElement,
    2187                 :                                          nsTemplateQuerySet* aQuerySet,
    2188                 :                                          bool* aCanUseTemplate)
    2189                 : {
    2190                 :     // compile a simple query, which is a query with no <query> or
    2191                 :     // <conditions>. This means that a default query is used.
    2192               0 :     nsCOMPtr<nsIDOMNode> query(do_QueryInterface(aRuleElement));
    2193                 : 
    2194               0 :     nsCOMPtr<nsIAtom> memberVariable;
    2195               0 :     if (mMemberVariable)
    2196               0 :         memberVariable = mMemberVariable;
    2197                 :     else
    2198               0 :         memberVariable = do_GetAtom("rdf:*");
    2199                 : 
    2200                 :     // since there is no <query> node for a simple query, the query node will
    2201                 :     // be either the <rule> node if multiple rules are used, or the <template> node.
    2202               0 :     aQuerySet->mQueryNode = aRuleElement;
    2203               0 :     nsresult rv = mQueryProcessor->CompileQuery(this, query,
    2204                 :                                                 mRefVariable, memberVariable,
    2205               0 :                                                 getter_AddRefs(aQuerySet->mCompiledQuery));
    2206               0 :     if (NS_FAILED(rv))
    2207               0 :         return rv;
    2208                 : 
    2209               0 :     if (! aQuerySet->mCompiledQuery) {
    2210               0 :         *aCanUseTemplate = false;
    2211               0 :         return NS_OK;
    2212                 :     }
    2213                 : 
    2214               0 :     nsTemplateRule* rule = aQuerySet->NewRule(aRuleElement, aRuleElement, aQuerySet);
    2215               0 :     if (! rule)
    2216               0 :         return NS_ERROR_OUT_OF_MEMORY;
    2217                 : 
    2218               0 :     rule->SetVars(mRefVariable, memberVariable);
    2219                 : 
    2220               0 :     nsAutoString tag;
    2221               0 :     aRuleElement->GetAttr(kNameSpaceID_None, nsGkAtoms::parent, tag);
    2222                 : 
    2223               0 :     if (!tag.IsEmpty()) {
    2224               0 :         nsCOMPtr<nsIAtom> tagatom = do_GetAtom(tag);
    2225               0 :         aQuerySet->SetTag(tagatom);
    2226                 :     }
    2227                 : 
    2228               0 :     *aCanUseTemplate = true;
    2229                 : 
    2230               0 :     return AddSimpleRuleBindings(rule, aRuleElement);
    2231                 : }
    2232                 : 
    2233                 : nsresult
    2234               0 : nsXULTemplateBuilder::CompileConditions(nsTemplateRule* aRule,
    2235                 :                                         nsIContent* aCondition)
    2236                 : {
    2237               0 :     nsAutoString tag;
    2238               0 :     aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::parent, tag);
    2239                 : 
    2240               0 :     if (!tag.IsEmpty()) {
    2241               0 :         nsCOMPtr<nsIAtom> tagatom = do_GetAtom(tag);
    2242               0 :         aRule->SetTag(tagatom);
    2243                 :     }
    2244                 : 
    2245               0 :     nsTemplateCondition* currentCondition = nsnull;
    2246                 : 
    2247               0 :     for (nsIContent* node = aCondition->GetFirstChild();
    2248                 :          node;
    2249               0 :          node = node->GetNextSibling()) {
    2250                 : 
    2251               0 :         if (node->NodeInfo()->Equals(nsGkAtoms::where, kNameSpaceID_XUL)) {
    2252               0 :             nsresult rv = CompileWhereCondition(aRule, node, &currentCondition);
    2253               0 :             if (NS_FAILED(rv))
    2254               0 :                 return rv;
    2255                 :         }
    2256                 :     }
    2257                 : 
    2258               0 :     return NS_OK;
    2259                 : }
    2260                 : 
    2261                 : nsresult
    2262               0 : nsXULTemplateBuilder::CompileWhereCondition(nsTemplateRule* aRule,
    2263                 :                                             nsIContent* aCondition,
    2264                 :                                             nsTemplateCondition** aCurrentCondition)
    2265                 : {
    2266                 :     // Compile a <where> condition, which must be of the form:
    2267                 :     //
    2268                 :     //   <where subject="?var1|string" rel="relation" value="?var2|string" />
    2269                 :     //
    2270                 :     //    The value of rel may be:
    2271                 :     //      equal - subject must be equal to object
    2272                 :     //      notequal - subject must not be equal to object
    2273                 :     //      less - subject must be less than object
    2274                 :     //      greater - subject must be greater than object
    2275                 :     //      startswith - subject must start with object
    2276                 :     //      endswith - subject must end with object
    2277                 :     //      contains - subject must contain object
    2278                 :     //    Comparisons are done as strings unless the subject is an integer.
    2279                 : 
    2280                 :     // subject
    2281               0 :     nsAutoString subject;
    2282               0 :     aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::subject, subject);
    2283               0 :     if (subject.IsEmpty()) {
    2284               0 :         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_WHERE_NO_SUBJECT);
    2285               0 :         return NS_OK;
    2286                 :     }
    2287                 : 
    2288               0 :     nsCOMPtr<nsIAtom> svar;
    2289               0 :     if (subject[0] == PRUnichar('?'))
    2290               0 :         svar = do_GetAtom(subject);
    2291                 : 
    2292               0 :     nsAutoString relstring;
    2293               0 :     aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, relstring);
    2294               0 :     if (relstring.IsEmpty()) {
    2295               0 :         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_WHERE_NO_RELATION);
    2296               0 :         return NS_OK;
    2297                 :     }
    2298                 : 
    2299                 :     // object
    2300               0 :     nsAutoString value;
    2301               0 :     aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::value, value);
    2302               0 :     if (value.IsEmpty()) {
    2303               0 :         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_WHERE_NO_VALUE);
    2304               0 :         return NS_OK;
    2305                 :     }
    2306                 : 
    2307                 :     // multiple
    2308                 :     bool shouldMultiple =
    2309                 :       aCondition->AttrValueIs(kNameSpaceID_None, nsGkAtoms::multiple,
    2310               0 :                               nsGkAtoms::_true, eCaseMatters);
    2311                 : 
    2312               0 :     nsCOMPtr<nsIAtom> vvar;
    2313               0 :     if (!shouldMultiple && (value[0] == PRUnichar('?'))) {
    2314               0 :         vvar = do_GetAtom(value);
    2315                 :     }
    2316                 : 
    2317                 :     // ignorecase
    2318                 :     bool shouldIgnoreCase =
    2319                 :       aCondition->AttrValueIs(kNameSpaceID_None, nsGkAtoms::ignorecase,
    2320               0 :                               nsGkAtoms::_true, eCaseMatters);
    2321                 : 
    2322                 :     // negate
    2323                 :     bool shouldNegate =
    2324                 :       aCondition->AttrValueIs(kNameSpaceID_None, nsGkAtoms::negate,
    2325               0 :                               nsGkAtoms::_true, eCaseMatters);
    2326                 : 
    2327                 :     nsTemplateCondition* condition;
    2328                 : 
    2329               0 :     if (svar && vvar) {
    2330                 :         condition = new nsTemplateCondition(svar, relstring, vvar,
    2331               0 :                                             shouldIgnoreCase, shouldNegate);
    2332                 :     }
    2333               0 :     else if (svar && !value.IsEmpty()) {
    2334                 :         condition = new nsTemplateCondition(svar, relstring, value,
    2335               0 :                                             shouldIgnoreCase, shouldNegate, shouldMultiple);
    2336                 :     }
    2337               0 :     else if (vvar) {
    2338                 :         condition = new nsTemplateCondition(subject, relstring, vvar,
    2339               0 :                                             shouldIgnoreCase, shouldNegate);
    2340                 :     }
    2341                 :     else {
    2342               0 :         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_WHERE_NO_VAR);
    2343               0 :         return NS_OK;
    2344                 :     }
    2345                 : 
    2346               0 :     if (! condition)
    2347               0 :         return NS_ERROR_OUT_OF_MEMORY;
    2348                 : 
    2349               0 :     if (*aCurrentCondition) {
    2350               0 :         (*aCurrentCondition)->SetNext(condition);
    2351                 :     }
    2352                 :     else {
    2353               0 :         aRule->SetCondition(condition);
    2354                 :     }
    2355                 : 
    2356               0 :     *aCurrentCondition = condition;
    2357                 : 
    2358               0 :     return NS_OK;
    2359                 : }
    2360                 : 
    2361                 : nsresult
    2362               0 : nsXULTemplateBuilder::CompileBindings(nsTemplateRule* aRule, nsIContent* aBindings)
    2363                 : {
    2364                 :     // Add an extended rule's bindings.
    2365                 :     nsresult rv;
    2366                 : 
    2367               0 :     for (nsIContent* binding = aBindings->GetFirstChild();
    2368                 :          binding;
    2369               0 :          binding = binding->GetNextSibling()) {
    2370                 : 
    2371               0 :         if (binding->NodeInfo()->Equals(nsGkAtoms::binding,
    2372               0 :                                         kNameSpaceID_XUL)) {
    2373               0 :             rv = CompileBinding(aRule, binding);
    2374               0 :             if (NS_FAILED(rv))
    2375               0 :                 return rv;
    2376                 :         }
    2377                 :     }
    2378                 : 
    2379               0 :     aRule->AddBindingsToQueryProcessor(mQueryProcessor);
    2380                 : 
    2381               0 :     return NS_OK;
    2382                 : }
    2383                 : 
    2384                 : 
    2385                 : nsresult
    2386               0 : nsXULTemplateBuilder::CompileBinding(nsTemplateRule* aRule,
    2387                 :                                      nsIContent* aBinding)
    2388                 : {
    2389                 :     // Compile a <binding> "condition", which must be of the form:
    2390                 :     //
    2391                 :     //   <binding subject="?var1"
    2392                 :     //            predicate="resource"
    2393                 :     //            object="?var2" />
    2394                 :     //
    2395                 :     // XXXwaterson Some day it would be cool to allow the 'predicate'
    2396                 :     // to be bound to a variable.
    2397                 : 
    2398                 :     // subject
    2399               0 :     nsAutoString subject;
    2400               0 :     aBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::subject, subject);
    2401               0 :     if (subject.IsEmpty()) {
    2402               0 :         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_SUBJECT);
    2403               0 :         return NS_OK;
    2404                 :     }
    2405                 : 
    2406               0 :     nsCOMPtr<nsIAtom> svar;
    2407               0 :     if (subject[0] == PRUnichar('?')) {
    2408               0 :         svar = do_GetAtom(subject);
    2409                 :     }
    2410                 :     else {
    2411               0 :         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_SUBJECT);
    2412               0 :         return NS_OK;
    2413                 :     }
    2414                 : 
    2415                 :     // predicate
    2416               0 :     nsAutoString predicate;
    2417               0 :     aBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::predicate, predicate);
    2418               0 :     if (predicate.IsEmpty()) {
    2419               0 :         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_PREDICATE);
    2420               0 :         return NS_OK;
    2421                 :     }
    2422                 : 
    2423                 :     // object
    2424               0 :     nsAutoString object;
    2425               0 :     aBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::object, object);
    2426                 : 
    2427               0 :     if (object.IsEmpty()) {
    2428               0 :         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_OBJECT);
    2429               0 :         return NS_OK;
    2430                 :     }
    2431                 : 
    2432               0 :     nsCOMPtr<nsIAtom> ovar;
    2433               0 :     if (object[0] == PRUnichar('?')) {
    2434               0 :         ovar = do_GetAtom(object);
    2435                 :     }
    2436                 :     else {
    2437               0 :         nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_OBJECT);
    2438               0 :         return NS_OK;
    2439                 :     }
    2440                 : 
    2441               0 :     return aRule->AddBinding(svar, predicate, ovar);
    2442                 : }
    2443                 : 
    2444                 : nsresult
    2445               0 : nsXULTemplateBuilder::AddSimpleRuleBindings(nsTemplateRule* aRule,
    2446                 :                                             nsIContent* aElement)
    2447                 : {
    2448                 :     // Crawl the content tree of a "simple" rule, adding a variable
    2449                 :     // assignment for any attribute whose value is "rdf:".
    2450                 : 
    2451               0 :     nsAutoTArray<nsIContent*, 8> elements;
    2452                 : 
    2453               0 :     if (elements.AppendElement(aElement) == nsnull)
    2454               0 :         return NS_ERROR_OUT_OF_MEMORY;
    2455                 : 
    2456               0 :     while (elements.Length()) {
    2457                 :         // Pop the next element off the stack
    2458               0 :         PRUint32 i = elements.Length() - 1;
    2459               0 :         nsIContent* element = elements[i];
    2460               0 :         elements.RemoveElementAt(i);
    2461                 : 
    2462                 :         // Iterate through its attributes, looking for substitutions
    2463                 :         // that we need to add as bindings.
    2464               0 :         PRUint32 count = element->GetAttrCount();
    2465                 : 
    2466               0 :         for (i = 0; i < count; ++i) {
    2467               0 :             const nsAttrName* name = element->GetAttrNameAt(i);
    2468                 : 
    2469               0 :             if (!name->Equals(nsGkAtoms::id, kNameSpaceID_None) &&
    2470               0 :                 !name->Equals(nsGkAtoms::uri, kNameSpaceID_None)) {
    2471               0 :                 nsAutoString value;
    2472               0 :                 element->GetAttr(name->NamespaceID(), name->LocalName(), value);
    2473                 : 
    2474                 :                 // Scan the attribute for variables, adding a binding for
    2475                 :                 // each one.
    2476               0 :                 ParseAttribute(value, AddBindingsFor, nsnull, aRule);
    2477                 :             }
    2478                 :         }
    2479                 : 
    2480                 :         // Push kids onto the stack, and search them next.
    2481               0 :         for (nsIContent* child = element->GetLastChild();
    2482                 :              child;
    2483               0 :              child = child->GetPreviousSibling()) {
    2484                 : 
    2485               0 :             if (!elements.AppendElement(child))
    2486               0 :                 return NS_ERROR_OUT_OF_MEMORY;
    2487                 :         }
    2488                 :     }
    2489                 : 
    2490               0 :     aRule->AddBindingsToQueryProcessor(mQueryProcessor);
    2491                 : 
    2492               0 :     return NS_OK;
    2493                 : }
    2494                 : 
    2495                 : void
    2496               0 : nsXULTemplateBuilder::AddBindingsFor(nsXULTemplateBuilder* aThis,
    2497                 :                                      const nsAString& aVariable,
    2498                 :                                      void* aClosure)
    2499                 : {
    2500                 :     // We should *only* be recieving "rdf:"-style variables. Make
    2501                 :     // sure...
    2502               0 :     if (!StringBeginsWith(aVariable, NS_LITERAL_STRING("rdf:")))
    2503               0 :         return;
    2504                 : 
    2505               0 :     nsTemplateRule* rule = static_cast<nsTemplateRule*>(aClosure);
    2506                 : 
    2507               0 :     nsCOMPtr<nsIAtom> var = do_GetAtom(aVariable);
    2508                 : 
    2509                 :     // Strip it down to the raw RDF property by clobbering the "rdf:"
    2510                 :     // prefix
    2511               0 :     nsAutoString property;
    2512               0 :     property.Assign(Substring(aVariable, PRUint32(4), aVariable.Length() - 4));
    2513                 : 
    2514               0 :     if (! rule->HasBinding(rule->GetMemberVariable(), property, var))
    2515                 :         // In the simple syntax, the binding is always from the
    2516                 :         // member variable, through the property, to the target.
    2517               0 :         rule->AddBinding(rule->GetMemberVariable(), property, var);
    2518                 : }
    2519                 : 
    2520                 : 
    2521                 : nsresult
    2522               0 : nsXULTemplateBuilder::IsSystemPrincipal(nsIPrincipal *principal, bool *result)
    2523                 : {
    2524               0 :   if (!gSystemPrincipal)
    2525               0 :     return NS_ERROR_UNEXPECTED;
    2526                 : 
    2527               0 :   *result = (principal == gSystemPrincipal);
    2528               0 :   return NS_OK;
    2529                 : }
    2530                 : 
    2531                 : bool
    2532               0 : nsXULTemplateBuilder::IsActivated(nsIRDFResource *aResource)
    2533                 : {
    2534               0 :     for (ActivationEntry *entry = mTop;
    2535                 :          entry != nsnull;
    2536                 :          entry = entry->mPrevious) {
    2537               0 :         if (entry->mResource == aResource)
    2538               0 :             return true;
    2539                 :     }
    2540               0 :     return false;
    2541                 : }
    2542                 : 
    2543                 : nsresult
    2544               0 : nsXULTemplateBuilder::GetResultResource(nsIXULTemplateResult* aResult,
    2545                 :                                         nsIRDFResource** aResource)
    2546                 : {
    2547                 :     // get the resource for a result by checking its resource property. If it
    2548                 :     // is not set, check the id. This allows non-chrome implementations to
    2549                 :     // avoid having to use RDF.
    2550               0 :     nsresult rv = aResult->GetResource(aResource);
    2551               0 :     if (NS_FAILED(rv))
    2552               0 :         return rv;
    2553                 : 
    2554               0 :     if (! *aResource) {
    2555               0 :         nsAutoString id;
    2556               0 :         rv = aResult->GetId(id);
    2557               0 :         if (NS_FAILED(rv))
    2558               0 :             return rv;
    2559                 : 
    2560               0 :         return gRDFService->GetUnicodeResource(id, aResource);
    2561                 :     }
    2562                 : 
    2563               0 :     return rv;
    2564                 : }
    2565                 : 
    2566                 : 
    2567                 : void
    2568               0 : nsXULTemplateBuilder::OutputMatchToLog(nsIRDFResource* aId,
    2569                 :                                        nsTemplateMatch* aMatch,
    2570                 :                                        bool aIsNew)
    2571                 : {
    2572               0 :     PRInt32 priority = aMatch->QuerySetPriority() + 1;
    2573               0 :     PRInt32 activePriority = -1;
    2574                 : 
    2575               0 :     nsAutoString msg;
    2576                 : 
    2577               0 :     nsAutoString templateid;
    2578               0 :     mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::id, templateid);
    2579               0 :     msg.AppendLiteral("In template");
    2580               0 :     if (!templateid.IsEmpty()) {
    2581               0 :         msg.AppendLiteral(" with id ");
    2582               0 :         msg.Append(templateid);
    2583                 :     }
    2584                 : 
    2585               0 :     nsAutoString refstring;
    2586               0 :     aMatch->mResult->GetBindingFor(mRefVariable, refstring);
    2587               0 :     if (!refstring.IsEmpty()) {
    2588               0 :         msg.AppendLiteral(" using ref ");
    2589               0 :         msg.Append(refstring);
    2590                 :     }
    2591                 : 
    2592               0 :     msg.AppendLiteral("\n    ");
    2593                 : 
    2594               0 :     nsTemplateMatch* match = nsnull;
    2595               0 :     if (mMatchMap.Get(aId, &match)){
    2596               0 :         while (match) {
    2597               0 :             if (match == aMatch)
    2598               0 :                 break;
    2599               0 :             if (match->IsActive() &&
    2600               0 :                 match->GetContainer() == aMatch->GetContainer()) {
    2601               0 :                 activePriority = match->QuerySetPriority() + 1;
    2602               0 :                 break;
    2603                 :             }
    2604               0 :             match = match->mNext;
    2605                 :         }
    2606                 :     }
    2607                 : 
    2608               0 :     if (aMatch->IsActive()) {
    2609               0 :         if (aIsNew) {
    2610               0 :             msg.AppendLiteral("New active result for query ");
    2611               0 :             msg.AppendInt(priority);
    2612               0 :             msg.AppendLiteral(" matching rule ");
    2613               0 :             msg.AppendInt(aMatch->RuleIndex() + 1);
    2614                 :         }
    2615                 :         else {
    2616               0 :             msg.AppendLiteral("Removed active result for query ");
    2617               0 :             msg.AppendInt(priority);
    2618               0 :             if (activePriority > 0) {
    2619               0 :                 msg.AppendLiteral(" (new active query is ");
    2620               0 :                 msg.AppendInt(activePriority);
    2621               0 :                 msg.Append(')');
    2622                 :             }
    2623                 :             else {
    2624               0 :                 msg.AppendLiteral(" (no new active query)");
    2625                 :             }
    2626                 :         }
    2627                 :     }
    2628                 :     else {
    2629               0 :         if (aIsNew) {
    2630               0 :             msg.AppendLiteral("New inactive result for query ");
    2631               0 :             msg.AppendInt(priority);
    2632               0 :             if (activePriority > 0) {
    2633               0 :                 msg.AppendLiteral(" (overridden by query ");
    2634               0 :                 msg.AppendInt(activePriority);
    2635               0 :                 msg.Append(')');
    2636                 :             }
    2637                 :             else {
    2638               0 :                 msg.AppendLiteral(" (didn't match a rule)");
    2639                 :             }
    2640                 :         }
    2641                 :         else {
    2642               0 :             msg.AppendLiteral("Removed inactive result for query ");
    2643               0 :             msg.AppendInt(priority);
    2644               0 :             if (activePriority > 0) {
    2645               0 :                 msg.AppendLiteral(" (active query is ");
    2646               0 :                 msg.AppendInt(activePriority);
    2647               0 :                 msg.Append(')');
    2648                 :             }
    2649                 :             else {
    2650               0 :                 msg.AppendLiteral(" (no active query)");
    2651                 :             }
    2652                 :         }
    2653                 :     }
    2654                 : 
    2655               0 :     nsAutoString idstring;
    2656               0 :     nsXULContentUtils::GetTextForNode(aId, idstring);
    2657               0 :     msg.AppendLiteral(": ");
    2658               0 :     msg.Append(idstring);
    2659                 : 
    2660               0 :     nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
    2661               0 :     if (cs)
    2662               0 :       cs->LogStringMessage(msg.get());
    2663            4392 : }

Generated by: LCOV version 1.7