content/xul/templates/src/nsXULTemplateBuilder.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/xul/templates/src/nsXULTemplateBuilder.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2600 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +/*
    1.10 +
    1.11 +  Builds content from a datasource using the XUL <template> tag.
    1.12 +
    1.13 +  TO DO
    1.14 +
    1.15 +  . Fix ContentTagTest's location in the network construction
    1.16 +
    1.17 +  To turn on logging for this module, set:
    1.18 +
    1.19 +    NSPR_LOG_MODULES nsXULTemplateBuilder:5
    1.20 +
    1.21 + */
    1.22 +
    1.23 +#include "nsCOMPtr.h"
    1.24 +#include "nsCRT.h"
    1.25 +#include "nsIContent.h"
    1.26 +#include "nsIDOMElement.h"
    1.27 +#include "nsIDOMNode.h"
    1.28 +#include "nsIDOMDocument.h"
    1.29 +#include "nsIDOMXMLDocument.h"
    1.30 +#include "nsIDOMXULElement.h"
    1.31 +#include "nsIDocument.h"
    1.32 +#include "nsBindingManager.h"
    1.33 +#include "nsIDOMNodeList.h"
    1.34 +#include "nsIObserverService.h"
    1.35 +#include "nsIRDFCompositeDataSource.h"
    1.36 +#include "nsIRDFInferDataSource.h"
    1.37 +#include "nsIRDFContainerUtils.h"
    1.38 +#include "nsIXULDocument.h"
    1.39 +#include "nsIXULTemplateBuilder.h"
    1.40 +#include "nsIXULBuilderListener.h"
    1.41 +#include "nsIRDFRemoteDataSource.h"
    1.42 +#include "nsIRDFService.h"
    1.43 +#include "nsIScriptContext.h"
    1.44 +#include "nsIScriptGlobalObject.h"
    1.45 +#include "nsIServiceManager.h"
    1.46 +#include "nsISimpleEnumerator.h"
    1.47 +#include "nsIMutableArray.h"
    1.48 +#include "nsIURL.h"
    1.49 +#include "nsIXPConnect.h"
    1.50 +#include "nsContentCID.h"
    1.51 +#include "nsNameSpaceManager.h"
    1.52 +#include "nsRDFCID.h"
    1.53 +#include "nsXULContentUtils.h"
    1.54 +#include "nsString.h"
    1.55 +#include "nsTArray.h"
    1.56 +#include "nsXPIDLString.h"
    1.57 +#include "nsWhitespaceTokenizer.h"
    1.58 +#include "nsGkAtoms.h"
    1.59 +#include "nsXULElement.h"
    1.60 +#include "jsapi.h"
    1.61 +#include "prlog.h"
    1.62 +#include "rdf.h"
    1.63 +#include "pldhash.h"
    1.64 +#include "plhash.h"
    1.65 +#include "nsDOMClassInfoID.h"
    1.66 +#include "nsPIDOMWindow.h"
    1.67 +#include "nsIConsoleService.h" 
    1.68 +#include "nsNetUtil.h"
    1.69 +#include "nsXULTemplateBuilder.h"
    1.70 +#include "nsXULTemplateQueryProcessorRDF.h"
    1.71 +#include "nsXULTemplateQueryProcessorXML.h"
    1.72 +#include "nsXULTemplateQueryProcessorStorage.h"
    1.73 +#include "nsContentUtils.h"
    1.74 +#include "ChildIterator.h"
    1.75 +#include "mozilla/dom/ScriptSettings.h"
    1.76 +
    1.77 +using namespace mozilla::dom;
    1.78 +using namespace mozilla;
    1.79 +
    1.80 +//----------------------------------------------------------------------
    1.81 +//
    1.82 +// nsXULTemplateBuilder
    1.83 +//
    1.84 +
    1.85 +nsrefcnt                  nsXULTemplateBuilder::gRefCnt = 0;
    1.86 +nsIRDFService*            nsXULTemplateBuilder::gRDFService;
    1.87 +nsIRDFContainerUtils*     nsXULTemplateBuilder::gRDFContainerUtils;
    1.88 +nsIScriptSecurityManager* nsXULTemplateBuilder::gScriptSecurityManager;
    1.89 +nsIPrincipal*             nsXULTemplateBuilder::gSystemPrincipal;
    1.90 +nsIObserverService*       nsXULTemplateBuilder::gObserverService;
    1.91 +
    1.92 +#ifdef PR_LOGGING
    1.93 +PRLogModuleInfo* gXULTemplateLog;
    1.94 +#endif
    1.95 +
    1.96 +#define NS_QUERY_PROCESSOR_CONTRACTID_PREFIX "@mozilla.org/xul/xul-query-processor;1?name="
    1.97 +
    1.98 +//----------------------------------------------------------------------
    1.99 +//
   1.100 +// nsXULTemplateBuilder methods
   1.101 +//
   1.102 +
   1.103 +nsXULTemplateBuilder::nsXULTemplateBuilder(void)
   1.104 +    : mQueriesCompiled(false),
   1.105 +      mFlags(0),
   1.106 +      mTop(nullptr),
   1.107 +      mObservedDocument(nullptr)
   1.108 +{
   1.109 +    MOZ_COUNT_CTOR(nsXULTemplateBuilder);
   1.110 +}
   1.111 +
   1.112 +static PLDHashOperator
   1.113 +DestroyMatchList(nsISupports* aKey, nsTemplateMatch*& aMatch, void* aContext)
   1.114 +{
   1.115 +    // delete all the matches in the list
   1.116 +    while (aMatch) {
   1.117 +        nsTemplateMatch* next = aMatch->mNext;
   1.118 +        nsTemplateMatch::Destroy(aMatch, true);
   1.119 +        aMatch = next;
   1.120 +    }
   1.121 +
   1.122 +    return PL_DHASH_REMOVE;
   1.123 +}
   1.124 +
   1.125 +nsXULTemplateBuilder::~nsXULTemplateBuilder(void)
   1.126 +{
   1.127 +    Uninit(true);
   1.128 +
   1.129 +    if (--gRefCnt == 0) {
   1.130 +        NS_IF_RELEASE(gRDFService);
   1.131 +        NS_IF_RELEASE(gRDFContainerUtils);
   1.132 +        NS_IF_RELEASE(gSystemPrincipal);
   1.133 +        NS_IF_RELEASE(gScriptSecurityManager);
   1.134 +        NS_IF_RELEASE(gObserverService);
   1.135 +    }
   1.136 +
   1.137 +    MOZ_COUNT_DTOR(nsXULTemplateBuilder);
   1.138 +}
   1.139 +
   1.140 +
   1.141 +nsresult
   1.142 +nsXULTemplateBuilder::InitGlobals()
   1.143 +{
   1.144 +    nsresult rv;
   1.145 +
   1.146 +    if (gRefCnt++ == 0) {
   1.147 +        // Initialize the global shared reference to the service
   1.148 +        // manager and get some shared resource objects.
   1.149 +        NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
   1.150 +        rv = CallGetService(kRDFServiceCID, &gRDFService);
   1.151 +        if (NS_FAILED(rv))
   1.152 +            return rv;
   1.153 +
   1.154 +        NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
   1.155 +        rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils);
   1.156 +        if (NS_FAILED(rv))
   1.157 +            return rv;
   1.158 +
   1.159 +        rv = CallGetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID,
   1.160 +                            &gScriptSecurityManager);
   1.161 +        if (NS_FAILED(rv))
   1.162 +            return rv;
   1.163 +
   1.164 +        rv = gScriptSecurityManager->GetSystemPrincipal(&gSystemPrincipal);
   1.165 +        if (NS_FAILED(rv))
   1.166 +            return rv;
   1.167 +
   1.168 +        rv = CallGetService(NS_OBSERVERSERVICE_CONTRACTID, &gObserverService);
   1.169 +        if (NS_FAILED(rv))
   1.170 +            return rv;
   1.171 +    }
   1.172 +
   1.173 +#ifdef PR_LOGGING
   1.174 +    if (! gXULTemplateLog)
   1.175 +        gXULTemplateLog = PR_NewLogModule("nsXULTemplateBuilder");
   1.176 +#endif
   1.177 +
   1.178 +    return NS_OK;
   1.179 +}
   1.180 +
   1.181 +void
   1.182 +nsXULTemplateBuilder::StartObserving(nsIDocument* aDocument)
   1.183 +{
   1.184 +    aDocument->AddObserver(this);
   1.185 +    mObservedDocument = aDocument;
   1.186 +    gObserverService->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC, false);
   1.187 +}
   1.188 +
   1.189 +void
   1.190 +nsXULTemplateBuilder::StopObserving()
   1.191 +{
   1.192 +    MOZ_ASSERT(mObservedDocument);
   1.193 +    mObservedDocument->RemoveObserver(this);
   1.194 +    mObservedDocument = nullptr;
   1.195 +    gObserverService->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
   1.196 +}
   1.197 +
   1.198 +void
   1.199 +nsXULTemplateBuilder::CleanUp(bool aIsFinal)
   1.200 +{
   1.201 +    for (int32_t q = mQuerySets.Length() - 1; q >= 0; q--) {
   1.202 +        nsTemplateQuerySet* qs = mQuerySets[q];
   1.203 +        delete qs;
   1.204 +    }
   1.205 +
   1.206 +    mQuerySets.Clear();
   1.207 +
   1.208 +    mMatchMap.Enumerate(DestroyMatchList, nullptr);
   1.209 +
   1.210 +    // Setting mQueryProcessor to null will close connections. This would be
   1.211 +    // handled by the cycle collector, but we want to close them earlier.
   1.212 +    if (aIsFinal)
   1.213 +        mQueryProcessor = nullptr;
   1.214 +}
   1.215 +
   1.216 +void
   1.217 +nsXULTemplateBuilder::Uninit(bool aIsFinal)
   1.218 +{
   1.219 +    if (mObservedDocument && aIsFinal) {
   1.220 +        StopObserving();
   1.221 +    }
   1.222 +
   1.223 +    if (mQueryProcessor)
   1.224 +        mQueryProcessor->Done();
   1.225 +
   1.226 +    CleanUp(aIsFinal);
   1.227 +
   1.228 +    mRootResult = nullptr;
   1.229 +    mRefVariable = nullptr;
   1.230 +    mMemberVariable = nullptr;
   1.231 +
   1.232 +    mQueriesCompiled = false;
   1.233 +}
   1.234 +
   1.235 +static PLDHashOperator
   1.236 +TraverseMatchList(nsISupports* aKey, nsTemplateMatch* aMatch, void* aContext)
   1.237 +{
   1.238 +    nsCycleCollectionTraversalCallback *cb =
   1.239 +        static_cast<nsCycleCollectionTraversalCallback*>(aContext);
   1.240 +
   1.241 +    cb->NoteXPCOMChild(aKey);
   1.242 +    nsTemplateMatch* match = aMatch;
   1.243 +    while (match) {
   1.244 +        cb->NoteXPCOMChild(match->GetContainer());
   1.245 +        cb->NoteXPCOMChild(match->mResult);
   1.246 +        match = match->mNext;
   1.247 +    }
   1.248 +
   1.249 +    return PL_DHASH_NEXT;
   1.250 +}
   1.251 +
   1.252 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULTemplateBuilder)
   1.253 +
   1.254 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULTemplateBuilder)
   1.255 +    NS_IMPL_CYCLE_COLLECTION_UNLINK(mDataSource)
   1.256 +    NS_IMPL_CYCLE_COLLECTION_UNLINK(mDB)
   1.257 +    NS_IMPL_CYCLE_COLLECTION_UNLINK(mCompDB)
   1.258 +    NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot)
   1.259 +    NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootResult)
   1.260 +    NS_IMPL_CYCLE_COLLECTION_UNLINK(mListeners)
   1.261 +    NS_IMPL_CYCLE_COLLECTION_UNLINK(mQueryProcessor)
   1.262 +    tmp->mMatchMap.Enumerate(DestroyMatchList, nullptr);
   1.263 +    for (uint32_t i = 0; i < tmp->mQuerySets.Length(); ++i) {
   1.264 +        nsTemplateQuerySet* qs = tmp->mQuerySets[i];
   1.265 +        delete qs;
   1.266 +    }
   1.267 +    tmp->mQuerySets.Clear();
   1.268 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   1.269 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULTemplateBuilder)
   1.270 +    if (tmp->mObservedDocument && !cb.WantAllTraces()) {
   1.271 +        // The global observer service holds us alive.
   1.272 +        return NS_SUCCESS_INTERRUPTED_TRAVERSE;
   1.273 +    }
   1.274 +
   1.275 +    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDataSource)
   1.276 +    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDB)
   1.277 +    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCompDB)
   1.278 +    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
   1.279 +    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootResult)
   1.280 +    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListeners)
   1.281 +    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueryProcessor)
   1.282 +    tmp->mMatchMap.EnumerateRead(TraverseMatchList, &cb);
   1.283 +    {
   1.284 +      uint32_t i, count = tmp->mQuerySets.Length();
   1.285 +      for (i = 0; i < count; ++i) {
   1.286 +        nsTemplateQuerySet *set = tmp->mQuerySets[i];
   1.287 +        cb.NoteXPCOMChild(set->mQueryNode);
   1.288 +        cb.NoteXPCOMChild(set->mCompiledQuery);
   1.289 +        uint16_t j, rulesCount = set->RuleCount();
   1.290 +        for (j = 0; j < rulesCount; ++j) {
   1.291 +          set->GetRuleAt(j)->Traverse(cb);
   1.292 +        }
   1.293 +      }
   1.294 +    }
   1.295 +    tmp->Traverse(cb);
   1.296 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   1.297 +
   1.298 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULTemplateBuilder)
   1.299 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULTemplateBuilder)
   1.300 +
   1.301 +DOMCI_DATA(XULTemplateBuilder, nsXULTemplateBuilder)
   1.302 +
   1.303 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULTemplateBuilder)
   1.304 +  NS_INTERFACE_MAP_ENTRY(nsIXULTemplateBuilder)
   1.305 +  NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
   1.306 +  NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
   1.307 +  NS_INTERFACE_MAP_ENTRY(nsIObserver)
   1.308 +  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULTemplateBuilder)
   1.309 +  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XULTemplateBuilder)
   1.310 +NS_INTERFACE_MAP_END
   1.311 +
   1.312 +//----------------------------------------------------------------------
   1.313 +//
   1.314 +// nsIXULTemplateBuilder methods
   1.315 +//
   1.316 +
   1.317 +NS_IMETHODIMP
   1.318 +nsXULTemplateBuilder::GetRoot(nsIDOMElement** aResult)
   1.319 +{
   1.320 +    if (mRoot) {
   1.321 +        return CallQueryInterface(mRoot, aResult);
   1.322 +    }
   1.323 +    *aResult = nullptr;
   1.324 +    return NS_OK;
   1.325 +}
   1.326 +
   1.327 +NS_IMETHODIMP
   1.328 +nsXULTemplateBuilder::GetDatasource(nsISupports** aResult)
   1.329 +{
   1.330 +    if (mCompDB)
   1.331 +        NS_ADDREF(*aResult = mCompDB);
   1.332 +    else
   1.333 +        NS_IF_ADDREF(*aResult = mDataSource);
   1.334 +    return NS_OK;
   1.335 +}
   1.336 +
   1.337 +NS_IMETHODIMP
   1.338 +nsXULTemplateBuilder::SetDatasource(nsISupports* aResult)
   1.339 +{
   1.340 +    mDataSource = aResult;
   1.341 +    mCompDB = do_QueryInterface(mDataSource);
   1.342 +
   1.343 +    return Rebuild();
   1.344 +}
   1.345 +
   1.346 +NS_IMETHODIMP
   1.347 +nsXULTemplateBuilder::GetDatabase(nsIRDFCompositeDataSource** aResult)
   1.348 +{
   1.349 +    NS_IF_ADDREF(*aResult = mCompDB);
   1.350 +    return NS_OK;
   1.351 +}
   1.352 +
   1.353 +NS_IMETHODIMP
   1.354 +nsXULTemplateBuilder::GetQueryProcessor(nsIXULTemplateQueryProcessor** aResult)
   1.355 +{
   1.356 +    NS_IF_ADDREF(*aResult = mQueryProcessor.get());
   1.357 +    return NS_OK;
   1.358 +}
   1.359 +
   1.360 +NS_IMETHODIMP
   1.361 +nsXULTemplateBuilder::AddRuleFilter(nsIDOMNode* aRule, nsIXULTemplateRuleFilter* aFilter)
   1.362 +{
   1.363 +    if (!aRule || !aFilter)
   1.364 +        return NS_ERROR_NULL_POINTER;
   1.365 +
   1.366 +    // a custom rule filter may be added, one for each rule. If a new one is
   1.367 +    // added, it replaces the old one. Look for the right rule and set its
   1.368 +    // filter
   1.369 +
   1.370 +    int32_t count = mQuerySets.Length();
   1.371 +    for (int32_t q = 0; q < count; q++) {
   1.372 +        nsTemplateQuerySet* queryset = mQuerySets[q];
   1.373 +
   1.374 +        int16_t rulecount = queryset->RuleCount();
   1.375 +        for (int16_t r = 0; r < rulecount; r++) {
   1.376 +            nsTemplateRule* rule = queryset->GetRuleAt(r);
   1.377 +
   1.378 +            nsCOMPtr<nsIDOMNode> rulenode;
   1.379 +            rule->GetRuleNode(getter_AddRefs(rulenode));
   1.380 +            if (aRule == rulenode) {
   1.381 +                rule->SetRuleFilter(aFilter);
   1.382 +                return NS_OK;
   1.383 +            }
   1.384 +        }
   1.385 +    }
   1.386 +
   1.387 +    return NS_OK;
   1.388 +}
   1.389 +
   1.390 +NS_IMETHODIMP
   1.391 +nsXULTemplateBuilder::Rebuild()
   1.392 +{
   1.393 +    int32_t i;
   1.394 +
   1.395 +    for (i = mListeners.Count() - 1; i >= 0; --i) {
   1.396 +        mListeners[i]->WillRebuild(this);
   1.397 +    }
   1.398 +
   1.399 +    nsresult rv = RebuildAll();
   1.400 +
   1.401 +    for (i = mListeners.Count() - 1; i >= 0; --i) {
   1.402 +        mListeners[i]->DidRebuild(this);
   1.403 +    }
   1.404 +
   1.405 +    return rv;
   1.406 +}
   1.407 +
   1.408 +NS_IMETHODIMP
   1.409 +nsXULTemplateBuilder::Refresh()
   1.410 +{
   1.411 +    nsresult rv;
   1.412 +
   1.413 +    if (!mCompDB)
   1.414 +        return NS_ERROR_FAILURE;
   1.415 +
   1.416 +    nsCOMPtr<nsISimpleEnumerator> dslist;
   1.417 +    rv = mCompDB->GetDataSources(getter_AddRefs(dslist));
   1.418 +    NS_ENSURE_SUCCESS(rv, rv);
   1.419 +
   1.420 +    bool hasMore;
   1.421 +    nsCOMPtr<nsISupports> next;
   1.422 +    nsCOMPtr<nsIRDFRemoteDataSource> rds;
   1.423 +
   1.424 +    while(NS_SUCCEEDED(dslist->HasMoreElements(&hasMore)) && hasMore) {
   1.425 +        dslist->GetNext(getter_AddRefs(next));
   1.426 +        if (next && (rds = do_QueryInterface(next))) {
   1.427 +            rds->Refresh(false);
   1.428 +        }
   1.429 +    }
   1.430 +
   1.431 +    // XXXbsmedberg: it would be kinda nice to install an async nsIRDFXMLSink
   1.432 +    // observer and call rebuild() once the load is complete. See bug 254600.
   1.433 +
   1.434 +    return NS_OK;
   1.435 +}
   1.436 +
   1.437 +NS_IMETHODIMP
   1.438 +nsXULTemplateBuilder::Init(nsIContent* aElement)
   1.439 +{
   1.440 +    NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
   1.441 +    mRoot = aElement;
   1.442 +
   1.443 +    nsCOMPtr<nsIDocument> doc = mRoot->GetDocument();
   1.444 +    NS_ASSERTION(doc, "element has no document");
   1.445 +    if (! doc)
   1.446 +        return NS_ERROR_UNEXPECTED;
   1.447 +
   1.448 +    bool shouldDelay;
   1.449 +    nsresult rv = LoadDataSources(doc, &shouldDelay);
   1.450 +
   1.451 +    if (NS_SUCCEEDED(rv)) {
   1.452 +        StartObserving(doc);
   1.453 +    }
   1.454 +
   1.455 +    return rv;
   1.456 +}
   1.457 +
   1.458 +NS_IMETHODIMP
   1.459 +nsXULTemplateBuilder::CreateContents(nsIContent* aElement, bool aForceCreation)
   1.460 +{
   1.461 +    return NS_OK;
   1.462 +}
   1.463 +
   1.464 +NS_IMETHODIMP
   1.465 +nsXULTemplateBuilder::HasGeneratedContent(nsIRDFResource* aResource,
   1.466 +                                          nsIAtom* aTag,
   1.467 +                                          bool* aGenerated)
   1.468 +{
   1.469 +    *aGenerated = false;
   1.470 +    return NS_OK;
   1.471 +}
   1.472 +
   1.473 +NS_IMETHODIMP
   1.474 +nsXULTemplateBuilder::AddResult(nsIXULTemplateResult* aResult,
   1.475 +                                nsIDOMNode* aQueryNode)
   1.476 +{
   1.477 +    NS_ENSURE_ARG_POINTER(aResult);
   1.478 +    NS_ENSURE_ARG_POINTER(aQueryNode);
   1.479 +
   1.480 +    return UpdateResult(nullptr, aResult, aQueryNode);
   1.481 +}
   1.482 +
   1.483 +NS_IMETHODIMP
   1.484 +nsXULTemplateBuilder::RemoveResult(nsIXULTemplateResult* aResult)
   1.485 +{
   1.486 +    NS_ENSURE_ARG_POINTER(aResult);
   1.487 +
   1.488 +    return UpdateResult(aResult, nullptr, nullptr);
   1.489 +}
   1.490 +
   1.491 +NS_IMETHODIMP
   1.492 +nsXULTemplateBuilder::ReplaceResult(nsIXULTemplateResult* aOldResult,
   1.493 +                                    nsIXULTemplateResult* aNewResult,
   1.494 +                                    nsIDOMNode* aQueryNode)
   1.495 +{
   1.496 +    NS_ENSURE_ARG_POINTER(aOldResult);
   1.497 +    NS_ENSURE_ARG_POINTER(aNewResult);
   1.498 +    NS_ENSURE_ARG_POINTER(aQueryNode);
   1.499 +
   1.500 +    // just remove the old result and then add a new result separately
   1.501 +
   1.502 +    nsresult rv = UpdateResult(aOldResult, nullptr, nullptr);
   1.503 +    if (NS_FAILED(rv))
   1.504 +        return rv;
   1.505 +
   1.506 +    return UpdateResult(nullptr, aNewResult, aQueryNode);
   1.507 +}
   1.508 +
   1.509 +nsresult
   1.510 +nsXULTemplateBuilder::UpdateResult(nsIXULTemplateResult* aOldResult,
   1.511 +                                   nsIXULTemplateResult* aNewResult,
   1.512 +                                   nsIDOMNode* aQueryNode)
   1.513 +{
   1.514 +    PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
   1.515 +           ("nsXULTemplateBuilder::UpdateResult %p %p %p",
   1.516 +           aOldResult, aNewResult, aQueryNode));
   1.517 +
   1.518 +    if (!mRoot || !mQueriesCompiled)
   1.519 +      return NS_OK;
   1.520 +
   1.521 +    // get the containers where content may be inserted. If
   1.522 +    // GetInsertionLocations returns false, no container has generated
   1.523 +    // any content yet so new content should not be generated either. This
   1.524 +    // will be false if the result applies to content that is in a closed menu
   1.525 +    // or treeitem for example.
   1.526 +
   1.527 +    nsAutoPtr<nsCOMArray<nsIContent> > insertionPoints;
   1.528 +    bool mayReplace = GetInsertionLocations(aOldResult ? aOldResult : aNewResult,
   1.529 +                                              getter_Transfers(insertionPoints));
   1.530 +    if (! mayReplace)
   1.531 +        return NS_OK;
   1.532 +
   1.533 +    nsresult rv = NS_OK;
   1.534 +
   1.535 +    nsCOMPtr<nsIRDFResource> oldId, newId;
   1.536 +    nsTemplateQuerySet* queryset = nullptr;
   1.537 +
   1.538 +    if (aOldResult) {
   1.539 +        rv = GetResultResource(aOldResult, getter_AddRefs(oldId));
   1.540 +        if (NS_FAILED(rv))
   1.541 +            return rv;
   1.542 +
   1.543 +        // Ignore re-entrant builds for content that is currently in our
   1.544 +        // activation stack.
   1.545 +        if (IsActivated(oldId))
   1.546 +            return NS_OK;
   1.547 +    }
   1.548 +
   1.549 +    if (aNewResult) {
   1.550 +        rv = GetResultResource(aNewResult, getter_AddRefs(newId));
   1.551 +        if (NS_FAILED(rv))
   1.552 +            return rv;
   1.553 +
   1.554 +        // skip results that don't have ids
   1.555 +        if (! newId)
   1.556 +            return NS_OK;
   1.557 +
   1.558 +        // Ignore re-entrant builds for content that is currently in our
   1.559 +        // activation stack.
   1.560 +        if (IsActivated(newId))
   1.561 +            return NS_OK;
   1.562 +
   1.563 +        // look for the queryset associated with the supplied query node
   1.564 +        nsCOMPtr<nsIContent> querycontent = do_QueryInterface(aQueryNode);
   1.565 +
   1.566 +        int32_t count = mQuerySets.Length();
   1.567 +        for (int32_t q = 0; q < count; q++) {
   1.568 +            nsTemplateQuerySet* qs = mQuerySets[q];
   1.569 +            if (qs->mQueryNode == querycontent) {
   1.570 +                queryset = qs;
   1.571 +                break;
   1.572 +            }
   1.573 +        }
   1.574 +
   1.575 +        if (! queryset)
   1.576 +            return NS_OK;
   1.577 +    }
   1.578 +
   1.579 +    if (insertionPoints) {
   1.580 +        // iterate over each insertion point and add or remove the result from
   1.581 +        // that container
   1.582 +        uint32_t count = insertionPoints->Count();
   1.583 +        for (uint32_t t = 0; t < count; t++) {
   1.584 +            nsCOMPtr<nsIContent> insertionPoint = insertionPoints->SafeObjectAt(t);
   1.585 +            if (insertionPoint) {
   1.586 +                rv = UpdateResultInContainer(aOldResult, aNewResult, queryset,
   1.587 +                                             oldId, newId, insertionPoint);
   1.588 +                if (NS_FAILED(rv))
   1.589 +                    return rv;
   1.590 +            }
   1.591 +        }
   1.592 +    }
   1.593 +    else {
   1.594 +        // The tree builder doesn't use insertion points, so no insertion
   1.595 +        // points will be set. In this case, just update the one result.
   1.596 +        rv = UpdateResultInContainer(aOldResult, aNewResult, queryset,
   1.597 +                                     oldId, newId, nullptr);
   1.598 +    }
   1.599 +
   1.600 +    return NS_OK;
   1.601 +}
   1.602 +
   1.603 +nsresult
   1.604 +nsXULTemplateBuilder::UpdateResultInContainer(nsIXULTemplateResult* aOldResult,
   1.605 +                                              nsIXULTemplateResult* aNewResult,
   1.606 +                                              nsTemplateQuerySet* aQuerySet,
   1.607 +                                              nsIRDFResource* aOldId,
   1.608 +                                              nsIRDFResource* aNewId,
   1.609 +                                              nsIContent* aInsertionPoint)
   1.610 +{
   1.611 +    // This method takes a result that no longer applies (aOldResult) and
   1.612 +    // replaces it with a new result (aNewResult). Either may be null
   1.613 +    // indicating to just remove a result or add a new one without replacing.
   1.614 +    //
   1.615 +    // Matches are stored in the hashtable mMatchMap, keyed by result id. If
   1.616 +    // there is more than one query, or the same id is found in different
   1.617 +    // containers, the values in the hashtable will be a linked list of all
   1.618 +    // the matches for that id. The matches are sorted according to the
   1.619 +    // queries they are associated with. Matches for earlier queries in the
   1.620 +    // template take priority over matches from later queries. The priority
   1.621 +    // for a match is determined from the match's QuerySetPriority method.
   1.622 +    // The first query has a priority 0, and higher numbers are for later
   1.623 +    // queries with successively higher priorities. Thus, a match takes
   1.624 +    // precedence if it has a lower priority than another. If there is only
   1.625 +    // one query or container, then the match doesn't have any linked items.
   1.626 +    //
   1.627 +    // Matches are nsTemplateMatch objects. They are wrappers around
   1.628 +    // nsIXULTemplateResult result objects and are created with
   1.629 +    // nsTemplateMatch::Create below. The aQuerySet argument specifies which
   1.630 +    // query the match is associated with.
   1.631 +    //
   1.632 +    // When a result id exists in multiple containers, the match's mContainer
   1.633 +    // field is set to the container it corresponds to. The aInsertionPoint
   1.634 +    // argument specifies which container is being updated. Even though they
   1.635 +    // are stored in the same linked list as other matches of the same id, the
   1.636 +    // matches for different containers are treated separately. They are only
   1.637 +    // stored in the same hashtable to avoid a more complex data structure, as
   1.638 +    // the use of the same id in multiple containers isn't a common occurance.
   1.639 +    //
   1.640 +    // Only one match with a given id per container is active at a time. When
   1.641 +    // a match is active, content is generated for it. When a match is
   1.642 +    // inactive, content is not generated for it. A match becomes active if
   1.643 +    // another match with the same id and container with a lower priority
   1.644 +    // isn't already active, and the match has a rule or conditions clause
   1.645 +    // which evaluates to true. The former is checked by comparing the value
   1.646 +    // of the QuerySetPriority method of the match with earlier matches. The
   1.647 +    // latter is checked with the DetermineMatchedRule method.
   1.648 +    //
   1.649 +    // Naturally, if a match with a lower priority is active, it overrides
   1.650 +    // the new match, so the new match is hooked up into the match linked
   1.651 +    // list as inactive, and no content is generated for it. If a match with a
   1.652 +    // higher priority is active, and the new match's conditions evaluate
   1.653 +    // to true, then this existing match with the higher priority needs to have
   1.654 +    // its generated content removed and replaced with the new match's
   1.655 +    // generated content.
   1.656 +    //
   1.657 +    // Similar situations apply when removing an existing match. If the match
   1.658 +    // is active, the existing generated content will need to be removed, and
   1.659 +    // a match of higher priority that is revealed may become active and need
   1.660 +    // to have content generated.
   1.661 +    //
   1.662 +    // Content removal and generation is done by the ReplaceMatch method which
   1.663 +    // is overridden for the content builder and tree builder to update the
   1.664 +    // generated output for each type.
   1.665 +    //
   1.666 +    // The code below handles all of the various cases and ensures that the
   1.667 +    // match lists are maintained properly.
   1.668 +
   1.669 +    nsresult rv = NS_OK;
   1.670 +    int16_t ruleindex;
   1.671 +    nsTemplateRule* matchedrule = nullptr;
   1.672 +
   1.673 +    // Indicates that the old match was active and must have its content
   1.674 +    // removed
   1.675 +    bool oldMatchWasActive = false;
   1.676 +
   1.677 +    // acceptedmatch will be set to a new match that has to have new content
   1.678 +    // generated for it. If a new match doesn't need to have content
   1.679 +    // generated, (because for example, a match with a lower priority
   1.680 +    // already applies), then acceptedmatch will be null, but the match will
   1.681 +    // be still hooked up into the chain, since it may become active later
   1.682 +    // as other results are updated.
   1.683 +    nsTemplateMatch* acceptedmatch = nullptr;
   1.684 +
   1.685 +    // When aOldResult is specified, removematch will be set to the
   1.686 +    // corresponding match. This match needs to be deleted as it no longer
   1.687 +    // applies. However, removedmatch will be null when aOldResult is null, or
   1.688 +    // when no match was found corresponding to aOldResult.
   1.689 +    nsTemplateMatch* removedmatch = nullptr;
   1.690 +
   1.691 +    // These will be set when aNewResult is specified indicating to add a
   1.692 +    // result, but will end up replacing an existing match. The former
   1.693 +    // indicates a match being replaced that was active and had content
   1.694 +    // generated for it, while the latter indicates a match that wasn't active
   1.695 +    // and just needs to be deleted. Both may point to different matches. For
   1.696 +    // example, if the new match becomes active, replacing an inactive match,
   1.697 +    // the inactive match will need to be deleted. However, if another match
   1.698 +    // with a higher priority is active, the new match will override it, so
   1.699 +    // content will need to be generated for the new match and removed for
   1.700 +    // this existing active match.
   1.701 +    nsTemplateMatch* replacedmatch = nullptr, * replacedmatchtodelete = nullptr;
   1.702 +
   1.703 +    if (aOldResult) {
   1.704 +        nsTemplateMatch* firstmatch;
   1.705 +        if (mMatchMap.Get(aOldId, &firstmatch)) {
   1.706 +            nsTemplateMatch* oldmatch = firstmatch;
   1.707 +            nsTemplateMatch* prevmatch = nullptr;
   1.708 +
   1.709 +            // look for the right match if there was more than one
   1.710 +            while (oldmatch && (oldmatch->mResult != aOldResult)) {
   1.711 +                prevmatch = oldmatch;
   1.712 +                oldmatch = oldmatch->mNext;
   1.713 +            }
   1.714 +
   1.715 +            if (oldmatch) {
   1.716 +                nsTemplateMatch* findmatch = oldmatch->mNext;
   1.717 +
   1.718 +                // Keep a reference so that linked list can be hooked up at
   1.719 +                // the end in case an error occurs.
   1.720 +                nsTemplateMatch* nextmatch = findmatch;
   1.721 +
   1.722 +                if (oldmatch->IsActive()) {
   1.723 +                    // Indicate that the old match was active so its content
   1.724 +                    // will be removed later.
   1.725 +                    oldMatchWasActive = true;
   1.726 +
   1.727 +                    // The match being removed is the active match, so scan
   1.728 +                    // through the later matches to determine if one should
   1.729 +                    // now become the active match.
   1.730 +                    while (findmatch) {
   1.731 +                        // only other matches with the same container should
   1.732 +                        // now match, leave other containers alone
   1.733 +                        if (findmatch->GetContainer() == aInsertionPoint) {
   1.734 +                            nsTemplateQuerySet* qs =
   1.735 +                                mQuerySets[findmatch->QuerySetPriority()];
   1.736 +                        
   1.737 +                            DetermineMatchedRule(aInsertionPoint, findmatch->mResult,
   1.738 +                                                 qs, &matchedrule, &ruleindex);
   1.739 +
   1.740 +                            if (matchedrule) {
   1.741 +                                rv = findmatch->RuleMatched(qs,
   1.742 +                                                            matchedrule, ruleindex,
   1.743 +                                                            findmatch->mResult);
   1.744 +                                if (NS_FAILED(rv))
   1.745 +                                    return rv;
   1.746 +
   1.747 +                                acceptedmatch = findmatch;
   1.748 +                                break;
   1.749 +                            }
   1.750 +                        }
   1.751 +
   1.752 +                        findmatch = findmatch->mNext;
   1.753 +                    }
   1.754 +                }
   1.755 +
   1.756 +                if (oldmatch == firstmatch) {
   1.757 +                    // the match to remove is at the beginning
   1.758 +                    if (oldmatch->mNext) {
   1.759 +                        mMatchMap.Put(aOldId, oldmatch->mNext);
   1.760 +                    }
   1.761 +                    else {
   1.762 +                        mMatchMap.Remove(aOldId);
   1.763 +                    }
   1.764 +                }
   1.765 +
   1.766 +                if (prevmatch)
   1.767 +                    prevmatch->mNext = nextmatch;
   1.768 +
   1.769 +                removedmatch = oldmatch;
   1.770 +                if (mFlags & eLoggingEnabled)
   1.771 +                    OutputMatchToLog(aOldId, removedmatch, false);
   1.772 +            }
   1.773 +        }
   1.774 +    }
   1.775 +
   1.776 +    nsTemplateMatch *newmatch = nullptr;
   1.777 +    if (aNewResult) {
   1.778 +        // only allow a result to be inserted into containers with a matching tag
   1.779 +        nsIAtom* tag = aQuerySet->GetTag();
   1.780 +        if (aInsertionPoint && tag && tag != aInsertionPoint->Tag())
   1.781 +            return NS_OK;
   1.782 +
   1.783 +        int32_t findpriority = aQuerySet->Priority();
   1.784 +
   1.785 +        newmatch = nsTemplateMatch::Create(findpriority,
   1.786 +                                           aNewResult, aInsertionPoint);
   1.787 +        if (!newmatch)
   1.788 +            return NS_ERROR_OUT_OF_MEMORY;
   1.789 +
   1.790 +        nsTemplateMatch* firstmatch;
   1.791 +        if (mMatchMap.Get(aNewId, &firstmatch)) {
   1.792 +            bool hasEarlierActiveMatch = false;
   1.793 +
   1.794 +            // Scan through the existing matches to find where the new one
   1.795 +            // should be inserted. oldmatch will be set to the old match for
   1.796 +            // the same query and prevmatch will be set to the match before it.
   1.797 +            nsTemplateMatch* prevmatch = nullptr;
   1.798 +            nsTemplateMatch* oldmatch = firstmatch;
   1.799 +            while (oldmatch) {
   1.800 +                // Break out once we've reached a query in the list with a
   1.801 +                // lower priority. The new match will be inserted at this
   1.802 +                // location so that the match list is sorted by priority.
   1.803 +                int32_t priority = oldmatch->QuerySetPriority();
   1.804 +                if (priority > findpriority) {
   1.805 +                    oldmatch = nullptr;
   1.806 +                    break;
   1.807 +                }
   1.808 +
   1.809 +                // look for matches that belong in the same container
   1.810 +                if (oldmatch->GetContainer() == aInsertionPoint) {
   1.811 +                    if (priority == findpriority)
   1.812 +                        break;
   1.813 +
   1.814 +                    // If a match with a lower priority is active, the new
   1.815 +                    // match can't replace it.
   1.816 +                    if (oldmatch->IsActive())
   1.817 +                        hasEarlierActiveMatch = true;
   1.818 +                }
   1.819 +
   1.820 +                prevmatch = oldmatch;
   1.821 +                oldmatch = oldmatch->mNext;
   1.822 +            }
   1.823 +
   1.824 +            // At this point, oldmatch will either be null, or set to a match
   1.825 +            // with the same container and priority. If set, oldmatch will
   1.826 +            // need to be replaced by newmatch.
   1.827 +
   1.828 +            if (oldmatch)
   1.829 +                newmatch->mNext = oldmatch->mNext;
   1.830 +            else if (prevmatch)
   1.831 +                newmatch->mNext = prevmatch->mNext;
   1.832 +            else
   1.833 +                newmatch->mNext = firstmatch;
   1.834 +
   1.835 +            // hasEarlierActiveMatch will be set to true if a match with a
   1.836 +            // lower priority was found. The new match won't replace it in
   1.837 +            // this case. If hasEarlierActiveMatch is false, then the new match
   1.838 +            // may be become active if it matches one of the rules, and will
   1.839 +            // generate output. It's also possible however, that a match with
   1.840 +            // the same priority already exists, which means that the new match
   1.841 +            // will replace the old one. In this case, oldmatch will be set to
   1.842 +            // the old match. The content for the old match must be removed and
   1.843 +            // content for the new match generated in its place.
   1.844 +            if (! hasEarlierActiveMatch) {
   1.845 +                // If the old match was the active match, set replacedmatch to
   1.846 +                // indicate that it needs its content removed.
   1.847 +                if (oldmatch) {
   1.848 +                    if (oldmatch->IsActive())
   1.849 +                        replacedmatch = oldmatch;
   1.850 +                    replacedmatchtodelete = oldmatch;
   1.851 +                }
   1.852 +
   1.853 +                // check if the new result matches the rules
   1.854 +                rv = DetermineMatchedRule(aInsertionPoint, newmatch->mResult,
   1.855 +                                          aQuerySet, &matchedrule, &ruleindex);
   1.856 +                if (NS_FAILED(rv)) {
   1.857 +                    nsTemplateMatch::Destroy(newmatch, false);
   1.858 +                    return rv;
   1.859 +                }
   1.860 +
   1.861 +                if (matchedrule) {
   1.862 +                    rv = newmatch->RuleMatched(aQuerySet,
   1.863 +                                               matchedrule, ruleindex,
   1.864 +                                               newmatch->mResult);
   1.865 +                    if (NS_FAILED(rv)) {
   1.866 +                        nsTemplateMatch::Destroy(newmatch, false);
   1.867 +                        return rv;
   1.868 +                    }
   1.869 +
   1.870 +                    // acceptedmatch may have been set in the block handling
   1.871 +                    // aOldResult earlier. If so, we would only get here when
   1.872 +                    // that match has a higher priority than this new match.
   1.873 +                    // As only one match can have content generated for it, it
   1.874 +                    // is OK to set acceptedmatch here to the new match,
   1.875 +                    // ignoring the other one.
   1.876 +                    acceptedmatch = newmatch;
   1.877 +
   1.878 +                    // Clear the matched state of the later results for the
   1.879 +                    // same container.
   1.880 +                    nsTemplateMatch* clearmatch = newmatch->mNext;
   1.881 +                    while (clearmatch) {
   1.882 +                        if (clearmatch->GetContainer() == aInsertionPoint &&
   1.883 +                            clearmatch->IsActive()) {
   1.884 +                            clearmatch->SetInactive();
   1.885 +                            // Replacedmatch should be null here. If not, it
   1.886 +                            // means that two matches were active which isn't
   1.887 +                            // a valid state
   1.888 +                            NS_ASSERTION(!replacedmatch,
   1.889 +                                         "replaced match already set");
   1.890 +                            replacedmatch = clearmatch;
   1.891 +                            break;
   1.892 +                        }
   1.893 +                        clearmatch = clearmatch->mNext;
   1.894 +                    }
   1.895 +                }
   1.896 +                else if (oldmatch && oldmatch->IsActive()) {
   1.897 +                    // The result didn't match the rules, so look for a later
   1.898 +                    // one. However, only do this if the old match was the
   1.899 +                    // active match.
   1.900 +                    newmatch = newmatch->mNext;
   1.901 +                    while (newmatch) {
   1.902 +                        if (newmatch->GetContainer() == aInsertionPoint) {
   1.903 +                            rv = DetermineMatchedRule(aInsertionPoint, newmatch->mResult,
   1.904 +                                                      aQuerySet, &matchedrule, &ruleindex);
   1.905 +                            if (NS_FAILED(rv)) {
   1.906 +                                nsTemplateMatch::Destroy(newmatch, false);
   1.907 +                                return rv;
   1.908 +                            }
   1.909 +
   1.910 +                            if (matchedrule) {
   1.911 +                                rv = newmatch->RuleMatched(aQuerySet,
   1.912 +                                                           matchedrule, ruleindex,
   1.913 +                                                           newmatch->mResult);
   1.914 +                                if (NS_FAILED(rv)) {
   1.915 +                                    nsTemplateMatch::Destroy(newmatch, false);
   1.916 +                                    return rv;
   1.917 +                                }
   1.918 +
   1.919 +                                acceptedmatch = newmatch;
   1.920 +                                break;
   1.921 +                            }
   1.922 +                        }
   1.923 +
   1.924 +                        newmatch = newmatch->mNext;
   1.925 +                    }
   1.926 +                }
   1.927 +
   1.928 +                // put the match in the map if there isn't a previous match
   1.929 +                if (! prevmatch) {
   1.930 +                    mMatchMap.Put(aNewId, newmatch);
   1.931 +                }
   1.932 +            }
   1.933 +
   1.934 +            // hook up the match last in case an error occurs
   1.935 +            if (prevmatch)
   1.936 +                prevmatch->mNext = newmatch;
   1.937 +        }
   1.938 +        else {
   1.939 +            // The id is not used in the hashtable yet so create a new match
   1.940 +            // and add it to the hashtable.
   1.941 +            rv = DetermineMatchedRule(aInsertionPoint, aNewResult,
   1.942 +                                      aQuerySet, &matchedrule, &ruleindex);
   1.943 +            if (NS_FAILED(rv)) {
   1.944 +                nsTemplateMatch::Destroy(newmatch, false);
   1.945 +                return rv;
   1.946 +            }
   1.947 +
   1.948 +            if (matchedrule) {
   1.949 +                rv = newmatch->RuleMatched(aQuerySet, matchedrule,
   1.950 +                                           ruleindex, aNewResult);
   1.951 +                if (NS_FAILED(rv)) {
   1.952 +                    nsTemplateMatch::Destroy(newmatch, false);
   1.953 +                    return rv;
   1.954 +                }
   1.955 +
   1.956 +                acceptedmatch = newmatch;
   1.957 +            }
   1.958 +
   1.959 +            mMatchMap.Put(aNewId, newmatch);
   1.960 +        }
   1.961 +    }
   1.962 +
   1.963 +    // The ReplaceMatch method is builder specific and removes the generated
   1.964 +    // content for a match.
   1.965 +
   1.966 +    // Remove the content for a match that was active and needs to be replaced.
   1.967 +    if (replacedmatch) {
   1.968 +        rv = ReplaceMatch(replacedmatch->mResult, nullptr, nullptr,
   1.969 +                          aInsertionPoint);
   1.970 +
   1.971 +        if (mFlags & eLoggingEnabled)
   1.972 +            OutputMatchToLog(aNewId, replacedmatch, false);
   1.973 +    }
   1.974 +
   1.975 +    // remove a match that needs to be deleted.
   1.976 +    if (replacedmatchtodelete)
   1.977 +        nsTemplateMatch::Destroy(replacedmatchtodelete, true);
   1.978 +
   1.979 +    // If the old match was active, the content for it needs to be removed.
   1.980 +    // If the old match was not active, it shouldn't have had any content,
   1.981 +    // so just pass null to ReplaceMatch. If acceptedmatch was set, then
   1.982 +    // content needs to be generated for a new match.
   1.983 +    if (oldMatchWasActive || acceptedmatch)
   1.984 +        rv = ReplaceMatch(oldMatchWasActive ? aOldResult : nullptr,
   1.985 +                          acceptedmatch, matchedrule, aInsertionPoint);
   1.986 +
   1.987 +    // delete the old match that was replaced
   1.988 +    if (removedmatch)
   1.989 +        nsTemplateMatch::Destroy(removedmatch, true);
   1.990 +
   1.991 +    if (mFlags & eLoggingEnabled && newmatch)
   1.992 +        OutputMatchToLog(aNewId, newmatch, true);
   1.993 +
   1.994 +    return rv;
   1.995 +}
   1.996 +
   1.997 +NS_IMETHODIMP
   1.998 +nsXULTemplateBuilder::ResultBindingChanged(nsIXULTemplateResult* aResult)
   1.999 +{
  1.1000 +    // A binding update is used when only the values of the bindings have
  1.1001 +    // changed, so the same rule still applies. Just synchronize the content.
  1.1002 +    // The new result will have the new values.
  1.1003 +    NS_ENSURE_ARG_POINTER(aResult);
  1.1004 +
  1.1005 +    if (!mRoot || !mQueriesCompiled)
  1.1006 +      return NS_OK;
  1.1007 +
  1.1008 +    return SynchronizeResult(aResult);
  1.1009 +}
  1.1010 +
  1.1011 +NS_IMETHODIMP
  1.1012 +nsXULTemplateBuilder::GetRootResult(nsIXULTemplateResult** aResult)
  1.1013 +{
  1.1014 +  *aResult = mRootResult;
  1.1015 +  NS_IF_ADDREF(*aResult);
  1.1016 +  return NS_OK;
  1.1017 +}
  1.1018 +
  1.1019 +NS_IMETHODIMP
  1.1020 +nsXULTemplateBuilder::GetResultForId(const nsAString& aId,
  1.1021 +                                     nsIXULTemplateResult** aResult)
  1.1022 +{
  1.1023 +    if (aId.IsEmpty())
  1.1024 +        return NS_ERROR_INVALID_ARG;
  1.1025 +
  1.1026 +    nsCOMPtr<nsIRDFResource> resource;
  1.1027 +    gRDFService->GetUnicodeResource(aId, getter_AddRefs(resource));
  1.1028 +
  1.1029 +    *aResult = nullptr;
  1.1030 +
  1.1031 +    nsTemplateMatch* match;
  1.1032 +    if (mMatchMap.Get(resource, &match)) {
  1.1033 +        // find the active match
  1.1034 +        while (match) {
  1.1035 +            if (match->IsActive()) {
  1.1036 +                *aResult = match->mResult;
  1.1037 +                NS_IF_ADDREF(*aResult);
  1.1038 +                break;
  1.1039 +            }
  1.1040 +            match = match->mNext;
  1.1041 +        }
  1.1042 +    }
  1.1043 +
  1.1044 +    return NS_OK;
  1.1045 +}
  1.1046 +
  1.1047 +NS_IMETHODIMP
  1.1048 +nsXULTemplateBuilder::GetResultForContent(nsIDOMElement* aContent,
  1.1049 +                                          nsIXULTemplateResult** aResult)
  1.1050 +{
  1.1051 +    *aResult = nullptr;
  1.1052 +    return NS_OK;
  1.1053 +}
  1.1054 +
  1.1055 +NS_IMETHODIMP
  1.1056 +nsXULTemplateBuilder::AddListener(nsIXULBuilderListener* aListener)
  1.1057 +{
  1.1058 +    NS_ENSURE_ARG(aListener);
  1.1059 +
  1.1060 +    if (!mListeners.AppendObject(aListener))
  1.1061 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1062 +
  1.1063 +    return NS_OK;
  1.1064 +}
  1.1065 +
  1.1066 +NS_IMETHODIMP
  1.1067 +nsXULTemplateBuilder::RemoveListener(nsIXULBuilderListener* aListener)
  1.1068 +{
  1.1069 +    NS_ENSURE_ARG(aListener);
  1.1070 +
  1.1071 +    mListeners.RemoveObject(aListener);
  1.1072 +
  1.1073 +    return NS_OK;
  1.1074 +}
  1.1075 +
  1.1076 +NS_IMETHODIMP
  1.1077 +nsXULTemplateBuilder::Observe(nsISupports* aSubject,
  1.1078 +                              const char* aTopic,
  1.1079 +                              const char16_t* aData)
  1.1080 +{
  1.1081 +    // Uuuuber hack to clean up circular references that the cycle collector
  1.1082 +    // doesn't know about. See bug 394514.
  1.1083 +    if (!strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC)) {
  1.1084 +        nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aSubject);
  1.1085 +        if (window) {
  1.1086 +            nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
  1.1087 +            if (doc && doc == mObservedDocument)
  1.1088 +                NodeWillBeDestroyed(doc);
  1.1089 +        }
  1.1090 +    }
  1.1091 +    return NS_OK;
  1.1092 +}
  1.1093 +//----------------------------------------------------------------------
  1.1094 +//
  1.1095 +// nsIDocumentOberver interface
  1.1096 +//
  1.1097 +
  1.1098 +void
  1.1099 +nsXULTemplateBuilder::AttributeChanged(nsIDocument* aDocument,
  1.1100 +                                       Element*     aElement,
  1.1101 +                                       int32_t      aNameSpaceID,
  1.1102 +                                       nsIAtom*     aAttribute,
  1.1103 +                                       int32_t      aModType)
  1.1104 +{
  1.1105 +    if (aElement == mRoot && aNameSpaceID == kNameSpaceID_None) {
  1.1106 +        // Check for a change to the 'ref' attribute on an atom, in which
  1.1107 +        // case we may need to nuke and rebuild the entire content model
  1.1108 +        // beneath the element.
  1.1109 +        if (aAttribute == nsGkAtoms::ref)
  1.1110 +            nsContentUtils::AddScriptRunner(
  1.1111 +                NS_NewRunnableMethod(this, &nsXULTemplateBuilder::RunnableRebuild));
  1.1112 +
  1.1113 +        // Check for a change to the 'datasources' attribute. If so, setup
  1.1114 +        // mDB by parsing the new value and rebuild.
  1.1115 +        else if (aAttribute == nsGkAtoms::datasources) {
  1.1116 +            nsContentUtils::AddScriptRunner(
  1.1117 +                NS_NewRunnableMethod(this, &nsXULTemplateBuilder::RunnableLoadAndRebuild));
  1.1118 +        }
  1.1119 +    }
  1.1120 +}
  1.1121 +
  1.1122 +void
  1.1123 +nsXULTemplateBuilder::ContentRemoved(nsIDocument* aDocument,
  1.1124 +                                     nsIContent* aContainer,
  1.1125 +                                     nsIContent* aChild,
  1.1126 +                                     int32_t aIndexInContainer,
  1.1127 +                                     nsIContent* aPreviousSibling)
  1.1128 +{
  1.1129 +    if (mRoot && nsContentUtils::ContentIsDescendantOf(mRoot, aChild)) {
  1.1130 +        nsRefPtr<nsXULTemplateBuilder> kungFuDeathGrip(this);
  1.1131 +
  1.1132 +        if (mQueryProcessor)
  1.1133 +            mQueryProcessor->Done();
  1.1134 +
  1.1135 +        // Pass false to Uninit since content is going away anyway
  1.1136 +        nsContentUtils::AddScriptRunner(
  1.1137 +            NS_NewRunnableMethod(this, &nsXULTemplateBuilder::UninitFalse));
  1.1138 +
  1.1139 +        MOZ_ASSERT(aDocument == mObservedDocument);
  1.1140 +        StopObserving();
  1.1141 +
  1.1142 +        nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(aDocument);
  1.1143 +        if (xuldoc)
  1.1144 +            xuldoc->SetTemplateBuilderFor(mRoot, nullptr);
  1.1145 +
  1.1146 +        // clear the template state when removing content so that template
  1.1147 +        // content will be regenerated again if the content is reinserted
  1.1148 +        nsXULElement *xulcontent = nsXULElement::FromContent(mRoot);
  1.1149 +        if (xulcontent)
  1.1150 +            xulcontent->ClearTemplateGenerated();
  1.1151 +
  1.1152 +        CleanUp(true);
  1.1153 +
  1.1154 +        mDB = nullptr;
  1.1155 +        mCompDB = nullptr;
  1.1156 +        mDataSource = nullptr;
  1.1157 +    }
  1.1158 +}
  1.1159 +
  1.1160 +void
  1.1161 +nsXULTemplateBuilder::NodeWillBeDestroyed(const nsINode* aNode)
  1.1162 +{
  1.1163 +    // The call to RemoveObserver could release the last reference to
  1.1164 +    // |this|, so hold another reference.
  1.1165 +    nsRefPtr<nsXULTemplateBuilder> kungFuDeathGrip(this);
  1.1166 +
  1.1167 +    // Break circular references
  1.1168 +    if (mQueryProcessor)
  1.1169 +        mQueryProcessor->Done();
  1.1170 +
  1.1171 +    mDataSource = nullptr;
  1.1172 +    mDB = nullptr;
  1.1173 +    mCompDB = nullptr;
  1.1174 +
  1.1175 +    nsContentUtils::AddScriptRunner(
  1.1176 +        NS_NewRunnableMethod(this, &nsXULTemplateBuilder::UninitTrue));
  1.1177 +}
  1.1178 +
  1.1179 +
  1.1180 +
  1.1181 +
  1.1182 +//----------------------------------------------------------------------
  1.1183 +//
  1.1184 +// Implementation methods
  1.1185 +//
  1.1186 +
  1.1187 +nsresult
  1.1188 +nsXULTemplateBuilder::LoadDataSources(nsIDocument* aDocument,
  1.1189 +                                      bool* aShouldDelayBuilding)
  1.1190 +{
  1.1191 +    NS_PRECONDITION(mRoot != nullptr, "not initialized");
  1.1192 +
  1.1193 +    nsresult rv;
  1.1194 +    bool isRDFQuery = false;
  1.1195 +  
  1.1196 +    // we'll set these again later, after we create a new composite ds
  1.1197 +    mDB = nullptr;
  1.1198 +    mCompDB = nullptr;
  1.1199 +    mDataSource = nullptr;
  1.1200 +
  1.1201 +    *aShouldDelayBuilding = false;
  1.1202 +
  1.1203 +    nsAutoString datasources;
  1.1204 +    mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::datasources, datasources);
  1.1205 +
  1.1206 +    nsAutoString querytype;
  1.1207 +    mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::querytype, querytype);
  1.1208 +
  1.1209 +    // create the query processor. The querytype attribute on the root element
  1.1210 +    // may be used to create one of a specific type.
  1.1211 +  
  1.1212 +    // XXX should non-chrome be restricted to specific names?
  1.1213 +    if (querytype.IsEmpty())
  1.1214 +        querytype.AssignLiteral("rdf");
  1.1215 +
  1.1216 +    if (querytype.EqualsLiteral("rdf")) {
  1.1217 +        isRDFQuery = true;
  1.1218 +        mQueryProcessor = new nsXULTemplateQueryProcessorRDF();
  1.1219 +        NS_ENSURE_TRUE(mQueryProcessor, NS_ERROR_OUT_OF_MEMORY);
  1.1220 +    }
  1.1221 +    else if (querytype.EqualsLiteral("xml")) {
  1.1222 +        mQueryProcessor = new nsXULTemplateQueryProcessorXML();
  1.1223 +        NS_ENSURE_TRUE(mQueryProcessor, NS_ERROR_OUT_OF_MEMORY);
  1.1224 +    }
  1.1225 +    else if (querytype.EqualsLiteral("storage")) {
  1.1226 +        mQueryProcessor = new nsXULTemplateQueryProcessorStorage();
  1.1227 +        NS_ENSURE_TRUE(mQueryProcessor, NS_ERROR_OUT_OF_MEMORY);
  1.1228 +    }
  1.1229 +    else {
  1.1230 +        nsAutoCString cid(NS_QUERY_PROCESSOR_CONTRACTID_PREFIX);
  1.1231 +        AppendUTF16toUTF8(querytype, cid);
  1.1232 +        mQueryProcessor = do_CreateInstance(cid.get(), &rv);
  1.1233 +
  1.1234 +        if (!mQueryProcessor) {
  1.1235 +            nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_INVALID_QUERYPROCESSOR);
  1.1236 +            return rv;
  1.1237 +        }
  1.1238 +    }
  1.1239 +
  1.1240 +    rv = LoadDataSourceUrls(aDocument, datasources,
  1.1241 +                            isRDFQuery, aShouldDelayBuilding);
  1.1242 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1243 +
  1.1244 +    // Now set the database on the element, so that script writers can
  1.1245 +    // access it.
  1.1246 +    nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(aDocument);
  1.1247 +    if (xuldoc)
  1.1248 +        xuldoc->SetTemplateBuilderFor(mRoot, this);
  1.1249 +
  1.1250 +    if (!mRoot->IsXUL()) {
  1.1251 +        // Hmm. This must be an HTML element. Try to set it as a
  1.1252 +        // JS property "by hand".
  1.1253 +        InitHTMLTemplateRoot();
  1.1254 +    }
  1.1255 +  
  1.1256 +    return NS_OK;
  1.1257 +}
  1.1258 +  
  1.1259 +nsresult
  1.1260 +nsXULTemplateBuilder::LoadDataSourceUrls(nsIDocument* aDocument,
  1.1261 +                                         const nsAString& aDataSources,
  1.1262 +                                         bool aIsRDFQuery,
  1.1263 +                                         bool* aShouldDelayBuilding)
  1.1264 +{
  1.1265 +    // Grab the doc's principal...
  1.1266 +    nsIPrincipal *docPrincipal = aDocument->NodePrincipal();
  1.1267 +
  1.1268 +    NS_ASSERTION(docPrincipal == mRoot->NodePrincipal(),
  1.1269 +                 "Principal mismatch?  Which one to use?");
  1.1270 +
  1.1271 +    bool isTrusted = false;
  1.1272 +    nsresult rv = IsSystemPrincipal(docPrincipal, &isTrusted);
  1.1273 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1274 +
  1.1275 +    // Parse datasources: they are assumed to be a whitespace
  1.1276 +    // separated list of URIs; e.g.,
  1.1277 +    //
  1.1278 +    //     rdf:bookmarks rdf:history http://foo.bar.com/blah.cgi?baz=9
  1.1279 +    //
  1.1280 +    nsIURI *docurl = aDocument->GetDocumentURI();
  1.1281 +
  1.1282 +    nsCOMPtr<nsIMutableArray> uriList = do_CreateInstance(NS_ARRAY_CONTRACTID);
  1.1283 +    if (!uriList)
  1.1284 +        return NS_ERROR_FAILURE;
  1.1285 +
  1.1286 +    nsAutoString datasources(aDataSources);
  1.1287 +    uint32_t first = 0;
  1.1288 +    while (1) {
  1.1289 +        while (first < datasources.Length() && nsCRT::IsAsciiSpace(datasources.CharAt(first)))
  1.1290 +            ++first;
  1.1291 +
  1.1292 +        if (first >= datasources.Length())
  1.1293 +            break;
  1.1294 +
  1.1295 +        uint32_t last = first;
  1.1296 +        while (last < datasources.Length() && !nsCRT::IsAsciiSpace(datasources.CharAt(last)))
  1.1297 +            ++last;
  1.1298 +
  1.1299 +        nsAutoString uriStr;
  1.1300 +        datasources.Mid(uriStr, first, last - first);
  1.1301 +        first = last + 1;
  1.1302 +
  1.1303 +        // A special 'dummy' datasource
  1.1304 +        if (uriStr.EqualsLiteral("rdf:null"))
  1.1305 +            continue;
  1.1306 +
  1.1307 +        if (uriStr.CharAt(0) == '#') {
  1.1308 +            // ok, the datasource is certainly a node of the current document
  1.1309 +            nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(aDocument);
  1.1310 +            nsCOMPtr<nsIDOMElement> dsnode;
  1.1311 +
  1.1312 +            domdoc->GetElementById(Substring(uriStr, 1),
  1.1313 +                                   getter_AddRefs(dsnode));
  1.1314 +
  1.1315 +            if (dsnode)
  1.1316 +                uriList->AppendElement(dsnode, false);
  1.1317 +            continue;
  1.1318 +        }
  1.1319 +
  1.1320 +        // N.B. that `failure' (e.g., because it's an unknown
  1.1321 +        // protocol) leaves uriStr unaltered.
  1.1322 +        NS_MakeAbsoluteURI(uriStr, uriStr, docurl);
  1.1323 +
  1.1324 +        nsCOMPtr<nsIURI> uri;
  1.1325 +        rv = NS_NewURI(getter_AddRefs(uri), uriStr);
  1.1326 +        if (NS_FAILED(rv) || !uri)
  1.1327 +            continue; // Necko will barf if our URI is weird
  1.1328 +
  1.1329 +        // don't add the uri to the list if the document is not allowed to
  1.1330 +        // load it
  1.1331 +        if (!isTrusted && NS_FAILED(docPrincipal->CheckMayLoad(uri, true, false)))
  1.1332 +          continue;
  1.1333 +
  1.1334 +        uriList->AppendElement(uri, false);
  1.1335 +    }
  1.1336 +
  1.1337 +    nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(mRoot);
  1.1338 +    rv = mQueryProcessor->GetDatasource(uriList,
  1.1339 +                                        rootNode,
  1.1340 +                                        isTrusted,
  1.1341 +                                        this,
  1.1342 +                                        aShouldDelayBuilding,
  1.1343 +                                        getter_AddRefs(mDataSource));
  1.1344 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1345 +
  1.1346 +    if (aIsRDFQuery && mDataSource) {  
  1.1347 +        // check if we were given an inference engine type
  1.1348 +        nsCOMPtr<nsIRDFInferDataSource> inferDB = do_QueryInterface(mDataSource);
  1.1349 +        if (inferDB) {
  1.1350 +            nsCOMPtr<nsIRDFDataSource> ds;
  1.1351 +            inferDB->GetBaseDataSource(getter_AddRefs(ds));
  1.1352 +            if (ds)
  1.1353 +                mCompDB = do_QueryInterface(ds);
  1.1354 +        }
  1.1355 +
  1.1356 +        if (!mCompDB)
  1.1357 +            mCompDB = do_QueryInterface(mDataSource);
  1.1358 +
  1.1359 +        mDB = do_QueryInterface(mDataSource);
  1.1360 +    }
  1.1361 +
  1.1362 +    if (!mDB && isTrusted) {
  1.1363 +        gRDFService->GetDataSource("rdf:local-store", getter_AddRefs(mDB));
  1.1364 +    }
  1.1365 +
  1.1366 +    return NS_OK;
  1.1367 +}
  1.1368 +
  1.1369 +nsresult
  1.1370 +nsXULTemplateBuilder::InitHTMLTemplateRoot()
  1.1371 +{
  1.1372 +    // Use XPConnect and the JS APIs to whack mDB and this as the
  1.1373 +    // 'database' and 'builder' properties onto aElement.
  1.1374 +    nsresult rv;
  1.1375 +
  1.1376 +    nsCOMPtr<nsIDocument> doc = mRoot->GetDocument();
  1.1377 +    NS_ASSERTION(doc, "no document");
  1.1378 +    if (! doc)
  1.1379 +        return NS_ERROR_UNEXPECTED;
  1.1380 +
  1.1381 +    nsCOMPtr<nsIScriptGlobalObject> global =
  1.1382 +      do_QueryInterface(doc->GetWindow());
  1.1383 +    if (! global)
  1.1384 +        return NS_ERROR_UNEXPECTED;
  1.1385 +
  1.1386 +    nsCOMPtr<nsIGlobalObject> innerWin =
  1.1387 +        do_QueryInterface(doc->GetInnerWindow());
  1.1388 +
  1.1389 +    // We are going to run script via JS_SetProperty, so we need a script entry
  1.1390 +    // point, but as this is XUL related it does not appear in the HTML spec.
  1.1391 +    AutoEntryScript entryScript(innerWin, true);
  1.1392 +    JSContext* jscontext = entryScript.cx();
  1.1393 +
  1.1394 +    JS::Rooted<JS::Value> v(jscontext);
  1.1395 +    rv = nsContentUtils::WrapNative(jscontext, mRoot, mRoot, &v);
  1.1396 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1397 +
  1.1398 +    JS::Rooted<JSObject*> jselement(jscontext, JSVAL_TO_OBJECT(v));
  1.1399 +
  1.1400 +    if (mDB) {
  1.1401 +        // database
  1.1402 +        JS::Rooted<JS::Value> jsdatabase(jscontext);
  1.1403 +        rv = nsContentUtils::WrapNative(jscontext, mDB,
  1.1404 +                                        &NS_GET_IID(nsIRDFCompositeDataSource),
  1.1405 +                                        &jsdatabase);
  1.1406 +        NS_ENSURE_SUCCESS(rv, rv);
  1.1407 +
  1.1408 +        bool ok = JS_SetProperty(jscontext, jselement, "database", jsdatabase);
  1.1409 +        NS_ASSERTION(ok, "unable to set database property");
  1.1410 +        if (! ok)
  1.1411 +            return NS_ERROR_FAILURE;
  1.1412 +    }
  1.1413 +
  1.1414 +    {
  1.1415 +        // builder
  1.1416 +        JS::Rooted<JS::Value> jsbuilder(jscontext);
  1.1417 +        rv = nsContentUtils::WrapNative(jscontext,
  1.1418 +                                        static_cast<nsIXULTemplateBuilder*>(this),
  1.1419 +                                        &NS_GET_IID(nsIXULTemplateBuilder),
  1.1420 +                                        &jsbuilder);
  1.1421 +        NS_ENSURE_SUCCESS(rv, rv);
  1.1422 +
  1.1423 +        bool ok = JS_SetProperty(jscontext, jselement, "builder", jsbuilder);
  1.1424 +        if (! ok)
  1.1425 +            return NS_ERROR_FAILURE;
  1.1426 +    }
  1.1427 +
  1.1428 +    return NS_OK;
  1.1429 +}
  1.1430 +
  1.1431 +nsresult
  1.1432 +nsXULTemplateBuilder::DetermineMatchedRule(nsIContent *aContainer,
  1.1433 +                                           nsIXULTemplateResult* aResult,
  1.1434 +                                           nsTemplateQuerySet* aQuerySet,
  1.1435 +                                           nsTemplateRule** aMatchedRule,
  1.1436 +                                           int16_t *aRuleIndex)
  1.1437 +{
  1.1438 +    // iterate through the rules and look for one that the result matches
  1.1439 +    int16_t count = aQuerySet->RuleCount();
  1.1440 +    for (int16_t r = 0; r < count; r++) {
  1.1441 +        nsTemplateRule* rule = aQuerySet->GetRuleAt(r);
  1.1442 +        // If a tag was specified, it must match the tag of the container
  1.1443 +        // where content is being inserted.
  1.1444 +        nsIAtom* tag = rule->GetTag();
  1.1445 +        if ((!aContainer || !tag || tag == aContainer->Tag()) &&
  1.1446 +            rule->CheckMatch(aResult)) {
  1.1447 +            *aMatchedRule = rule;
  1.1448 +            *aRuleIndex = r;
  1.1449 +            return NS_OK;
  1.1450 +        }
  1.1451 +    }
  1.1452 +
  1.1453 +    *aRuleIndex = -1;
  1.1454 +    *aMatchedRule = nullptr;
  1.1455 +    return NS_OK;
  1.1456 +}
  1.1457 +
  1.1458 +void
  1.1459 +nsXULTemplateBuilder::ParseAttribute(const nsAString& aAttributeValue,
  1.1460 +                                     void (*aVariableCallback)(nsXULTemplateBuilder*, const nsAString&, void*),
  1.1461 +                                     void (*aTextCallback)(nsXULTemplateBuilder*, const nsAString&, void*),
  1.1462 +                                     void* aClosure)
  1.1463 +{
  1.1464 +    nsAString::const_iterator done_parsing;
  1.1465 +    aAttributeValue.EndReading(done_parsing);
  1.1466 +
  1.1467 +    nsAString::const_iterator iter;
  1.1468 +    aAttributeValue.BeginReading(iter);
  1.1469 +
  1.1470 +    nsAString::const_iterator mark(iter), backup(iter);
  1.1471 +
  1.1472 +    for (; iter != done_parsing; backup = ++iter) {
  1.1473 +        // A variable is either prefixed with '?' (in the extended
  1.1474 +        // syntax) or "rdf:" (in the simple syntax).
  1.1475 +        bool isvar;
  1.1476 +        if (*iter == char16_t('?') && (++iter != done_parsing)) {
  1.1477 +            isvar = true;
  1.1478 +        }
  1.1479 +        else if ((*iter == char16_t('r') && (++iter != done_parsing)) &&
  1.1480 +                 (*iter == char16_t('d') && (++iter != done_parsing)) &&
  1.1481 +                 (*iter == char16_t('f') && (++iter != done_parsing)) &&
  1.1482 +                 (*iter == char16_t(':') && (++iter != done_parsing))) {
  1.1483 +            isvar = true;
  1.1484 +        }
  1.1485 +        else {
  1.1486 +            isvar = false;
  1.1487 +        }
  1.1488 +
  1.1489 +        if (! isvar) {
  1.1490 +            // It's not a variable, or we ran off the end of the
  1.1491 +            // string after the initial variable prefix. Since we may
  1.1492 +            // have slurped down some characters before realizing that
  1.1493 +            // fact, back up to the point where we started.
  1.1494 +            iter = backup;
  1.1495 +            continue;
  1.1496 +        }
  1.1497 +        else if (backup != mark && aTextCallback) {
  1.1498 +            // Okay, we've found a variable, and there's some vanilla
  1.1499 +            // text that's been buffered up. Flush it.
  1.1500 +            (*aTextCallback)(this, Substring(mark, backup), aClosure);
  1.1501 +        }
  1.1502 +
  1.1503 +        if (*iter == char16_t('?')) {
  1.1504 +            // Well, it was not really a variable, but "??". We use one
  1.1505 +            // question mark (the second one, actually) literally.
  1.1506 +            mark = iter;
  1.1507 +            continue;
  1.1508 +        }
  1.1509 +
  1.1510 +        // Construct a substring that is the symbol we need to look up
  1.1511 +        // in the rule's symbol table. The symbol is terminated by a
  1.1512 +        // space character, a caret, or the end of the string,
  1.1513 +        // whichever comes first.
  1.1514 +        nsAString::const_iterator first(backup);
  1.1515 +
  1.1516 +        char16_t c = 0;
  1.1517 +        while (iter != done_parsing) {
  1.1518 +            c = *iter;
  1.1519 +            if ((c == char16_t(' ')) || (c == char16_t('^')))
  1.1520 +                break;
  1.1521 +
  1.1522 +            ++iter;
  1.1523 +        }
  1.1524 +
  1.1525 +        nsAString::const_iterator last(iter);
  1.1526 +
  1.1527 +        // Back up so we don't consume the terminating character
  1.1528 +        // *unless* the terminating character was a caret: the caret
  1.1529 +        // means "concatenate with no space in between".
  1.1530 +        if (c != char16_t('^'))
  1.1531 +            --iter;
  1.1532 +
  1.1533 +        (*aVariableCallback)(this, Substring(first, last), aClosure);
  1.1534 +        mark = iter;
  1.1535 +        ++mark;
  1.1536 +    }
  1.1537 +
  1.1538 +    if (backup != mark && aTextCallback) {
  1.1539 +        // If there's any text left over, then fire the text callback
  1.1540 +        (*aTextCallback)(this, Substring(mark, backup), aClosure);
  1.1541 +    }
  1.1542 +}
  1.1543 +
  1.1544 +
  1.1545 +struct MOZ_STACK_CLASS SubstituteTextClosure {
  1.1546 +    SubstituteTextClosure(nsIXULTemplateResult* aResult, nsAString& aString)
  1.1547 +        : result(aResult), str(aString) {}
  1.1548 +
  1.1549 +    // some datasources are lazily initialized or modified while values are
  1.1550 +    // being retrieved, causing results to be removed. Due to this, hold a
  1.1551 +    // strong reference to the result.
  1.1552 +    nsCOMPtr<nsIXULTemplateResult> result;
  1.1553 +    nsAString& str;
  1.1554 +};
  1.1555 +
  1.1556 +nsresult
  1.1557 +nsXULTemplateBuilder::SubstituteText(nsIXULTemplateResult* aResult,
  1.1558 +                                     const nsAString& aAttributeValue,
  1.1559 +                                     nsAString& aString)
  1.1560 +{
  1.1561 +    // See if it's the special value "..."
  1.1562 +    if (aAttributeValue.EqualsLiteral("...")) {
  1.1563 +        aResult->GetId(aString);
  1.1564 +        return NS_OK;
  1.1565 +    }
  1.1566 +
  1.1567 +    // Reasonable guess at how big it should be
  1.1568 +    aString.SetCapacity(aAttributeValue.Length());
  1.1569 +
  1.1570 +    SubstituteTextClosure closure(aResult, aString);
  1.1571 +    ParseAttribute(aAttributeValue,
  1.1572 +                   SubstituteTextReplaceVariable,
  1.1573 +                   SubstituteTextAppendText,
  1.1574 +                   &closure);
  1.1575 +
  1.1576 +    return NS_OK;
  1.1577 +}
  1.1578 +
  1.1579 +
  1.1580 +void
  1.1581 +nsXULTemplateBuilder::SubstituteTextAppendText(nsXULTemplateBuilder* aThis,
  1.1582 +                                               const nsAString& aText,
  1.1583 +                                               void* aClosure)
  1.1584 +{
  1.1585 +    // Append aString to the closure's result
  1.1586 +    SubstituteTextClosure* c = static_cast<SubstituteTextClosure*>(aClosure);
  1.1587 +    c->str.Append(aText);
  1.1588 +}
  1.1589 +
  1.1590 +void
  1.1591 +nsXULTemplateBuilder::SubstituteTextReplaceVariable(nsXULTemplateBuilder* aThis,
  1.1592 +                                                    const nsAString& aVariable,
  1.1593 +                                                    void* aClosure)
  1.1594 +{
  1.1595 +    // Substitute the value for the variable and append to the
  1.1596 +    // closure's result.
  1.1597 +    SubstituteTextClosure* c = static_cast<SubstituteTextClosure*>(aClosure);
  1.1598 +
  1.1599 +    nsAutoString replacementText;
  1.1600 +
  1.1601 +    // The symbol "rdf:*" is special, and means "this guy's URI"
  1.1602 +    if (aVariable.EqualsLiteral("rdf:*")){
  1.1603 +        c->result->GetId(replacementText);
  1.1604 +    }
  1.1605 +    else {
  1.1606 +        // Got a variable; get the value it's assigned to
  1.1607 +        nsCOMPtr<nsIAtom> var = do_GetAtom(aVariable);
  1.1608 +        c->result->GetBindingFor(var, replacementText);
  1.1609 +    }
  1.1610 +
  1.1611 +    c->str += replacementText;
  1.1612 +}
  1.1613 +
  1.1614 +bool
  1.1615 +nsXULTemplateBuilder::IsTemplateElement(nsIContent* aContent)
  1.1616 +{
  1.1617 +    return aContent->NodeInfo()->Equals(nsGkAtoms::_template,
  1.1618 +                                        kNameSpaceID_XUL);
  1.1619 +}
  1.1620 +
  1.1621 +nsresult
  1.1622 +nsXULTemplateBuilder::GetTemplateRoot(nsIContent** aResult)
  1.1623 +{
  1.1624 +    NS_PRECONDITION(mRoot != nullptr, "not initialized");
  1.1625 +    if (! mRoot)
  1.1626 +        return NS_ERROR_NOT_INITIALIZED;
  1.1627 +
  1.1628 +    // First, check and see if the root has a template attribute. This
  1.1629 +    // allows a template to be specified "out of line"; e.g.,
  1.1630 +    //
  1.1631 +    //   <window>
  1.1632 +    //     <foo template="MyTemplate">...</foo>
  1.1633 +    //     <template id="MyTemplate">...</template>
  1.1634 +    //   </window>
  1.1635 +    //
  1.1636 +    nsAutoString templateID;
  1.1637 +    mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::_template, templateID);
  1.1638 +
  1.1639 +    if (! templateID.IsEmpty()) {
  1.1640 +        nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(mRoot->GetDocument());
  1.1641 +        if (! domDoc)
  1.1642 +            return NS_OK;
  1.1643 +
  1.1644 +        nsCOMPtr<nsIDOMElement> domElement;
  1.1645 +        domDoc->GetElementById(templateID, getter_AddRefs(domElement));
  1.1646 +
  1.1647 +        if (domElement) {
  1.1648 +            nsCOMPtr<nsIContent> content = do_QueryInterface(domElement);
  1.1649 +            NS_ENSURE_STATE(content &&
  1.1650 +                            !nsContentUtils::ContentIsDescendantOf(mRoot,
  1.1651 +                                                                   content));
  1.1652 +            content.forget(aResult);
  1.1653 +            return NS_OK;
  1.1654 +        }
  1.1655 +    }
  1.1656 +
  1.1657 +    // If root node has no template attribute, then look for a child
  1.1658 +    // node which is a template tag.
  1.1659 +    for (nsIContent* child = mRoot->GetFirstChild();
  1.1660 +         child;
  1.1661 +         child = child->GetNextSibling()) {
  1.1662 +
  1.1663 +        if (IsTemplateElement(child)) {
  1.1664 +            NS_ADDREF(*aResult = child);
  1.1665 +            return NS_OK;
  1.1666 +        }
  1.1667 +    }
  1.1668 +
  1.1669 +    // Look through the anonymous children as well. Although FlattenedChildIterator
  1.1670 +    // will find a template element that has been placed in an insertion point, many
  1.1671 +    // bindings do not have a specific insertion point for the template element, which
  1.1672 +    // would cause it to not be part of the flattened content tree. The check above to
  1.1673 +    // check the explicit children as well handles this case.
  1.1674 +    FlattenedChildIterator iter(mRoot);
  1.1675 +    for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
  1.1676 +        if (IsTemplateElement(child)) {
  1.1677 +            NS_ADDREF(*aResult = child);
  1.1678 +            return NS_OK;
  1.1679 +        }
  1.1680 +    }
  1.1681 +
  1.1682 +    *aResult = nullptr;
  1.1683 +    return NS_OK;
  1.1684 +}
  1.1685 +
  1.1686 +nsresult
  1.1687 +nsXULTemplateBuilder::CompileQueries()
  1.1688 +{
  1.1689 +    nsCOMPtr<nsIContent> tmpl;
  1.1690 +    GetTemplateRoot(getter_AddRefs(tmpl));
  1.1691 +    if (! tmpl)
  1.1692 +        return NS_OK;
  1.1693 +
  1.1694 +    if (! mRoot)
  1.1695 +        return NS_ERROR_NOT_INITIALIZED;
  1.1696 +
  1.1697 +    // Determine if there are any special settings we need to observe
  1.1698 +    mFlags = 0;
  1.1699 +
  1.1700 +    nsAutoString flags;
  1.1701 +    mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::flags, flags);
  1.1702 +
  1.1703 +    // if the dont-test-empty flag is set, containers should not be checked to
  1.1704 +    // see if they are empty. If dont-recurse is set, then don't process the
  1.1705 +    // template recursively and only show one level of results. The logging
  1.1706 +    // flag logs errors and results to the console, which is useful when
  1.1707 +    // debugging templates.
  1.1708 +    nsWhitespaceTokenizer tokenizer(flags);
  1.1709 +    while (tokenizer.hasMoreTokens()) {
  1.1710 +      const nsDependentSubstring& token(tokenizer.nextToken());
  1.1711 +      if (token.EqualsLiteral("dont-test-empty"))
  1.1712 +        mFlags |= eDontTestEmpty;
  1.1713 +      else if (token.EqualsLiteral("dont-recurse"))
  1.1714 +        mFlags |= eDontRecurse;
  1.1715 +      else if (token.EqualsLiteral("logging"))
  1.1716 +        mFlags |= eLoggingEnabled;
  1.1717 +    }
  1.1718 +
  1.1719 +#ifdef PR_LOGGING
  1.1720 +    // always enable logging if the debug setting is used
  1.1721 +    if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG))
  1.1722 +        mFlags |= eLoggingEnabled;
  1.1723 +#endif
  1.1724 +
  1.1725 +    nsCOMPtr<nsIDOMNode> rootnode = do_QueryInterface(mRoot);
  1.1726 +    nsresult rv =
  1.1727 +        mQueryProcessor->InitializeForBuilding(mDataSource, this, rootnode);
  1.1728 +    if (NS_FAILED(rv))
  1.1729 +        return rv;
  1.1730 +
  1.1731 +    // Set the "container" and "member" variables, if the user has specified
  1.1732 +    // them. The container variable may be specified with the container
  1.1733 +    // attribute on the <template> and the member variable may be specified
  1.1734 +    // using the member attribute or the value of the uri attribute inside the
  1.1735 +    // first action body in the template. If not specified, the container
  1.1736 +    // variable defaults to '?uri' and the member variable defaults to '?' or
  1.1737 +    // 'rdf:*' for simple queries.
  1.1738 +
  1.1739 +    // For RDF queries, the container variable may also be set via the
  1.1740 +    // <content> tag.
  1.1741 +
  1.1742 +    nsAutoString containervar;
  1.1743 +    tmpl->GetAttr(kNameSpaceID_None, nsGkAtoms::container, containervar);
  1.1744 +
  1.1745 +    if (containervar.IsEmpty())
  1.1746 +        mRefVariable = do_GetAtom("?uri");
  1.1747 +    else
  1.1748 +        mRefVariable = do_GetAtom(containervar);
  1.1749 +
  1.1750 +    nsAutoString membervar;
  1.1751 +    tmpl->GetAttr(kNameSpaceID_None, nsGkAtoms::member, membervar);
  1.1752 +
  1.1753 +    if (membervar.IsEmpty())
  1.1754 +        mMemberVariable = nullptr;
  1.1755 +    else
  1.1756 +        mMemberVariable = do_GetAtom(membervar);
  1.1757 +
  1.1758 +    nsTemplateQuerySet* queryset = new nsTemplateQuerySet(0);
  1.1759 +    if (!queryset)
  1.1760 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1761 +
  1.1762 +    if (!mQuerySets.AppendElement(queryset)) {
  1.1763 +        delete queryset;
  1.1764 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1765 +    }
  1.1766 +
  1.1767 +    bool canUseTemplate = false;
  1.1768 +    int32_t priority = 0;
  1.1769 +    rv = CompileTemplate(tmpl, queryset, false, &priority, &canUseTemplate);
  1.1770 +
  1.1771 +    if (NS_FAILED(rv) || !canUseTemplate) {
  1.1772 +        for (int32_t q = mQuerySets.Length() - 1; q >= 0; q--) {
  1.1773 +            nsTemplateQuerySet* qs = mQuerySets[q];
  1.1774 +            delete qs;
  1.1775 +        }
  1.1776 +        mQuerySets.Clear();
  1.1777 +    }
  1.1778 +
  1.1779 +    mQueriesCompiled = true;
  1.1780 +
  1.1781 +    return NS_OK;
  1.1782 +}
  1.1783 +
  1.1784 +nsresult
  1.1785 +nsXULTemplateBuilder::CompileTemplate(nsIContent* aTemplate,
  1.1786 +                                      nsTemplateQuerySet* aQuerySet,
  1.1787 +                                      bool aIsQuerySet,
  1.1788 +                                      int32_t* aPriority,
  1.1789 +                                      bool* aCanUseTemplate)
  1.1790 +{
  1.1791 +    NS_ASSERTION(aQuerySet, "No queryset supplied");
  1.1792 +
  1.1793 +    nsresult rv = NS_OK;
  1.1794 +
  1.1795 +    bool isQuerySetMode = false;
  1.1796 +    bool hasQuerySet = false, hasRule = false, hasQuery = false;
  1.1797 +
  1.1798 +    for (nsIContent* rulenode = aTemplate->GetFirstChild();
  1.1799 +         rulenode;
  1.1800 +         rulenode = rulenode->GetNextSibling()) {
  1.1801 +
  1.1802 +        nsINodeInfo *ni = rulenode->NodeInfo();
  1.1803 +
  1.1804 +        // don't allow more queries than can be supported
  1.1805 +        if (*aPriority == INT16_MAX)
  1.1806 +            return NS_ERROR_FAILURE;
  1.1807 +
  1.1808 +        // XXXndeakin queryset isn't a good name for this tag since it only
  1.1809 +        //            ever contains one query
  1.1810 +        if (!aIsQuerySet && ni->Equals(nsGkAtoms::queryset, kNameSpaceID_XUL)) {
  1.1811 +            if (hasRule || hasQuery) {
  1.1812 +              nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_INVALID_QUERYSET);
  1.1813 +              continue;
  1.1814 +            }
  1.1815 +
  1.1816 +            isQuerySetMode = true;
  1.1817 +
  1.1818 +            // only create a queryset for those after the first since the
  1.1819 +            // first one is always created by CompileQueries
  1.1820 +            if (hasQuerySet) {
  1.1821 +                aQuerySet = new nsTemplateQuerySet(++*aPriority);
  1.1822 +                if (!aQuerySet)
  1.1823 +                    return NS_ERROR_OUT_OF_MEMORY;
  1.1824 +
  1.1825 +                // once the queryset is appended to the mQuerySets list, it
  1.1826 +                // will be removed by CompileQueries if an error occurs
  1.1827 +                if (!mQuerySets.AppendElement(aQuerySet)) {
  1.1828 +                    delete aQuerySet;
  1.1829 +                    return NS_ERROR_OUT_OF_MEMORY;
  1.1830 +                }
  1.1831 +            }
  1.1832 +
  1.1833 +            hasQuerySet = true;
  1.1834 +
  1.1835 +            rv = CompileTemplate(rulenode, aQuerySet, true, aPriority, aCanUseTemplate);
  1.1836 +            if (NS_FAILED(rv))
  1.1837 +                return rv;
  1.1838 +        }
  1.1839 +
  1.1840 +        // once a queryset is used, everything must be a queryset
  1.1841 +        if (isQuerySetMode)
  1.1842 +            continue;
  1.1843 +
  1.1844 +        if (ni->Equals(nsGkAtoms::rule, kNameSpaceID_XUL)) {
  1.1845 +            nsCOMPtr<nsIContent> action;
  1.1846 +            nsXULContentUtils::FindChildByTag(rulenode,
  1.1847 +                                              kNameSpaceID_XUL,
  1.1848 +                                              nsGkAtoms::action,
  1.1849 +                                              getter_AddRefs(action));
  1.1850 +
  1.1851 +            if (action){
  1.1852 +                nsCOMPtr<nsIAtom> memberVariable = mMemberVariable;
  1.1853 +                if (!memberVariable) {
  1.1854 +                    memberVariable = DetermineMemberVariable(action);
  1.1855 +                    if (!memberVariable) {
  1.1856 +                        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_NO_MEMBERVAR);
  1.1857 +                        continue;
  1.1858 +                    }
  1.1859 +                }
  1.1860 +
  1.1861 +                if (hasQuery) {
  1.1862 +                    nsCOMPtr<nsIAtom> tag;
  1.1863 +                    DetermineRDFQueryRef(aQuerySet->mQueryNode,
  1.1864 +                                         getter_AddRefs(tag));
  1.1865 +                    if (tag)
  1.1866 +                        aQuerySet->SetTag(tag);
  1.1867 +
  1.1868 +                    if (! aQuerySet->mCompiledQuery) {
  1.1869 +                        nsCOMPtr<nsIDOMNode> query(do_QueryInterface(aQuerySet->mQueryNode));
  1.1870 +
  1.1871 +                        rv = mQueryProcessor->CompileQuery(this, query,
  1.1872 +                                                           mRefVariable, memberVariable,
  1.1873 +                                                           getter_AddRefs(aQuerySet->mCompiledQuery));
  1.1874 +                        if (NS_FAILED(rv))
  1.1875 +                            return rv;
  1.1876 +                    }
  1.1877 +
  1.1878 +                    if (aQuerySet->mCompiledQuery) {
  1.1879 +                        rv = CompileExtendedQuery(rulenode, action, memberVariable,
  1.1880 +                                                  aQuerySet);
  1.1881 +                        if (NS_FAILED(rv))
  1.1882 +                            return rv;
  1.1883 +
  1.1884 +                        *aCanUseTemplate = true;
  1.1885 +                    }
  1.1886 +                }
  1.1887 +                else {
  1.1888 +                    // backwards-compatible RDF template syntax where there is
  1.1889 +                    // an <action> node but no <query> node. In this case,
  1.1890 +                    // use the conditions as if it was the query.
  1.1891 +
  1.1892 +                    nsCOMPtr<nsIContent> conditions;
  1.1893 +                    nsXULContentUtils::FindChildByTag(rulenode,
  1.1894 +                                                      kNameSpaceID_XUL,
  1.1895 +                                                      nsGkAtoms::conditions,
  1.1896 +                                                      getter_AddRefs(conditions));
  1.1897 +
  1.1898 +                    if (conditions) {
  1.1899 +                        // create a new queryset if one hasn't been created already
  1.1900 +                        if (hasQuerySet) {
  1.1901 +                            aQuerySet = new nsTemplateQuerySet(++*aPriority);
  1.1902 +                            if (! aQuerySet)
  1.1903 +                                return NS_ERROR_OUT_OF_MEMORY;
  1.1904 +
  1.1905 +                            if (!mQuerySets.AppendElement(aQuerySet)) {
  1.1906 +                                delete aQuerySet;
  1.1907 +                                return NS_ERROR_OUT_OF_MEMORY;
  1.1908 +                            }
  1.1909 +                        }
  1.1910 +
  1.1911 +                        nsCOMPtr<nsIAtom> tag;
  1.1912 +                        DetermineRDFQueryRef(conditions, getter_AddRefs(tag));
  1.1913 +                        if (tag)
  1.1914 +                            aQuerySet->SetTag(tag);
  1.1915 +
  1.1916 +                        hasQuerySet = true;
  1.1917 +
  1.1918 +                        nsCOMPtr<nsIDOMNode> conditionsnode(do_QueryInterface(conditions));
  1.1919 +
  1.1920 +                        aQuerySet->mQueryNode = conditions;
  1.1921 +                        rv = mQueryProcessor->CompileQuery(this, conditionsnode,
  1.1922 +                                                           mRefVariable,
  1.1923 +                                                           memberVariable,
  1.1924 +                                                           getter_AddRefs(aQuerySet->mCompiledQuery));
  1.1925 +                        if (NS_FAILED(rv))
  1.1926 +                            return rv;
  1.1927 +
  1.1928 +                        if (aQuerySet->mCompiledQuery) {
  1.1929 +                            rv = CompileExtendedQuery(rulenode, action, memberVariable,
  1.1930 +                                                      aQuerySet);
  1.1931 +                            if (NS_FAILED(rv))
  1.1932 +                                return rv;
  1.1933 +
  1.1934 +                            *aCanUseTemplate = true;
  1.1935 +                        }
  1.1936 +                    }
  1.1937 +                }
  1.1938 +            }
  1.1939 +            else {
  1.1940 +                if (hasQuery)
  1.1941 +                    continue;
  1.1942 +
  1.1943 +                // a new queryset must always be created in this case
  1.1944 +                if (hasQuerySet) {
  1.1945 +                    aQuerySet = new nsTemplateQuerySet(++*aPriority);
  1.1946 +                    if (! aQuerySet)
  1.1947 +                        return NS_ERROR_OUT_OF_MEMORY;
  1.1948 +
  1.1949 +                    if (!mQuerySets.AppendElement(aQuerySet)) {
  1.1950 +                        delete aQuerySet;
  1.1951 +                        return NS_ERROR_OUT_OF_MEMORY;
  1.1952 +                    }
  1.1953 +                }
  1.1954 +
  1.1955 +                hasQuerySet = true;
  1.1956 +
  1.1957 +                rv = CompileSimpleQuery(rulenode, aQuerySet, aCanUseTemplate);
  1.1958 +                if (NS_FAILED(rv))
  1.1959 +                    return rv;
  1.1960 +            }
  1.1961 +
  1.1962 +            hasRule = true;
  1.1963 +        }
  1.1964 +        else if (ni->Equals(nsGkAtoms::query, kNameSpaceID_XUL)) {
  1.1965 +            if (hasQuery)
  1.1966 +              continue;
  1.1967 +
  1.1968 +            aQuerySet->mQueryNode = rulenode;
  1.1969 +            hasQuery = true;
  1.1970 +        }
  1.1971 +        else if (ni->Equals(nsGkAtoms::action, kNameSpaceID_XUL)) {
  1.1972 +            // the query must appear before the action
  1.1973 +            if (! hasQuery)
  1.1974 +                continue;
  1.1975 +
  1.1976 +            nsCOMPtr<nsIAtom> tag;
  1.1977 +            DetermineRDFQueryRef(aQuerySet->mQueryNode, getter_AddRefs(tag));
  1.1978 +            if (tag)
  1.1979 +                aQuerySet->SetTag(tag);
  1.1980 +
  1.1981 +            nsCOMPtr<nsIAtom> memberVariable = mMemberVariable;
  1.1982 +            if (!memberVariable) {
  1.1983 +                memberVariable = DetermineMemberVariable(rulenode);
  1.1984 +                if (!memberVariable) {
  1.1985 +                    nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_NO_MEMBERVAR);
  1.1986 +                    continue;
  1.1987 +                }
  1.1988 +            }
  1.1989 +
  1.1990 +            nsCOMPtr<nsIDOMNode> query(do_QueryInterface(aQuerySet->mQueryNode));
  1.1991 +
  1.1992 +            rv = mQueryProcessor->CompileQuery(this, query,
  1.1993 +                                               mRefVariable, memberVariable,
  1.1994 +                                               getter_AddRefs(aQuerySet->mCompiledQuery));
  1.1995 +
  1.1996 +            if (aQuerySet->mCompiledQuery) {
  1.1997 +                nsTemplateRule* rule = aQuerySet->NewRule(aTemplate, rulenode, aQuerySet);
  1.1998 +                if (! rule)
  1.1999 +                    return NS_ERROR_OUT_OF_MEMORY;
  1.2000 +
  1.2001 +                rule->SetVars(mRefVariable, memberVariable);
  1.2002 +
  1.2003 +                *aCanUseTemplate = true;
  1.2004 +
  1.2005 +                return NS_OK;
  1.2006 +            }
  1.2007 +        }
  1.2008 +    }
  1.2009 +
  1.2010 +    if (! hasRule && ! hasQuery && ! hasQuerySet) {
  1.2011 +        // if no rules are specified in the template, then the contents of the
  1.2012 +        // <template> tag are the one-and-only template.
  1.2013 +        rv = CompileSimpleQuery(aTemplate, aQuerySet, aCanUseTemplate);
  1.2014 +     }
  1.2015 +
  1.2016 +    return rv;
  1.2017 +}
  1.2018 +
  1.2019 +nsresult
  1.2020 +nsXULTemplateBuilder::CompileExtendedQuery(nsIContent* aRuleElement,
  1.2021 +                                           nsIContent* aActionElement,
  1.2022 +                                           nsIAtom* aMemberVariable,
  1.2023 +                                           nsTemplateQuerySet* aQuerySet)
  1.2024 +{
  1.2025 +    // Compile an "extended" <template> rule. An extended rule may have
  1.2026 +    // a <conditions> child, an <action> child, and a <bindings> child.
  1.2027 +    nsresult rv;
  1.2028 +
  1.2029 +    nsTemplateRule* rule = aQuerySet->NewRule(aRuleElement, aActionElement, aQuerySet);
  1.2030 +    if (! rule)
  1.2031 +         return NS_ERROR_OUT_OF_MEMORY;
  1.2032 +
  1.2033 +    nsCOMPtr<nsIContent> conditions;
  1.2034 +    nsXULContentUtils::FindChildByTag(aRuleElement,
  1.2035 +                                      kNameSpaceID_XUL,
  1.2036 +                                      nsGkAtoms::conditions,
  1.2037 +                                      getter_AddRefs(conditions));
  1.2038 +
  1.2039 +    // allow the conditions to be placed directly inside the rule
  1.2040 +    if (!conditions)
  1.2041 +        conditions = aRuleElement;
  1.2042 +  
  1.2043 +    rv = CompileConditions(rule, conditions);
  1.2044 +    // If the rule compilation failed, then we have to bail.
  1.2045 +    if (NS_FAILED(rv)) {
  1.2046 +        aQuerySet->RemoveRule(rule);
  1.2047 +        return rv;
  1.2048 +    }
  1.2049 +
  1.2050 +    rule->SetVars(mRefVariable, aMemberVariable);
  1.2051 +
  1.2052 +    // If we've got bindings, add 'em.
  1.2053 +    nsCOMPtr<nsIContent> bindings;
  1.2054 +    nsXULContentUtils::FindChildByTag(aRuleElement,
  1.2055 +                                      kNameSpaceID_XUL,
  1.2056 +                                      nsGkAtoms::bindings,
  1.2057 +                                      getter_AddRefs(bindings));
  1.2058 +
  1.2059 +    // allow bindings to be placed directly inside rule
  1.2060 +    if (!bindings)
  1.2061 +        bindings = aRuleElement;
  1.2062 +
  1.2063 +    rv = CompileBindings(rule, bindings);
  1.2064 +    NS_ENSURE_SUCCESS(rv, rv);
  1.2065 +
  1.2066 +    return NS_OK;
  1.2067 +}
  1.2068 +
  1.2069 +already_AddRefed<nsIAtom>
  1.2070 +nsXULTemplateBuilder::DetermineMemberVariable(nsIContent* aElement)
  1.2071 +{
  1.2072 +    // recursively iterate over the children looking for an element
  1.2073 +    // with uri="?..."
  1.2074 +    for (nsIContent* child = aElement->GetFirstChild();
  1.2075 +         child;
  1.2076 +         child = child->GetNextSibling()) {
  1.2077 +        nsAutoString uri;
  1.2078 +        child->GetAttr(kNameSpaceID_None, nsGkAtoms::uri, uri);
  1.2079 +        if (!uri.IsEmpty() && uri[0] == char16_t('?')) {
  1.2080 +            return NS_NewAtom(uri);
  1.2081 +        }
  1.2082 +
  1.2083 +        nsCOMPtr<nsIAtom> result = DetermineMemberVariable(child);
  1.2084 +        if (result) {
  1.2085 +            return result.forget();
  1.2086 +        }
  1.2087 +    }
  1.2088 +
  1.2089 +    return nullptr;
  1.2090 +}
  1.2091 +
  1.2092 +void
  1.2093 +nsXULTemplateBuilder::DetermineRDFQueryRef(nsIContent* aQueryElement, nsIAtom** aTag)
  1.2094 +{
  1.2095 +    // check for a tag
  1.2096 +    nsCOMPtr<nsIContent> content;
  1.2097 +    nsXULContentUtils::FindChildByTag(aQueryElement,
  1.2098 +                                      kNameSpaceID_XUL,
  1.2099 +                                      nsGkAtoms::content,
  1.2100 +                                      getter_AddRefs(content));
  1.2101 +
  1.2102 +    if (! content) {
  1.2103 +        // look for older treeitem syntax as well
  1.2104 +        nsXULContentUtils::FindChildByTag(aQueryElement,
  1.2105 +                                          kNameSpaceID_XUL,
  1.2106 +                                          nsGkAtoms::treeitem,
  1.2107 +                                          getter_AddRefs(content));
  1.2108 +    }
  1.2109 +
  1.2110 +    if (content) {
  1.2111 +        nsAutoString uri;
  1.2112 +        content->GetAttr(kNameSpaceID_None, nsGkAtoms::uri, uri);
  1.2113 +
  1.2114 +        if (!uri.IsEmpty())
  1.2115 +            mRefVariable = do_GetAtom(uri);
  1.2116 +
  1.2117 +        nsAutoString tag;
  1.2118 +        content->GetAttr(kNameSpaceID_None, nsGkAtoms::tag, tag);
  1.2119 +
  1.2120 +        if (!tag.IsEmpty())
  1.2121 +            *aTag = NS_NewAtom(tag).take();
  1.2122 +    }
  1.2123 +}
  1.2124 +
  1.2125 +nsresult
  1.2126 +nsXULTemplateBuilder::CompileSimpleQuery(nsIContent* aRuleElement,
  1.2127 +                                         nsTemplateQuerySet* aQuerySet,
  1.2128 +                                         bool* aCanUseTemplate)
  1.2129 +{
  1.2130 +    // compile a simple query, which is a query with no <query> or
  1.2131 +    // <conditions>. This means that a default query is used.
  1.2132 +    nsCOMPtr<nsIDOMNode> query(do_QueryInterface(aRuleElement));
  1.2133 +
  1.2134 +    nsCOMPtr<nsIAtom> memberVariable;
  1.2135 +    if (mMemberVariable)
  1.2136 +        memberVariable = mMemberVariable;
  1.2137 +    else
  1.2138 +        memberVariable = do_GetAtom("rdf:*");
  1.2139 +
  1.2140 +    // since there is no <query> node for a simple query, the query node will
  1.2141 +    // be either the <rule> node if multiple rules are used, or the <template> node.
  1.2142 +    aQuerySet->mQueryNode = aRuleElement;
  1.2143 +    nsresult rv = mQueryProcessor->CompileQuery(this, query,
  1.2144 +                                                mRefVariable, memberVariable,
  1.2145 +                                                getter_AddRefs(aQuerySet->mCompiledQuery));
  1.2146 +    if (NS_FAILED(rv))
  1.2147 +        return rv;
  1.2148 +
  1.2149 +    if (! aQuerySet->mCompiledQuery) {
  1.2150 +        *aCanUseTemplate = false;
  1.2151 +        return NS_OK;
  1.2152 +    }
  1.2153 +
  1.2154 +    nsTemplateRule* rule = aQuerySet->NewRule(aRuleElement, aRuleElement, aQuerySet);
  1.2155 +    if (! rule)
  1.2156 +        return NS_ERROR_OUT_OF_MEMORY;
  1.2157 +
  1.2158 +    rule->SetVars(mRefVariable, memberVariable);
  1.2159 +
  1.2160 +    nsAutoString tag;
  1.2161 +    aRuleElement->GetAttr(kNameSpaceID_None, nsGkAtoms::parent, tag);
  1.2162 +
  1.2163 +    if (!tag.IsEmpty()) {
  1.2164 +        nsCOMPtr<nsIAtom> tagatom = do_GetAtom(tag);
  1.2165 +        aQuerySet->SetTag(tagatom);
  1.2166 +    }
  1.2167 +
  1.2168 +    *aCanUseTemplate = true;
  1.2169 +
  1.2170 +    return AddSimpleRuleBindings(rule, aRuleElement);
  1.2171 +}
  1.2172 +
  1.2173 +nsresult
  1.2174 +nsXULTemplateBuilder::CompileConditions(nsTemplateRule* aRule,
  1.2175 +                                        nsIContent* aCondition)
  1.2176 +{
  1.2177 +    nsAutoString tag;
  1.2178 +    aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::parent, tag);
  1.2179 +
  1.2180 +    if (!tag.IsEmpty()) {
  1.2181 +        nsCOMPtr<nsIAtom> tagatom = do_GetAtom(tag);
  1.2182 +        aRule->SetTag(tagatom);
  1.2183 +    }
  1.2184 +
  1.2185 +    nsTemplateCondition* currentCondition = nullptr;
  1.2186 +
  1.2187 +    for (nsIContent* node = aCondition->GetFirstChild();
  1.2188 +         node;
  1.2189 +         node = node->GetNextSibling()) {
  1.2190 +
  1.2191 +        if (node->NodeInfo()->Equals(nsGkAtoms::where, kNameSpaceID_XUL)) {
  1.2192 +            nsresult rv = CompileWhereCondition(aRule, node, &currentCondition);
  1.2193 +            if (NS_FAILED(rv))
  1.2194 +                return rv;
  1.2195 +        }
  1.2196 +    }
  1.2197 +
  1.2198 +    return NS_OK;
  1.2199 +}
  1.2200 +
  1.2201 +nsresult
  1.2202 +nsXULTemplateBuilder::CompileWhereCondition(nsTemplateRule* aRule,
  1.2203 +                                            nsIContent* aCondition,
  1.2204 +                                            nsTemplateCondition** aCurrentCondition)
  1.2205 +{
  1.2206 +    // Compile a <where> condition, which must be of the form:
  1.2207 +    //
  1.2208 +    //   <where subject="?var1|string" rel="relation" value="?var2|string" />
  1.2209 +    //
  1.2210 +    //    The value of rel may be:
  1.2211 +    //      equal - subject must be equal to object
  1.2212 +    //      notequal - subject must not be equal to object
  1.2213 +    //      less - subject must be less than object
  1.2214 +    //      greater - subject must be greater than object
  1.2215 +    //      startswith - subject must start with object
  1.2216 +    //      endswith - subject must end with object
  1.2217 +    //      contains - subject must contain object
  1.2218 +    //    Comparisons are done as strings unless the subject is an integer.
  1.2219 +
  1.2220 +    // subject
  1.2221 +    nsAutoString subject;
  1.2222 +    aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::subject, subject);
  1.2223 +    if (subject.IsEmpty()) {
  1.2224 +        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_WHERE_NO_SUBJECT);
  1.2225 +        return NS_OK;
  1.2226 +    }
  1.2227 +
  1.2228 +    nsCOMPtr<nsIAtom> svar;
  1.2229 +    if (subject[0] == char16_t('?'))
  1.2230 +        svar = do_GetAtom(subject);
  1.2231 +
  1.2232 +    nsAutoString relstring;
  1.2233 +    aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, relstring);
  1.2234 +    if (relstring.IsEmpty()) {
  1.2235 +        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_WHERE_NO_RELATION);
  1.2236 +        return NS_OK;
  1.2237 +    }
  1.2238 +
  1.2239 +    // object
  1.2240 +    nsAutoString value;
  1.2241 +    aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::value, value);
  1.2242 +    if (value.IsEmpty()) {
  1.2243 +        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_WHERE_NO_VALUE);
  1.2244 +        return NS_OK;
  1.2245 +    }
  1.2246 +
  1.2247 +    // multiple
  1.2248 +    bool shouldMultiple =
  1.2249 +      aCondition->AttrValueIs(kNameSpaceID_None, nsGkAtoms::multiple,
  1.2250 +                              nsGkAtoms::_true, eCaseMatters);
  1.2251 +
  1.2252 +    nsCOMPtr<nsIAtom> vvar;
  1.2253 +    if (!shouldMultiple && (value[0] == char16_t('?'))) {
  1.2254 +        vvar = do_GetAtom(value);
  1.2255 +    }
  1.2256 +
  1.2257 +    // ignorecase
  1.2258 +    bool shouldIgnoreCase =
  1.2259 +      aCondition->AttrValueIs(kNameSpaceID_None, nsGkAtoms::ignorecase,
  1.2260 +                              nsGkAtoms::_true, eCaseMatters);
  1.2261 +
  1.2262 +    // negate
  1.2263 +    bool shouldNegate =
  1.2264 +      aCondition->AttrValueIs(kNameSpaceID_None, nsGkAtoms::negate,
  1.2265 +                              nsGkAtoms::_true, eCaseMatters);
  1.2266 +
  1.2267 +    nsTemplateCondition* condition;
  1.2268 +
  1.2269 +    if (svar && vvar) {
  1.2270 +        condition = new nsTemplateCondition(svar, relstring, vvar,
  1.2271 +                                            shouldIgnoreCase, shouldNegate);
  1.2272 +    }
  1.2273 +    else if (svar && !value.IsEmpty()) {
  1.2274 +        condition = new nsTemplateCondition(svar, relstring, value,
  1.2275 +                                            shouldIgnoreCase, shouldNegate, shouldMultiple);
  1.2276 +    }
  1.2277 +    else if (vvar) {
  1.2278 +        condition = new nsTemplateCondition(subject, relstring, vvar,
  1.2279 +                                            shouldIgnoreCase, shouldNegate);
  1.2280 +    }
  1.2281 +    else {
  1.2282 +        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_WHERE_NO_VAR);
  1.2283 +        return NS_OK;
  1.2284 +    }
  1.2285 +
  1.2286 +    if (! condition)
  1.2287 +        return NS_ERROR_OUT_OF_MEMORY;
  1.2288 +
  1.2289 +    if (*aCurrentCondition) {
  1.2290 +        (*aCurrentCondition)->SetNext(condition);
  1.2291 +    }
  1.2292 +    else {
  1.2293 +        aRule->SetCondition(condition);
  1.2294 +    }
  1.2295 +
  1.2296 +    *aCurrentCondition = condition;
  1.2297 +
  1.2298 +    return NS_OK;
  1.2299 +}
  1.2300 +
  1.2301 +nsresult
  1.2302 +nsXULTemplateBuilder::CompileBindings(nsTemplateRule* aRule, nsIContent* aBindings)
  1.2303 +{
  1.2304 +    // Add an extended rule's bindings.
  1.2305 +    nsresult rv;
  1.2306 +
  1.2307 +    for (nsIContent* binding = aBindings->GetFirstChild();
  1.2308 +         binding;
  1.2309 +         binding = binding->GetNextSibling()) {
  1.2310 +
  1.2311 +        if (binding->NodeInfo()->Equals(nsGkAtoms::binding,
  1.2312 +                                        kNameSpaceID_XUL)) {
  1.2313 +            rv = CompileBinding(aRule, binding);
  1.2314 +            if (NS_FAILED(rv))
  1.2315 +                return rv;
  1.2316 +        }
  1.2317 +    }
  1.2318 +
  1.2319 +    aRule->AddBindingsToQueryProcessor(mQueryProcessor);
  1.2320 +
  1.2321 +    return NS_OK;
  1.2322 +}
  1.2323 +
  1.2324 +
  1.2325 +nsresult
  1.2326 +nsXULTemplateBuilder::CompileBinding(nsTemplateRule* aRule,
  1.2327 +                                     nsIContent* aBinding)
  1.2328 +{
  1.2329 +    // Compile a <binding> "condition", which must be of the form:
  1.2330 +    //
  1.2331 +    //   <binding subject="?var1"
  1.2332 +    //            predicate="resource"
  1.2333 +    //            object="?var2" />
  1.2334 +    //
  1.2335 +    // XXXwaterson Some day it would be cool to allow the 'predicate'
  1.2336 +    // to be bound to a variable.
  1.2337 +
  1.2338 +    // subject
  1.2339 +    nsAutoString subject;
  1.2340 +    aBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::subject, subject);
  1.2341 +    if (subject.IsEmpty()) {
  1.2342 +        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_SUBJECT);
  1.2343 +        return NS_OK;
  1.2344 +    }
  1.2345 +
  1.2346 +    nsCOMPtr<nsIAtom> svar;
  1.2347 +    if (subject[0] == char16_t('?')) {
  1.2348 +        svar = do_GetAtom(subject);
  1.2349 +    }
  1.2350 +    else {
  1.2351 +        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_SUBJECT);
  1.2352 +        return NS_OK;
  1.2353 +    }
  1.2354 +
  1.2355 +    // predicate
  1.2356 +    nsAutoString predicate;
  1.2357 +    aBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::predicate, predicate);
  1.2358 +    if (predicate.IsEmpty()) {
  1.2359 +        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_PREDICATE);
  1.2360 +        return NS_OK;
  1.2361 +    }
  1.2362 +
  1.2363 +    // object
  1.2364 +    nsAutoString object;
  1.2365 +    aBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::object, object);
  1.2366 +
  1.2367 +    if (object.IsEmpty()) {
  1.2368 +        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_OBJECT);
  1.2369 +        return NS_OK;
  1.2370 +    }
  1.2371 +
  1.2372 +    nsCOMPtr<nsIAtom> ovar;
  1.2373 +    if (object[0] == char16_t('?')) {
  1.2374 +        ovar = do_GetAtom(object);
  1.2375 +    }
  1.2376 +    else {
  1.2377 +        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_OBJECT);
  1.2378 +        return NS_OK;
  1.2379 +    }
  1.2380 +
  1.2381 +    return aRule->AddBinding(svar, predicate, ovar);
  1.2382 +}
  1.2383 +
  1.2384 +nsresult
  1.2385 +nsXULTemplateBuilder::AddSimpleRuleBindings(nsTemplateRule* aRule,
  1.2386 +                                            nsIContent* aElement)
  1.2387 +{
  1.2388 +    // Crawl the content tree of a "simple" rule, adding a variable
  1.2389 +    // assignment for any attribute whose value is "rdf:".
  1.2390 +
  1.2391 +    nsAutoTArray<nsIContent*, 8> elements;
  1.2392 +
  1.2393 +    if (elements.AppendElement(aElement) == nullptr)
  1.2394 +        return NS_ERROR_OUT_OF_MEMORY;
  1.2395 +
  1.2396 +    while (elements.Length()) {
  1.2397 +        // Pop the next element off the stack
  1.2398 +        uint32_t i = elements.Length() - 1;
  1.2399 +        nsIContent* element = elements[i];
  1.2400 +        elements.RemoveElementAt(i);
  1.2401 +
  1.2402 +        // Iterate through its attributes, looking for substitutions
  1.2403 +        // that we need to add as bindings.
  1.2404 +        uint32_t count = element->GetAttrCount();
  1.2405 +
  1.2406 +        for (i = 0; i < count; ++i) {
  1.2407 +            const nsAttrName* name = element->GetAttrNameAt(i);
  1.2408 +
  1.2409 +            if (!name->Equals(nsGkAtoms::id, kNameSpaceID_None) &&
  1.2410 +                !name->Equals(nsGkAtoms::uri, kNameSpaceID_None)) {
  1.2411 +                nsAutoString value;
  1.2412 +                element->GetAttr(name->NamespaceID(), name->LocalName(), value);
  1.2413 +
  1.2414 +                // Scan the attribute for variables, adding a binding for
  1.2415 +                // each one.
  1.2416 +                ParseAttribute(value, AddBindingsFor, nullptr, aRule);
  1.2417 +            }
  1.2418 +        }
  1.2419 +
  1.2420 +        // Push kids onto the stack, and search them next.
  1.2421 +        for (nsIContent* child = element->GetLastChild();
  1.2422 +             child;
  1.2423 +             child = child->GetPreviousSibling()) {
  1.2424 +
  1.2425 +            if (!elements.AppendElement(child))
  1.2426 +                return NS_ERROR_OUT_OF_MEMORY;
  1.2427 +        }
  1.2428 +    }
  1.2429 +
  1.2430 +    aRule->AddBindingsToQueryProcessor(mQueryProcessor);
  1.2431 +
  1.2432 +    return NS_OK;
  1.2433 +}
  1.2434 +
  1.2435 +void
  1.2436 +nsXULTemplateBuilder::AddBindingsFor(nsXULTemplateBuilder* aThis,
  1.2437 +                                     const nsAString& aVariable,
  1.2438 +                                     void* aClosure)
  1.2439 +{
  1.2440 +    // We should *only* be recieving "rdf:"-style variables. Make
  1.2441 +    // sure...
  1.2442 +    if (!StringBeginsWith(aVariable, NS_LITERAL_STRING("rdf:")))
  1.2443 +        return;
  1.2444 +
  1.2445 +    nsTemplateRule* rule = static_cast<nsTemplateRule*>(aClosure);
  1.2446 +
  1.2447 +    nsCOMPtr<nsIAtom> var = do_GetAtom(aVariable);
  1.2448 +
  1.2449 +    // Strip it down to the raw RDF property by clobbering the "rdf:"
  1.2450 +    // prefix
  1.2451 +    nsAutoString property;
  1.2452 +    property.Assign(Substring(aVariable, uint32_t(4), aVariable.Length() - 4));
  1.2453 +
  1.2454 +    if (! rule->HasBinding(rule->GetMemberVariable(), property, var))
  1.2455 +        // In the simple syntax, the binding is always from the
  1.2456 +        // member variable, through the property, to the target.
  1.2457 +        rule->AddBinding(rule->GetMemberVariable(), property, var);
  1.2458 +}
  1.2459 +
  1.2460 +
  1.2461 +nsresult
  1.2462 +nsXULTemplateBuilder::IsSystemPrincipal(nsIPrincipal *principal, bool *result)
  1.2463 +{
  1.2464 +  if (!gSystemPrincipal)
  1.2465 +    return NS_ERROR_UNEXPECTED;
  1.2466 +
  1.2467 +  *result = (principal == gSystemPrincipal);
  1.2468 +  return NS_OK;
  1.2469 +}
  1.2470 +
  1.2471 +bool
  1.2472 +nsXULTemplateBuilder::IsActivated(nsIRDFResource *aResource)
  1.2473 +{
  1.2474 +    for (ActivationEntry *entry = mTop;
  1.2475 +         entry != nullptr;
  1.2476 +         entry = entry->mPrevious) {
  1.2477 +        if (entry->mResource == aResource)
  1.2478 +            return true;
  1.2479 +    }
  1.2480 +    return false;
  1.2481 +}
  1.2482 +
  1.2483 +nsresult
  1.2484 +nsXULTemplateBuilder::GetResultResource(nsIXULTemplateResult* aResult,
  1.2485 +                                        nsIRDFResource** aResource)
  1.2486 +{
  1.2487 +    // get the resource for a result by checking its resource property. If it
  1.2488 +    // is not set, check the id. This allows non-chrome implementations to
  1.2489 +    // avoid having to use RDF.
  1.2490 +    nsresult rv = aResult->GetResource(aResource);
  1.2491 +    if (NS_FAILED(rv))
  1.2492 +        return rv;
  1.2493 +
  1.2494 +    if (! *aResource) {
  1.2495 +        nsAutoString id;
  1.2496 +        rv = aResult->GetId(id);
  1.2497 +        if (NS_FAILED(rv))
  1.2498 +            return rv;
  1.2499 +
  1.2500 +        return gRDFService->GetUnicodeResource(id, aResource);
  1.2501 +    }
  1.2502 +
  1.2503 +    return rv;
  1.2504 +}
  1.2505 +
  1.2506 +
  1.2507 +void
  1.2508 +nsXULTemplateBuilder::OutputMatchToLog(nsIRDFResource* aId,
  1.2509 +                                       nsTemplateMatch* aMatch,
  1.2510 +                                       bool aIsNew)
  1.2511 +{
  1.2512 +    int32_t priority = aMatch->QuerySetPriority() + 1;
  1.2513 +    int32_t activePriority = -1;
  1.2514 +
  1.2515 +    nsAutoString msg;
  1.2516 +
  1.2517 +    nsAutoString templateid;
  1.2518 +    mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::id, templateid);
  1.2519 +    msg.AppendLiteral("In template");
  1.2520 +    if (!templateid.IsEmpty()) {
  1.2521 +        msg.AppendLiteral(" with id ");
  1.2522 +        msg.Append(templateid);
  1.2523 +    }
  1.2524 +
  1.2525 +    nsAutoString refstring;
  1.2526 +    aMatch->mResult->GetBindingFor(mRefVariable, refstring);
  1.2527 +    if (!refstring.IsEmpty()) {
  1.2528 +        msg.AppendLiteral(" using ref ");
  1.2529 +        msg.Append(refstring);
  1.2530 +    }
  1.2531 +
  1.2532 +    msg.AppendLiteral("\n    ");
  1.2533 +
  1.2534 +    nsTemplateMatch* match = nullptr;
  1.2535 +    if (mMatchMap.Get(aId, &match)){
  1.2536 +        while (match) {
  1.2537 +            if (match == aMatch)
  1.2538 +                break;
  1.2539 +            if (match->IsActive() &&
  1.2540 +                match->GetContainer() == aMatch->GetContainer()) {
  1.2541 +                activePriority = match->QuerySetPriority() + 1;
  1.2542 +                break;
  1.2543 +            }
  1.2544 +            match = match->mNext;
  1.2545 +        }
  1.2546 +    }
  1.2547 +
  1.2548 +    if (aMatch->IsActive()) {
  1.2549 +        if (aIsNew) {
  1.2550 +            msg.AppendLiteral("New active result for query ");
  1.2551 +            msg.AppendInt(priority);
  1.2552 +            msg.AppendLiteral(" matching rule ");
  1.2553 +            msg.AppendInt(aMatch->RuleIndex() + 1);
  1.2554 +        }
  1.2555 +        else {
  1.2556 +            msg.AppendLiteral("Removed active result for query ");
  1.2557 +            msg.AppendInt(priority);
  1.2558 +            if (activePriority > 0) {
  1.2559 +                msg.AppendLiteral(" (new active query is ");
  1.2560 +                msg.AppendInt(activePriority);
  1.2561 +                msg.Append(')');
  1.2562 +            }
  1.2563 +            else {
  1.2564 +                msg.AppendLiteral(" (no new active query)");
  1.2565 +            }
  1.2566 +        }
  1.2567 +    }
  1.2568 +    else {
  1.2569 +        if (aIsNew) {
  1.2570 +            msg.AppendLiteral("New inactive result for query ");
  1.2571 +            msg.AppendInt(priority);
  1.2572 +            if (activePriority > 0) {
  1.2573 +                msg.AppendLiteral(" (overridden by query ");
  1.2574 +                msg.AppendInt(activePriority);
  1.2575 +                msg.Append(')');
  1.2576 +            }
  1.2577 +            else {
  1.2578 +                msg.AppendLiteral(" (didn't match a rule)");
  1.2579 +            }
  1.2580 +        }
  1.2581 +        else {
  1.2582 +            msg.AppendLiteral("Removed inactive result for query ");
  1.2583 +            msg.AppendInt(priority);
  1.2584 +            if (activePriority > 0) {
  1.2585 +                msg.AppendLiteral(" (active query is ");
  1.2586 +                msg.AppendInt(activePriority);
  1.2587 +                msg.Append(')');
  1.2588 +            }
  1.2589 +            else {
  1.2590 +                msg.AppendLiteral(" (no active query)");
  1.2591 +            }
  1.2592 +        }
  1.2593 +    }
  1.2594 +
  1.2595 +    nsAutoString idstring;
  1.2596 +    nsXULContentUtils::GetTextForNode(aId, idstring);
  1.2597 +    msg.AppendLiteral(": ");
  1.2598 +    msg.Append(idstring);
  1.2599 +
  1.2600 +    nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
  1.2601 +    if (cs)
  1.2602 +      cs->LogStringMessage(msg.get());
  1.2603 +}

mercurial