content/xul/templates/src/nsXULTemplateQueryProcessorRDF.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/xul/templates/src/nsXULTemplateQueryProcessorRDF.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1896 @@
     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 +#include "nsCOMPtr.h"
    1.10 +#include "nsICollation.h"
    1.11 +#include "nsIDOMNode.h"
    1.12 +#include "nsIRDFNode.h"
    1.13 +#include "nsIRDFObserver.h"
    1.14 +#include "nsIRDFRemoteDataSource.h"
    1.15 +#include "nsIRDFInferDataSource.h"
    1.16 +#include "nsIRDFService.h"
    1.17 +#include "nsRDFCID.h"
    1.18 +#include "nsIServiceManager.h"
    1.19 +#include "nsNameSpaceManager.h"
    1.20 +#include "nsGkAtoms.h"
    1.21 +#include "nsIDOMDocument.h"
    1.22 +#include "nsAttrName.h"
    1.23 +#include "rdf.h"
    1.24 +#include "nsArrayUtils.h"
    1.25 +#include "nsIURI.h"
    1.26 +
    1.27 +#include "nsContentTestNode.h"
    1.28 +#include "nsRDFConInstanceTestNode.h"
    1.29 +#include "nsRDFConMemberTestNode.h"
    1.30 +#include "nsRDFPropertyTestNode.h"
    1.31 +#include "nsInstantiationNode.h"
    1.32 +#include "nsRDFTestNode.h"
    1.33 +#include "nsXULContentUtils.h"
    1.34 +#include "nsXULTemplateBuilder.h"
    1.35 +#include "nsXULTemplateResultRDF.h"
    1.36 +#include "nsXULTemplateResultSetRDF.h"
    1.37 +#include "nsXULTemplateQueryProcessorRDF.h"
    1.38 +#include "nsXULSortService.h"
    1.39 +#include "nsIDocument.h"
    1.40 +
    1.41 +//----------------------------------------------------------------------
    1.42 +
    1.43 +#define PARSE_TYPE_INTEGER  "Integer"
    1.44 +
    1.45 +nsrefcnt                  nsXULTemplateQueryProcessorRDF::gRefCnt = 0;
    1.46 +nsIRDFService*            nsXULTemplateQueryProcessorRDF::gRDFService;
    1.47 +nsIRDFContainerUtils*     nsXULTemplateQueryProcessorRDF::gRDFContainerUtils;
    1.48 +nsIRDFResource*           nsXULTemplateQueryProcessorRDF::kNC_BookmarkSeparator;
    1.49 +nsIRDFResource*           nsXULTemplateQueryProcessorRDF::kRDF_type;
    1.50 +
    1.51 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULTemplateQueryProcessorRDF)
    1.52 +
    1.53 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULTemplateQueryProcessorRDF)
    1.54 +    tmp->Done();
    1.55 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    1.56 +
    1.57 +static PLDHashOperator
    1.58 +BindingDependenciesTraverser(nsISupports* key,
    1.59 +                             nsXULTemplateQueryProcessorRDF::ResultArray* array,
    1.60 +                             void* userArg)
    1.61 +{
    1.62 +    nsCycleCollectionTraversalCallback *cb = 
    1.63 +        static_cast<nsCycleCollectionTraversalCallback*>(userArg);
    1.64 +
    1.65 +    int32_t i, count = array->Length();
    1.66 +    for (i = 0; i < count; ++i) {
    1.67 +        cb->NoteXPCOMChild(array->ElementAt(i));
    1.68 +    }
    1.69 +
    1.70 +    return PL_DHASH_NEXT;
    1.71 +}
    1.72 +
    1.73 +static PLDHashOperator
    1.74 +MemoryElementTraverser(const uint32_t& key,
    1.75 +                       nsCOMArray<nsXULTemplateResultRDF>* array,
    1.76 +                       void* userArg)
    1.77 +{
    1.78 +    nsCycleCollectionTraversalCallback *cb = 
    1.79 +        static_cast<nsCycleCollectionTraversalCallback*>(userArg);
    1.80 +
    1.81 +    int32_t i, count = array->Count();
    1.82 +    for (i = 0; i < count; ++i) {
    1.83 +        cb->NoteXPCOMChild(array->ObjectAt(i));
    1.84 +    }
    1.85 +
    1.86 +    return PL_DHASH_NEXT;
    1.87 +}
    1.88 +
    1.89 +static PLDHashOperator
    1.90 +RuleToBindingTraverser(nsISupports* key, RDFBindingSet* binding, void* userArg)
    1.91 +{
    1.92 +    nsCycleCollectionTraversalCallback *cb = 
    1.93 +        static_cast<nsCycleCollectionTraversalCallback*>(userArg);
    1.94 +
    1.95 +    cb->NoteXPCOMChild(key);
    1.96 +
    1.97 +    return PL_DHASH_NEXT;
    1.98 +}
    1.99 +
   1.100 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULTemplateQueryProcessorRDF)
   1.101 +    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDB)
   1.102 +    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLastRef)
   1.103 +    tmp->mBindingDependencies.EnumerateRead(BindingDependenciesTraverser,
   1.104 +                                            &cb);
   1.105 +    tmp->mMemoryElementToResultMap.EnumerateRead(MemoryElementTraverser,
   1.106 +                                                 &cb);
   1.107 +    tmp->mRuleToBindingsMap.EnumerateRead(RuleToBindingTraverser, &cb);
   1.108 +    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueries)
   1.109 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   1.110 +
   1.111 +NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULTemplateQueryProcessorRDF)
   1.112 +NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULTemplateQueryProcessorRDF)
   1.113 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULTemplateQueryProcessorRDF)
   1.114 +    NS_INTERFACE_MAP_ENTRY(nsIXULTemplateQueryProcessor)
   1.115 +    NS_INTERFACE_MAP_ENTRY(nsIRDFObserver)
   1.116 +    NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULTemplateQueryProcessor)
   1.117 +NS_INTERFACE_MAP_END
   1.118 +
   1.119 +nsXULTemplateQueryProcessorRDF::nsXULTemplateQueryProcessorRDF(void)
   1.120 +    : mDB(nullptr),
   1.121 +      mBuilder(nullptr),
   1.122 +      mQueryProcessorRDFInited(false),
   1.123 +      mGenerationStarted(false),
   1.124 +      mUpdateBatchNest(0),
   1.125 +      mSimpleRuleMemberTest(nullptr)
   1.126 +{
   1.127 +    gRefCnt++;
   1.128 +}
   1.129 +
   1.130 +nsXULTemplateQueryProcessorRDF::~nsXULTemplateQueryProcessorRDF(void)
   1.131 +{
   1.132 +    if (--gRefCnt == 0) {
   1.133 +        NS_IF_RELEASE(gRDFService);
   1.134 +        NS_IF_RELEASE(gRDFContainerUtils);
   1.135 +        NS_IF_RELEASE(kNC_BookmarkSeparator);
   1.136 +        NS_IF_RELEASE(kRDF_type);
   1.137 +    }
   1.138 +}
   1.139 +
   1.140 +nsresult
   1.141 +nsXULTemplateQueryProcessorRDF::InitGlobals()
   1.142 +{
   1.143 +    nsresult rv;
   1.144 +
   1.145 +    // Initialize the global shared reference to the service
   1.146 +    // manager and get some shared resource objects.
   1.147 +    if (!gRDFService) {
   1.148 +        NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
   1.149 +        rv = CallGetService(kRDFServiceCID, &gRDFService);
   1.150 +        if (NS_FAILED(rv))
   1.151 +            return rv;
   1.152 +    }
   1.153 +
   1.154 +    if (!gRDFContainerUtils) {
   1.155 +        NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
   1.156 +        rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils);
   1.157 +        if (NS_FAILED(rv))
   1.158 +            return rv;
   1.159 +    }
   1.160 +  
   1.161 +    if (!kNC_BookmarkSeparator) {
   1.162 +        gRDFService->GetResource(
   1.163 +          NS_LITERAL_CSTRING(NC_NAMESPACE_URI "BookmarkSeparator"),
   1.164 +                             &kNC_BookmarkSeparator);
   1.165 +    }
   1.166 +
   1.167 +    if (!kRDF_type) {
   1.168 +        gRDFService->GetResource(
   1.169 +          NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "type"),
   1.170 +                             &kRDF_type);
   1.171 +    }
   1.172 +
   1.173 +    return NS_OK;
   1.174 +}
   1.175 +
   1.176 +//----------------------------------------------------------------------
   1.177 +//
   1.178 +// nsIXULTemplateQueryProcessor interface
   1.179 +//
   1.180 +
   1.181 +NS_IMETHODIMP
   1.182 +nsXULTemplateQueryProcessorRDF::GetDatasource(nsIArray* aDataSources,
   1.183 +                                              nsIDOMNode* aRootNode,
   1.184 +                                              bool aIsTrusted,
   1.185 +                                              nsIXULTemplateBuilder* aBuilder,
   1.186 +                                              bool* aShouldDelayBuilding,
   1.187 +                                              nsISupports** aResult)
   1.188 +{
   1.189 +    nsCOMPtr<nsIRDFCompositeDataSource> compDB;
   1.190 +    nsCOMPtr<nsIContent> root = do_QueryInterface(aRootNode);
   1.191 +    nsresult rv;
   1.192 +
   1.193 +    *aResult = nullptr;
   1.194 +    *aShouldDelayBuilding = false;
   1.195 +
   1.196 +    NS_ENSURE_TRUE(root, NS_ERROR_UNEXPECTED);
   1.197 +
   1.198 +    // make sure the RDF service is set up
   1.199 +    rv = InitGlobals();
   1.200 +    NS_ENSURE_SUCCESS(rv, rv);
   1.201 +
   1.202 +    // create a database for the builder
   1.203 +    compDB = do_CreateInstance(NS_RDF_DATASOURCE_CONTRACTID_PREFIX 
   1.204 +                               "composite-datasource");
   1.205 +    if (!compDB) {
   1.206 +        NS_ERROR("unable to construct new composite data source");
   1.207 +        return NS_ERROR_UNEXPECTED;
   1.208 +    }
   1.209 +
   1.210 +    // check for magical attributes. XXX move to ``flags''?
   1.211 +    if (root->AttrValueIs(kNameSpaceID_None,
   1.212 +                          nsGkAtoms::coalesceduplicatearcs,
   1.213 +                          nsGkAtoms::_false, eCaseMatters))
   1.214 +        compDB->SetCoalesceDuplicateArcs(false);
   1.215 +
   1.216 +    if (root->AttrValueIs(kNameSpaceID_None,
   1.217 +                          nsGkAtoms::allownegativeassertions,
   1.218 +                          nsGkAtoms::_false, eCaseMatters))
   1.219 +        compDB->SetAllowNegativeAssertions(false);
   1.220 +
   1.221 +    if (aIsTrusted) {
   1.222 +        // If we're a privileged (e.g., chrome) document, then add the
   1.223 +        // local store as the first data source in the db. Note that
   1.224 +        // we _might_ not be able to get a local store if we haven't
   1.225 +        // got a profile to read from yet.
   1.226 +        nsCOMPtr<nsIRDFDataSource> localstore;
   1.227 +        rv = gRDFService->GetDataSource("rdf:local-store",
   1.228 +                                        getter_AddRefs(localstore));
   1.229 +        NS_ENSURE_SUCCESS(rv, rv);
   1.230 +
   1.231 +        rv = compDB->AddDataSource(localstore);
   1.232 +        NS_ASSERTION(NS_SUCCEEDED(rv), "unable to add local store to db");
   1.233 +        NS_ENSURE_SUCCESS(rv, rv);
   1.234 +    }
   1.235 +
   1.236 +    uint32_t length, index;
   1.237 +    rv = aDataSources->GetLength(&length);
   1.238 +    NS_ENSURE_SUCCESS(rv,rv);
   1.239 +
   1.240 +    for (index = 0; index < length; index++) {
   1.241 +
   1.242 +        nsCOMPtr<nsIURI> uri = do_QueryElementAt(aDataSources, index);
   1.243 +        if (!uri) // we ignore other datasources than uri
   1.244 +            continue;
   1.245 +
   1.246 +        nsCOMPtr<nsIRDFDataSource> ds;
   1.247 +        nsAutoCString uristrC;
   1.248 +        uri->GetSpec(uristrC);
   1.249 +
   1.250 +        rv = gRDFService->GetDataSource(uristrC.get(), getter_AddRefs(ds));
   1.251 +
   1.252 +        if (NS_FAILED(rv)) {
   1.253 +            // This is only a warning because the data source may not
   1.254 +            // be accessible for any number of reasons, including
   1.255 +            // security, a bad URL, etc.
   1.256 +  #ifdef DEBUG
   1.257 +            nsAutoCString msg;
   1.258 +            msg.Append("unable to load datasource '");
   1.259 +            msg.Append(uristrC);
   1.260 +            msg.Append('\'');
   1.261 +            NS_WARNING(msg.get());
   1.262 +  #endif
   1.263 +            continue;
   1.264 +        }
   1.265 +
   1.266 +        compDB->AddDataSource(ds);
   1.267 +    }
   1.268 +
   1.269 +
   1.270 +    // check if we were given an inference engine type
   1.271 +    nsAutoString infer;
   1.272 +    nsCOMPtr<nsIRDFDataSource> db;
   1.273 +    root->GetAttr(kNameSpaceID_None, nsGkAtoms::infer, infer);
   1.274 +    if (!infer.IsEmpty()) {
   1.275 +        nsCString inferCID(NS_RDF_INFER_DATASOURCE_CONTRACTID_PREFIX);
   1.276 +        AppendUTF16toUTF8(infer, inferCID);
   1.277 +        nsCOMPtr<nsIRDFInferDataSource> inferDB =
   1.278 +            do_CreateInstance(inferCID.get());
   1.279 +
   1.280 +        if (inferDB) {
   1.281 +            inferDB->SetBaseDataSource(compDB);
   1.282 +            db = do_QueryInterface(inferDB);
   1.283 +        }
   1.284 +        else {
   1.285 +            NS_WARNING("failed to construct inference engine specified on template");
   1.286 +        }
   1.287 +    }
   1.288 +
   1.289 +    if (!db)
   1.290 +        db = compDB;
   1.291 +
   1.292 +    return CallQueryInterface(db, aResult);
   1.293 +}
   1.294 +
   1.295 +NS_IMETHODIMP
   1.296 +nsXULTemplateQueryProcessorRDF::InitializeForBuilding(nsISupports* aDatasource,
   1.297 +                                                      nsIXULTemplateBuilder* aBuilder,
   1.298 +                                                      nsIDOMNode* aRootNode)
   1.299 +{
   1.300 +    if (!mQueryProcessorRDFInited) {
   1.301 +        nsresult rv = InitGlobals();
   1.302 +        if (NS_FAILED(rv))
   1.303 +            return rv;
   1.304 +
   1.305 +        mQueryProcessorRDFInited = true;
   1.306 +    }
   1.307 +
   1.308 +    // don't do anything if generation has already been done
   1.309 +    if (mGenerationStarted)
   1.310 +        return NS_ERROR_UNEXPECTED;
   1.311 +
   1.312 +    mDB = do_QueryInterface(aDatasource);
   1.313 +    mBuilder = aBuilder;
   1.314 +
   1.315 +    ComputeContainmentProperties(aRootNode);
   1.316 +
   1.317 +    // Add ourselves as a datasource observer
   1.318 +    if (mDB)
   1.319 +        mDB->AddObserver(this);
   1.320 +
   1.321 +    return NS_OK;
   1.322 +}
   1.323 +
   1.324 +NS_IMETHODIMP
   1.325 +nsXULTemplateQueryProcessorRDF::Done()
   1.326 +{
   1.327 +    if (!mQueryProcessorRDFInited)
   1.328 +        return NS_OK;
   1.329 +
   1.330 +    if (mDB)
   1.331 +        mDB->RemoveObserver(this);
   1.332 +
   1.333 +    mDB = nullptr;
   1.334 +    mBuilder = nullptr;
   1.335 +    mRefVariable = nullptr;
   1.336 +    mLastRef = nullptr;
   1.337 +
   1.338 +    mGenerationStarted = false;
   1.339 +    mUpdateBatchNest = 0;
   1.340 +
   1.341 +    mContainmentProperties.Clear();
   1.342 +
   1.343 +    for (ReteNodeSet::Iterator node = mAllTests.First();
   1.344 +         node != mAllTests.Last(); ++node)
   1.345 +        delete *node;
   1.346 +
   1.347 +    mAllTests.Clear();
   1.348 +    mRDFTests.Clear();
   1.349 +    mQueries.Clear();
   1.350 +
   1.351 +    mSimpleRuleMemberTest = nullptr;
   1.352 +
   1.353 +    mBindingDependencies.Clear();
   1.354 +
   1.355 +    mMemoryElementToResultMap.Clear();
   1.356 +
   1.357 +    mRuleToBindingsMap.Clear();
   1.358 +
   1.359 +    return NS_OK;
   1.360 +}
   1.361 +
   1.362 +NS_IMETHODIMP
   1.363 +nsXULTemplateQueryProcessorRDF::CompileQuery(nsIXULTemplateBuilder* aBuilder,
   1.364 +                                             nsIDOMNode* aQueryNode,
   1.365 +                                             nsIAtom* aRefVariable,
   1.366 +                                             nsIAtom* aMemberVariable,
   1.367 +                                             nsISupports** _retval)
   1.368 +{
   1.369 +    nsRefPtr<nsRDFQuery> query = new nsRDFQuery(this);
   1.370 +    if (!query)
   1.371 +        return NS_ERROR_OUT_OF_MEMORY;
   1.372 +
   1.373 +    query->mRefVariable = aRefVariable;
   1.374 +    if (!mRefVariable)
   1.375 +      mRefVariable = aRefVariable;
   1.376 +
   1.377 +    if (!aMemberVariable)
   1.378 +        query->mMemberVariable = do_GetAtom("?");
   1.379 +    else
   1.380 +        query->mMemberVariable = aMemberVariable;
   1.381 +
   1.382 +    nsresult rv;
   1.383 +    TestNode *lastnode = nullptr;
   1.384 +
   1.385 +    nsCOMPtr<nsIContent> content = do_QueryInterface(aQueryNode);
   1.386 +
   1.387 +    if (content->NodeInfo()->Equals(nsGkAtoms::_template, kNameSpaceID_XUL)) {
   1.388 +        // simplified syntax with no rules
   1.389 +
   1.390 +        query->SetSimple();
   1.391 +        NS_ASSERTION(!mSimpleRuleMemberTest,
   1.392 +                     "CompileQuery called twice with the same template");
   1.393 +        if (!mSimpleRuleMemberTest)
   1.394 +            rv = CompileSimpleQuery(query, content, &lastnode);
   1.395 +        else
   1.396 +            rv = NS_ERROR_FAILURE;
   1.397 +    }
   1.398 +    else if (content->NodeInfo()->Equals(nsGkAtoms::rule, kNameSpaceID_XUL)) {
   1.399 +        // simplified syntax with at least one rule
   1.400 +        query->SetSimple();
   1.401 +        rv = CompileSimpleQuery(query, content, &lastnode);
   1.402 +    }
   1.403 +    else {
   1.404 +        rv = CompileExtendedQuery(query, content, &lastnode);
   1.405 +    }
   1.406 +
   1.407 +    if (NS_FAILED(rv))
   1.408 +        return rv;
   1.409 +
   1.410 +    query->SetQueryNode(aQueryNode);
   1.411 +
   1.412 +    nsInstantiationNode* instnode = new nsInstantiationNode(this, query);
   1.413 +    if (!instnode)
   1.414 +        return NS_ERROR_OUT_OF_MEMORY;
   1.415 +
   1.416 +    // this and other functions always add nodes to mAllTests first. That
   1.417 +    // way if something fails, the node will just sit harmlessly in mAllTests
   1.418 +    // where it can be deleted later. 
   1.419 +    rv = mAllTests.Add(instnode);
   1.420 +    if (NS_FAILED(rv)) {
   1.421 +        delete instnode;
   1.422 +        return rv;
   1.423 +    }
   1.424 +
   1.425 +    rv = lastnode->AddChild(instnode);
   1.426 +    if (NS_FAILED(rv))
   1.427 +        return rv;
   1.428 +
   1.429 +    mQueries.AppendElement(query);
   1.430 +
   1.431 +    *_retval = query;
   1.432 +    NS_ADDREF(*_retval);
   1.433 +
   1.434 +    return NS_OK;
   1.435 +}
   1.436 +
   1.437 +NS_IMETHODIMP
   1.438 +nsXULTemplateQueryProcessorRDF::GenerateResults(nsISupports* aDatasource,
   1.439 +                                                nsIXULTemplateResult* aRef,
   1.440 +                                                nsISupports* aQuery,
   1.441 +                                                nsISimpleEnumerator** aResults)
   1.442 +{
   1.443 +    nsCOMPtr<nsITemplateRDFQuery> rdfquery = do_QueryInterface(aQuery);
   1.444 +    if (! rdfquery)
   1.445 +        return NS_ERROR_INVALID_ARG;
   1.446 +
   1.447 +    mGenerationStarted = true;
   1.448 +
   1.449 +    // should be safe to cast here since the query is a
   1.450 +    // non-scriptable nsITemplateRDFQuery
   1.451 +    nsRDFQuery* query = static_cast<nsRDFQuery *>(aQuery);
   1.452 +
   1.453 +    *aResults = nullptr;
   1.454 +
   1.455 +    nsCOMPtr<nsISimpleEnumerator> results;
   1.456 +
   1.457 +    if (aRef) {
   1.458 +        // make sure that cached results were generated for this ref, and if not,
   1.459 +        // regenerate them. Otherwise, things will go wrong for templates bound to
   1.460 +        // an HTML element as they are not generated lazily.
   1.461 +        if (aRef == mLastRef) {
   1.462 +            query->UseCachedResults(getter_AddRefs(results));
   1.463 +        }
   1.464 +        else {
   1.465 +            // clear the cached results
   1.466 +            int32_t count = mQueries.Length();
   1.467 +            for (int32_t r = 0; r < count; r++) {
   1.468 +                mQueries[r]->ClearCachedResults();
   1.469 +            }
   1.470 +        }
   1.471 +
   1.472 +        if (! results) {
   1.473 +            if (! query->mRefVariable)
   1.474 +                query->mRefVariable = do_GetAtom("?uri");
   1.475 +
   1.476 +            nsCOMPtr<nsIRDFResource> refResource;
   1.477 +            aRef->GetResource(getter_AddRefs(refResource));
   1.478 +            if (! refResource)
   1.479 +                return NS_ERROR_FAILURE;
   1.480 +
   1.481 +            // Propagate the assignments through the network
   1.482 +            TestNode* root = query->GetRoot();
   1.483 +
   1.484 +            if (query->IsSimple() && mSimpleRuleMemberTest) {
   1.485 +                // get the top node in the simple rule tree
   1.486 +                root = mSimpleRuleMemberTest->GetParent();
   1.487 +                mLastRef = aRef;
   1.488 +            }
   1.489 +
   1.490 +#ifdef PR_LOGGING
   1.491 +            if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
   1.492 +                nsAutoString id;
   1.493 +                aRef->GetId(id);
   1.494 +
   1.495 +                nsAutoString rvar;
   1.496 +                query->mRefVariable->ToString(rvar);
   1.497 +                nsAutoString mvar;
   1.498 +                query->mMemberVariable->ToString(mvar);
   1.499 +
   1.500 +                PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
   1.501 +                       ("QueryProcessor::GenerateResults using ref %s and vars [ ref: %s  member: %s]",
   1.502 +                       NS_ConvertUTF16toUTF8(id).get(),
   1.503 +                       NS_ConvertUTF16toUTF8(rvar).get(),
   1.504 +                       NS_ConvertUTF16toUTF8(mvar).get()));
   1.505 +            }
   1.506 +#endif
   1.507 +
   1.508 +            if (root) {
   1.509 +                // the seed is the initial instantiation, which has a single
   1.510 +                // assignment holding the reference point
   1.511 +                Instantiation seed;
   1.512 +                seed.AddAssignment(query->mRefVariable, refResource);
   1.513 +
   1.514 +                InstantiationSet* instantiations = new InstantiationSet();
   1.515 +                if (!instantiations)
   1.516 +                    return NS_ERROR_OUT_OF_MEMORY;
   1.517 +                instantiations->Append(seed);
   1.518 +
   1.519 +                // if the propagation caused a match, then the results will be
   1.520 +                // cached in the query, retrieved below by calling
   1.521 +                // UseCachedResults. The matching result set owns the
   1.522 +                // instantiations and will delete them when results have been
   1.523 +                // iterated over. If the propagation did not match, the
   1.524 +                // instantiations need to be deleted.
   1.525 +                bool owned = false;
   1.526 +                nsresult rv = root->Propagate(*instantiations, false, owned);
   1.527 +                if (! owned)
   1.528 +                    delete instantiations;
   1.529 +                if (NS_FAILED(rv))
   1.530 +                    return rv;
   1.531 +
   1.532 +                query->UseCachedResults(getter_AddRefs(results));
   1.533 +            }
   1.534 +        }
   1.535 +    }
   1.536 +
   1.537 +    if (! results) {
   1.538 +        // no results were found so create an empty set
   1.539 +        results = new nsXULTemplateResultSetRDF(this, query, nullptr);
   1.540 +        if (! results)
   1.541 +            return NS_ERROR_OUT_OF_MEMORY;
   1.542 +    }
   1.543 +
   1.544 +    results.swap(*aResults);
   1.545 +
   1.546 +    return NS_OK;
   1.547 +}
   1.548 +
   1.549 +NS_IMETHODIMP
   1.550 +nsXULTemplateQueryProcessorRDF::AddBinding(nsIDOMNode* aRuleNode,
   1.551 +                                           nsIAtom* aVar,
   1.552 +                                           nsIAtom* aRef,
   1.553 +                                           const nsAString& aExpr)
   1.554 +{
   1.555 +    // add a <binding> to a rule. When a result is matched, the bindings are
   1.556 +    // examined to add additional variable assignments
   1.557 +
   1.558 +    // bindings can't be added once result generation has started, otherwise
   1.559 +    // the array sizes will get out of sync
   1.560 +    if (mGenerationStarted)
   1.561 +        return NS_ERROR_UNEXPECTED;
   1.562 +
   1.563 +    nsCOMPtr<nsIRDFResource> property;
   1.564 +    nsresult rv = gRDFService->GetUnicodeResource(aExpr, getter_AddRefs(property));
   1.565 +    if (NS_FAILED(rv))
   1.566 +        return rv;
   1.567 +
   1.568 +    nsRefPtr<RDFBindingSet> bindings = mRuleToBindingsMap.GetWeak(aRuleNode);
   1.569 +    if (!bindings) {
   1.570 +        bindings = new RDFBindingSet();
   1.571 +        mRuleToBindingsMap.Put(aRuleNode, bindings);
   1.572 +    }
   1.573 +
   1.574 +    return bindings->AddBinding(aVar, aRef, property);
   1.575 +}
   1.576 +
   1.577 +NS_IMETHODIMP
   1.578 +nsXULTemplateQueryProcessorRDF::TranslateRef(nsISupports* aDatasource,
   1.579 +                                                           const nsAString& aRefString,
   1.580 +                                                           nsIXULTemplateResult** aRef)
   1.581 +{
   1.582 +    // make sure the RDF service is set up
   1.583 +    nsresult rv = InitGlobals();
   1.584 +    if (NS_FAILED(rv))
   1.585 +        return rv;
   1.586 +
   1.587 +    nsCOMPtr<nsIRDFResource> uri;
   1.588 +    gRDFService->GetUnicodeResource(aRefString, getter_AddRefs(uri));
   1.589 +
   1.590 +    nsXULTemplateResultRDF* refresult = new nsXULTemplateResultRDF(uri);
   1.591 +    if (! refresult)
   1.592 +        return NS_ERROR_OUT_OF_MEMORY;
   1.593 +
   1.594 +    *aRef = refresult;
   1.595 +    NS_ADDREF(*aRef);
   1.596 +
   1.597 +    return NS_OK;
   1.598 +}
   1.599 +
   1.600 +NS_IMETHODIMP
   1.601 +nsXULTemplateQueryProcessorRDF::CompareResults(nsIXULTemplateResult* aLeft,
   1.602 +                                               nsIXULTemplateResult* aRight,
   1.603 +                                               nsIAtom* aVar,
   1.604 +                                               uint32_t aSortHints,
   1.605 +                                               int32_t* aResult)
   1.606 +{
   1.607 +    NS_ENSURE_ARG_POINTER(aLeft);
   1.608 +    NS_ENSURE_ARG_POINTER(aRight);
   1.609 +
   1.610 +    *aResult = 0;
   1.611 +
   1.612 +    // for natural order sorting, use the index in the RDF container for the
   1.613 +    // order. If there is no container, just sort them arbitrarily.
   1.614 +    if (!aVar) {
   1.615 +        // if a result has a negative index, just sort it first
   1.616 +        int32_t leftindex = GetContainerIndexOf(aLeft);
   1.617 +        int32_t rightindex = GetContainerIndexOf(aRight);
   1.618 +        *aResult = leftindex == rightindex ? 0 :
   1.619 +                   leftindex > rightindex ? 1 :
   1.620 +                   -1;
   1.621 +        return NS_OK;
   1.622 +    }
   1.623 +
   1.624 +    nsDependentAtomString sortkey(aVar);
   1.625 +
   1.626 +    nsCOMPtr<nsISupports> leftNode, rightNode;
   1.627 +
   1.628 +    if (!sortkey.IsEmpty() && sortkey[0] != '?' &&
   1.629 +        !StringBeginsWith(sortkey, NS_LITERAL_STRING("rdf:")) &&
   1.630 +        mDB) {
   1.631 +        // if the sort key is not a template variable, it should be an RDF
   1.632 +        // predicate. Get the targets and compare those instead.
   1.633 +        nsCOMPtr<nsIRDFResource> predicate;
   1.634 +        nsresult rv = gRDFService->GetUnicodeResource(sortkey, getter_AddRefs(predicate));
   1.635 +        NS_ENSURE_SUCCESS(rv, rv);
   1.636 +
   1.637 +        // create a predicate with '?sort=true' appended. This special
   1.638 +        // predicate may be used to have a different sort value than the
   1.639 +        // displayed value
   1.640 +        sortkey.AppendLiteral("?sort=true");
   1.641 +
   1.642 +        nsCOMPtr<nsIRDFResource> sortPredicate;
   1.643 +        rv = gRDFService->GetUnicodeResource(sortkey, getter_AddRefs(sortPredicate));
   1.644 +        NS_ENSURE_SUCCESS(rv, rv);
   1.645 +
   1.646 +        rv = GetSortValue(aLeft, predicate, sortPredicate, getter_AddRefs(leftNode));
   1.647 +        NS_ENSURE_SUCCESS(rv, rv);
   1.648 +
   1.649 +        rv = GetSortValue(aRight, predicate, sortPredicate, getter_AddRefs(rightNode));
   1.650 +        NS_ENSURE_SUCCESS(rv, rv);
   1.651 +    }
   1.652 +    else {
   1.653 +        // get the values for the sort key from the results
   1.654 +        aLeft->GetBindingObjectFor(aVar, getter_AddRefs(leftNode));
   1.655 +        aRight->GetBindingObjectFor(aVar, getter_AddRefs(rightNode));
   1.656 +    }
   1.657 +
   1.658 +    {
   1.659 +        // Literals?
   1.660 +        nsCOMPtr<nsIRDFLiteral> l = do_QueryInterface(leftNode);
   1.661 +        if (l) {
   1.662 +            nsCOMPtr<nsIRDFLiteral> r = do_QueryInterface(rightNode);
   1.663 +            if (r) {
   1.664 +                const char16_t *lstr, *rstr;
   1.665 +                l->GetValueConst(&lstr);
   1.666 +                r->GetValueConst(&rstr);
   1.667 +
   1.668 +                *aResult = XULSortServiceImpl::CompareValues(
   1.669 +                               nsDependentString(lstr),
   1.670 +                               nsDependentString(rstr), aSortHints);
   1.671 +            }
   1.672 +
   1.673 +            return NS_OK;
   1.674 +        }
   1.675 +    }
   1.676 +
   1.677 +    {
   1.678 +        // Dates?
   1.679 +        nsCOMPtr<nsIRDFDate> l = do_QueryInterface(leftNode);
   1.680 +        if (l) {
   1.681 +            nsCOMPtr<nsIRDFDate> r = do_QueryInterface(rightNode);
   1.682 +            if (r) {
   1.683 +                PRTime ldate, rdate;
   1.684 +                l->GetValue(&ldate);
   1.685 +                r->GetValue(&rdate);
   1.686 +
   1.687 +                int64_t delta = ldate - rdate;
   1.688 +                if (delta == 0)
   1.689 +                    *aResult = 0;
   1.690 +                else if (delta >= 0)
   1.691 +                    *aResult = 1;
   1.692 +                else
   1.693 +                    *aResult = -1;
   1.694 +            }
   1.695 +
   1.696 +            return NS_OK;
   1.697 +        }
   1.698 +    }
   1.699 +
   1.700 +    {
   1.701 +        // Integers?
   1.702 +        nsCOMPtr<nsIRDFInt> l = do_QueryInterface(leftNode);
   1.703 +        if (l) {
   1.704 +            nsCOMPtr<nsIRDFInt> r = do_QueryInterface(rightNode);
   1.705 +            if (r) {
   1.706 +                int32_t lval, rval;
   1.707 +                l->GetValue(&lval);
   1.708 +                r->GetValue(&rval);
   1.709 +
   1.710 +                *aResult = lval - rval;
   1.711 +            }
   1.712 +
   1.713 +            return NS_OK;
   1.714 +        }
   1.715 +    }
   1.716 +
   1.717 +    nsICollation* collation = nsXULContentUtils::GetCollation();
   1.718 +    if (collation) {
   1.719 +        // Blobs? (We can only compare these reasonably if we have a
   1.720 +        // collation object.)
   1.721 +        nsCOMPtr<nsIRDFBlob> l = do_QueryInterface(leftNode);
   1.722 +        if (l) {
   1.723 +            nsCOMPtr<nsIRDFBlob> r = do_QueryInterface(rightNode);
   1.724 +            if (r) {
   1.725 +                const uint8_t *lval, *rval;
   1.726 +                int32_t llen, rlen;
   1.727 +                l->GetValue(&lval);
   1.728 +                l->GetLength(&llen);
   1.729 +                r->GetValue(&rval);
   1.730 +                r->GetLength(&rlen);
   1.731 +                
   1.732 +                collation->CompareRawSortKey(lval, llen, rval, rlen, aResult);
   1.733 +            }
   1.734 +        }
   1.735 +    }
   1.736 +
   1.737 +    // if the results are none of the above, just pretend that they are equal
   1.738 +    return NS_OK;
   1.739 +}
   1.740 +
   1.741 +//----------------------------------------------------------------------
   1.742 +//
   1.743 +// nsIRDFObserver interface
   1.744 +//
   1.745 +
   1.746 +
   1.747 +NS_IMETHODIMP
   1.748 +nsXULTemplateQueryProcessorRDF::OnAssert(nsIRDFDataSource* aDataSource,
   1.749 +                                         nsIRDFResource* aSource,
   1.750 +                                         nsIRDFResource* aProperty,
   1.751 +                                         nsIRDFNode* aTarget)
   1.752 +{
   1.753 +    // Ignore updates if we're batching
   1.754 +    if (mUpdateBatchNest)
   1.755 +        return(NS_OK);
   1.756 +
   1.757 +    if (! mBuilder)
   1.758 +        return NS_OK;
   1.759 +
   1.760 +    LOG("onassert", aSource, aProperty, aTarget);
   1.761 +
   1.762 +    Propagate(aSource, aProperty, aTarget);
   1.763 +    SynchronizeAll(aSource, aProperty, nullptr, aTarget);
   1.764 +    return NS_OK;
   1.765 +}
   1.766 +
   1.767 +
   1.768 +
   1.769 +NS_IMETHODIMP
   1.770 +nsXULTemplateQueryProcessorRDF::OnUnassert(nsIRDFDataSource* aDataSource,
   1.771 +                                           nsIRDFResource* aSource,
   1.772 +                                           nsIRDFResource* aProperty,
   1.773 +                                           nsIRDFNode* aTarget)
   1.774 +{
   1.775 +    // Ignore updates if we're batching
   1.776 +    if (mUpdateBatchNest)
   1.777 +        return NS_OK;
   1.778 +
   1.779 +    if (! mBuilder)
   1.780 +        return NS_OK;
   1.781 +
   1.782 +    LOG("onunassert", aSource, aProperty, aTarget);
   1.783 +
   1.784 +    Retract(aSource, aProperty, aTarget);
   1.785 +    SynchronizeAll(aSource, aProperty, aTarget, nullptr);
   1.786 +    return NS_OK;
   1.787 +}
   1.788 +
   1.789 +
   1.790 +NS_IMETHODIMP
   1.791 +nsXULTemplateQueryProcessorRDF::OnChange(nsIRDFDataSource* aDataSource,
   1.792 +                                         nsIRDFResource* aSource,
   1.793 +                                         nsIRDFResource* aProperty,
   1.794 +                                         nsIRDFNode* aOldTarget,
   1.795 +                                         nsIRDFNode* aNewTarget)
   1.796 +{
   1.797 +    // Ignore updates if we're batching
   1.798 +    if (mUpdateBatchNest)
   1.799 +        return NS_OK;
   1.800 +
   1.801 +    if (! mBuilder)
   1.802 +        return NS_OK;
   1.803 +
   1.804 +    LOG("onchange", aSource, aProperty, aNewTarget);
   1.805 +
   1.806 +    if (aOldTarget) {
   1.807 +        // Pull any old results that were relying on aOldTarget
   1.808 +        Retract(aSource, aProperty, aOldTarget);
   1.809 +    }
   1.810 +
   1.811 +    if (aNewTarget) {
   1.812 +        // Fire any new results that are activated by aNewTarget
   1.813 +        Propagate(aSource, aProperty, aNewTarget);
   1.814 +    }
   1.815 +
   1.816 +    // Synchronize any of the content model that may have changed.
   1.817 +    SynchronizeAll(aSource, aProperty, aOldTarget, aNewTarget);
   1.818 +    return NS_OK;
   1.819 +}
   1.820 +
   1.821 +
   1.822 +NS_IMETHODIMP
   1.823 +nsXULTemplateQueryProcessorRDF::OnMove(nsIRDFDataSource* aDataSource,
   1.824 +                                       nsIRDFResource* aOldSource,
   1.825 +                                       nsIRDFResource* aNewSource,
   1.826 +                                       nsIRDFResource* aProperty,
   1.827 +                                       nsIRDFNode* aTarget)
   1.828 +{
   1.829 +    // Ignore updates if we're batching
   1.830 +    if (mUpdateBatchNest)
   1.831 +        return NS_OK;
   1.832 +
   1.833 +    NS_NOTYETIMPLEMENTED("write me");
   1.834 +    return NS_ERROR_NOT_IMPLEMENTED;
   1.835 +}
   1.836 +
   1.837 +
   1.838 +NS_IMETHODIMP
   1.839 +nsXULTemplateQueryProcessorRDF::OnBeginUpdateBatch(nsIRDFDataSource* aDataSource)
   1.840 +{
   1.841 +    mUpdateBatchNest++;
   1.842 +    return NS_OK;
   1.843 +}
   1.844 +
   1.845 +
   1.846 +NS_IMETHODIMP
   1.847 +nsXULTemplateQueryProcessorRDF::OnEndUpdateBatch(nsIRDFDataSource* aDataSource)
   1.848 +{
   1.849 +    NS_ASSERTION(mUpdateBatchNest > 0, "badly nested update batch");
   1.850 +    if (--mUpdateBatchNest <= 0) {
   1.851 +        mUpdateBatchNest = 0;
   1.852 +
   1.853 +        if (mBuilder)
   1.854 +            mBuilder->Rebuild();
   1.855 +    }
   1.856 +
   1.857 +    return NS_OK;
   1.858 +}
   1.859 +
   1.860 +nsresult
   1.861 +nsXULTemplateQueryProcessorRDF::Propagate(nsIRDFResource* aSource,
   1.862 +                                          nsIRDFResource* aProperty,
   1.863 +                                          nsIRDFNode* aTarget)
   1.864 +{
   1.865 +    // When a new assertion is added to the graph, determine any new matches
   1.866 +    // that must be added to the template builder. First, iterate through all
   1.867 +    // the RDF tests (<member> and <triple> tests), and find the topmost test
   1.868 +    // that would be affected by the new assertion.
   1.869 +    nsresult rv;
   1.870 +
   1.871 +    ReteNodeSet livenodes;
   1.872 +
   1.873 +#ifdef PR_LOGGING
   1.874 +    if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
   1.875 +        const char* sourceStr;
   1.876 +        aSource->GetValueConst(&sourceStr);
   1.877 +        const char* propertyStr;
   1.878 +        aProperty->GetValueConst(&propertyStr);
   1.879 +        nsAutoString targetStr;
   1.880 +        nsXULContentUtils::GetTextForNode(aTarget, targetStr);
   1.881 +
   1.882 +        PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
   1.883 +               ("nsXULTemplateQueryProcessorRDF::Propagate: [%s] -> [%s] -> [%s]\n",
   1.884 +               sourceStr, propertyStr, NS_ConvertUTF16toUTF8(targetStr).get()));
   1.885 +    }
   1.886 +#endif
   1.887 +
   1.888 +    {
   1.889 +        ReteNodeSet::Iterator last = mRDFTests.Last();
   1.890 +        for (ReteNodeSet::Iterator i = mRDFTests.First(); i != last; ++i) {
   1.891 +            nsRDFTestNode* rdftestnode = static_cast<nsRDFTestNode*>(*i);
   1.892 +
   1.893 +            Instantiation seed;
   1.894 +            if (rdftestnode->CanPropagate(aSource, aProperty, aTarget, seed)) {
   1.895 +                rv = livenodes.Add(rdftestnode);
   1.896 +                if (NS_FAILED(rv))
   1.897 +                    return rv;
   1.898 +            }
   1.899 +        }
   1.900 +    }
   1.901 +
   1.902 +    // Now, we'll go through each, and any that aren't dominated by
   1.903 +    // another live node will be used to propagate the assertion
   1.904 +    // through the rule network
   1.905 +    {
   1.906 +        ReteNodeSet::Iterator last = livenodes.Last();
   1.907 +        for (ReteNodeSet::Iterator i = livenodes.First(); i != last; ++i) {
   1.908 +            nsRDFTestNode* rdftestnode = static_cast<nsRDFTestNode*>(*i);
   1.909 +
   1.910 +            // What happens here is we create an instantiation as if we were
   1.911 +            // at the found test in the rule network. For example, if the
   1.912 +            // found test was a member test (parent => child), the parent
   1.913 +            // and child variables are assigned the values provided by the new
   1.914 +            // RDF assertion in the graph. The Constrain call is used to go
   1.915 +            // up to earlier RDF tests, filling in variables as it goes.
   1.916 +            // Constrain will eventually get up to the top node, an
   1.917 +            // nsContentTestNode, which takes the value of the reference
   1.918 +            // variable and calls the template builder to see if a result has
   1.919 +            // been generated already for the reference value. If it hasn't,
   1.920 +            // the new assertion couldn't cause a new match. If the result
   1.921 +            // exists, call Propagate to continue to the later RDF tests to
   1.922 +            // fill in the rest of the variable assignments.
   1.923 +
   1.924 +            // Bogus, to get the seed instantiation
   1.925 +            Instantiation seed;
   1.926 +            rdftestnode->CanPropagate(aSource, aProperty, aTarget, seed);
   1.927 +
   1.928 +            InstantiationSet* instantiations = new InstantiationSet();
   1.929 +            if (!instantiations)
   1.930 +                return NS_ERROR_OUT_OF_MEMORY;
   1.931 +            instantiations->Append(seed);
   1.932 +
   1.933 +            rv = rdftestnode->Constrain(*instantiations);
   1.934 +            if (NS_FAILED(rv)) {
   1.935 +                delete instantiations;
   1.936 +                return rv;
   1.937 +            }
   1.938 +
   1.939 +            bool owned = false;
   1.940 +            if (!instantiations->Empty())
   1.941 +                rv = rdftestnode->Propagate(*instantiations, true, owned);
   1.942 +
   1.943 +            // owned should always be false in update mode, but check just
   1.944 +            // to be sure
   1.945 +            if (!owned)
   1.946 +                delete instantiations;
   1.947 +            if (NS_FAILED(rv))
   1.948 +                return rv;
   1.949 +        }
   1.950 +    }
   1.951 +
   1.952 +    return NS_OK;
   1.953 +}
   1.954 +
   1.955 +
   1.956 +nsresult
   1.957 +nsXULTemplateQueryProcessorRDF::Retract(nsIRDFResource* aSource,
   1.958 +                                        nsIRDFResource* aProperty,
   1.959 +                                        nsIRDFNode* aTarget)
   1.960 +{
   1.961 +
   1.962 +#ifdef PR_LOGGING
   1.963 +    if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
   1.964 +        const char* sourceStr;
   1.965 +        aSource->GetValueConst(&sourceStr);
   1.966 +        const char* propertyStr;
   1.967 +        aProperty->GetValueConst(&propertyStr);
   1.968 +        nsAutoString targetStr;
   1.969 +        nsXULContentUtils::GetTextForNode(aTarget, targetStr);
   1.970 +
   1.971 +        PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
   1.972 +               ("nsXULTemplateQueryProcessorRDF::Retract: [%s] -> [%s] -> [%s]\n",
   1.973 +               sourceStr, propertyStr, NS_ConvertUTF16toUTF8(targetStr).get()));
   1.974 +    }
   1.975 +#endif
   1.976 +
   1.977 +    // Retract any currently active rules that will no longer be matched.
   1.978 +    ReteNodeSet::ConstIterator lastnode = mRDFTests.Last();
   1.979 +    for (ReteNodeSet::ConstIterator node = mRDFTests.First(); node != lastnode; ++node) {
   1.980 +        const nsRDFTestNode* rdftestnode = static_cast<const nsRDFTestNode*>(*node);
   1.981 +
   1.982 +        rdftestnode->Retract(aSource, aProperty, aTarget);
   1.983 +
   1.984 +        // Now fire any newly revealed rules
   1.985 +        // XXXwaterson yo. write me.
   1.986 +        // The intent here is to handle any rules that might be
   1.987 +        // "revealed" by the removal of an assertion from the datasource.
   1.988 +        // Waterson doesn't think we support negated conditions in a rule.
   1.989 +        // Nor is he sure that this is currently useful.
   1.990 +    }
   1.991 +
   1.992 +    return NS_OK;
   1.993 +}
   1.994 +
   1.995 +nsresult
   1.996 +nsXULTemplateQueryProcessorRDF::SynchronizeAll(nsIRDFResource* aSource,
   1.997 +                                               nsIRDFResource* aProperty,
   1.998 +                                               nsIRDFNode* aOldTarget,
   1.999 +                                               nsIRDFNode* aNewTarget)
  1.1000 +{
  1.1001 +    // Update each match that contains <aSource, aProperty, aOldTarget>.
  1.1002 +
  1.1003 +    // Get all the matches whose assignments are currently supported
  1.1004 +    // by aSource and aProperty: we'll need to recompute them.
  1.1005 +    ResultArray* results;
  1.1006 +    if (!mBindingDependencies.Get(aSource, &results) || !mBuilder)
  1.1007 +        return NS_OK;
  1.1008 +
  1.1009 +    uint32_t length = results->Length();
  1.1010 +
  1.1011 +    for (uint32_t r = 0; r < length; r++) {
  1.1012 +        nsXULTemplateResultRDF* result = (*results)[r];
  1.1013 +        if (result) {
  1.1014 +            // synchronize the result's bindings and then update the builder
  1.1015 +            // so that content can be updated
  1.1016 +            if (result->SyncAssignments(aSource, aProperty, aNewTarget)) {
  1.1017 +                nsITemplateRDFQuery* query = result->Query();
  1.1018 +                if (query) {
  1.1019 +                    nsCOMPtr<nsIDOMNode> querynode;
  1.1020 +                    query->GetQueryNode(getter_AddRefs(querynode));
  1.1021 +
  1.1022 +                    mBuilder->ResultBindingChanged(result);
  1.1023 +                }
  1.1024 +            }
  1.1025 +        }
  1.1026 +    }
  1.1027 +
  1.1028 +    return NS_OK;
  1.1029 +}
  1.1030 +
  1.1031 +#ifdef PR_LOGGING
  1.1032 +nsresult
  1.1033 +nsXULTemplateQueryProcessorRDF::Log(const char* aOperation,
  1.1034 +                                    nsIRDFResource* aSource,
  1.1035 +                                    nsIRDFResource* aProperty,
  1.1036 +                                    nsIRDFNode* aTarget)
  1.1037 +{
  1.1038 +    if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
  1.1039 +        nsresult rv;
  1.1040 +
  1.1041 +        const char* sourceStr;
  1.1042 +        rv = aSource->GetValueConst(&sourceStr);
  1.1043 +        if (NS_FAILED(rv))
  1.1044 +            return rv;
  1.1045 +
  1.1046 +        PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
  1.1047 +               ("xultemplate[%p] %8s [%s]--", this, aOperation, sourceStr));
  1.1048 +
  1.1049 +        const char* propertyStr;
  1.1050 +        rv = aProperty->GetValueConst(&propertyStr);
  1.1051 +        if (NS_FAILED(rv))
  1.1052 +            return rv;
  1.1053 +
  1.1054 +        nsAutoString targetStr;
  1.1055 +        rv = nsXULContentUtils::GetTextForNode(aTarget, targetStr);
  1.1056 +        if (NS_FAILED(rv))
  1.1057 +            return rv;
  1.1058 +
  1.1059 +        nsAutoCString targetstrC;
  1.1060 +        targetstrC.AssignWithConversion(targetStr);
  1.1061 +        PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
  1.1062 +               ("                        --[%s]-->[%s]",
  1.1063 +                propertyStr,
  1.1064 +                targetstrC.get()));
  1.1065 +    }
  1.1066 +    return NS_OK;
  1.1067 +}
  1.1068 +#endif
  1.1069 +
  1.1070 +nsresult
  1.1071 +nsXULTemplateQueryProcessorRDF::CheckContainer(nsIRDFResource* aResource,
  1.1072 +                                               bool* aIsContainer)
  1.1073 +{
  1.1074 +    NS_ENSURE_ARG_POINTER(aIsContainer);
  1.1075 +    NS_ENSURE_STATE(mDB);
  1.1076 +
  1.1077 +    // We have to look at all of the arcs extending out of the
  1.1078 +    // resource: if any of them are that "containment" property, then
  1.1079 +    // we know we'll have children.
  1.1080 +    bool isContainer = false;
  1.1081 +
  1.1082 +    for (nsResourceSet::ConstIterator property = mContainmentProperties.First();
  1.1083 +         property != mContainmentProperties.Last();
  1.1084 +         property++) {
  1.1085 +        bool hasArc = false;
  1.1086 +        mDB->HasArcOut(aResource, *property, &hasArc);
  1.1087 +
  1.1088 +        if (hasArc) {
  1.1089 +            // Well, it's a container...
  1.1090 +            isContainer = true;
  1.1091 +            break;
  1.1092 +        }
  1.1093 +    }
  1.1094 +
  1.1095 +    // If we get here, and we're still not sure if it's a container,
  1.1096 +    // then see if it's an RDF container
  1.1097 +    if (! isContainer) {
  1.1098 +        gRDFContainerUtils->IsContainer(mDB, aResource, &isContainer);
  1.1099 +    }
  1.1100 +
  1.1101 +    *aIsContainer = isContainer;
  1.1102 +
  1.1103 +    return NS_OK;
  1.1104 +}
  1.1105 +
  1.1106 +nsresult
  1.1107 +nsXULTemplateQueryProcessorRDF::CheckEmpty(nsIRDFResource* aResource,
  1.1108 +                                           bool* aIsEmpty)
  1.1109 +{
  1.1110 +    NS_ENSURE_STATE(mDB);
  1.1111 +    *aIsEmpty = true;
  1.1112 +
  1.1113 +    for (nsResourceSet::ConstIterator property = mContainmentProperties.First();
  1.1114 +         property != mContainmentProperties.Last();
  1.1115 +         property++) {
  1.1116 +
  1.1117 +        nsCOMPtr<nsIRDFNode> dummy;
  1.1118 +        mDB->GetTarget(aResource, *property, true, getter_AddRefs(dummy));
  1.1119 +
  1.1120 +        if (dummy) {
  1.1121 +            *aIsEmpty = false;
  1.1122 +            break;
  1.1123 +        }
  1.1124 +    }
  1.1125 +
  1.1126 +    if (*aIsEmpty){
  1.1127 +        return nsXULTemplateQueryProcessorRDF::gRDFContainerUtils->
  1.1128 +                   IsEmpty(mDB, aResource, aIsEmpty);
  1.1129 +    }
  1.1130 +
  1.1131 +    return NS_OK;
  1.1132 +}
  1.1133 +
  1.1134 +nsresult
  1.1135 +nsXULTemplateQueryProcessorRDF::CheckIsSeparator(nsIRDFResource* aResource,
  1.1136 +                                                 bool* aIsSeparator)
  1.1137 +{
  1.1138 +    NS_ENSURE_STATE(mDB);
  1.1139 +    return mDB->HasAssertion(aResource, kRDF_type, kNC_BookmarkSeparator,
  1.1140 +                             true, aIsSeparator);
  1.1141 +}
  1.1142 +
  1.1143 +//----------------------------------------------------------------------
  1.1144 +
  1.1145 +nsresult
  1.1146 +nsXULTemplateQueryProcessorRDF::ComputeContainmentProperties(nsIDOMNode* aRootNode)
  1.1147 +{
  1.1148 +    // The 'containment' attribute on the root node is a
  1.1149 +    // whitespace-separated list that tells us which properties we
  1.1150 +    // should use to test for containment.
  1.1151 +    nsresult rv;
  1.1152 +
  1.1153 +    mContainmentProperties.Clear();
  1.1154 +
  1.1155 +    nsCOMPtr<nsIContent> content = do_QueryInterface(aRootNode);
  1.1156 +
  1.1157 +    nsAutoString containment;
  1.1158 +    content->GetAttr(kNameSpaceID_None, nsGkAtoms::containment, containment);
  1.1159 +
  1.1160 +    uint32_t len = containment.Length();
  1.1161 +    uint32_t offset = 0;
  1.1162 +    while (offset < len) {
  1.1163 +        while (offset < len && nsCRT::IsAsciiSpace(containment[offset]))
  1.1164 +            ++offset;
  1.1165 +
  1.1166 +        if (offset >= len)
  1.1167 +            break;
  1.1168 +
  1.1169 +        uint32_t end = offset;
  1.1170 +        while (end < len && !nsCRT::IsAsciiSpace(containment[end]))
  1.1171 +            ++end;
  1.1172 +
  1.1173 +        nsAutoString propertyStr;
  1.1174 +        containment.Mid(propertyStr, offset, end - offset);
  1.1175 +
  1.1176 +        nsCOMPtr<nsIRDFResource> property;
  1.1177 +        rv = gRDFService->GetUnicodeResource(propertyStr, getter_AddRefs(property));
  1.1178 +        if (NS_FAILED(rv))
  1.1179 +            return rv;
  1.1180 +
  1.1181 +        rv = mContainmentProperties.Add(property);
  1.1182 +        if (NS_FAILED(rv))
  1.1183 +            return rv;
  1.1184 +
  1.1185 +        offset = end;
  1.1186 +    }
  1.1187 +
  1.1188 +#define TREE_PROPERTY_HACK 1
  1.1189 +#if defined(TREE_PROPERTY_HACK)
  1.1190 +    if (! len) {
  1.1191 +        // Some ever-present membership tests.
  1.1192 +        mContainmentProperties.Add(nsXULContentUtils::NC_child);
  1.1193 +        mContainmentProperties.Add(nsXULContentUtils::NC_Folder);
  1.1194 +    }
  1.1195 +#endif
  1.1196 +
  1.1197 +    return NS_OK;
  1.1198 +}
  1.1199 +
  1.1200 +nsresult
  1.1201 +nsXULTemplateQueryProcessorRDF::CompileExtendedQuery(nsRDFQuery* aQuery,
  1.1202 +                                                     nsIContent* aConditions,
  1.1203 +                                                     TestNode** aLastNode)
  1.1204 +{
  1.1205 +    // Compile an extended query's children
  1.1206 +
  1.1207 +    nsContentTestNode* idnode =
  1.1208 +        new nsContentTestNode(this, aQuery->mRefVariable);
  1.1209 +    if (! idnode)
  1.1210 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1211 +
  1.1212 +    aQuery->SetRoot(idnode);
  1.1213 +    nsresult rv = mAllTests.Add(idnode);
  1.1214 +    if (NS_FAILED(rv)) {
  1.1215 +        delete idnode;
  1.1216 +        return rv;
  1.1217 +    }
  1.1218 +
  1.1219 +    TestNode* prevnode = idnode;
  1.1220 +
  1.1221 +    for (nsIContent* condition = aConditions->GetFirstChild();
  1.1222 +         condition;
  1.1223 +         condition = condition->GetNextSibling()) {
  1.1224 +
  1.1225 +        // the <content> condition should always be the first child
  1.1226 +        if (condition->Tag() == nsGkAtoms::content) {
  1.1227 +            if (condition != aConditions->GetFirstChild()) {
  1.1228 +                nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_CONTENT_NOT_FIRST);
  1.1229 +                continue;
  1.1230 +            }
  1.1231 +
  1.1232 +            // check for <content tag='tag'/> which indicates that matches
  1.1233 +            // should only be generated for items inside content with that tag
  1.1234 +            nsAutoString tagstr;
  1.1235 +            condition->GetAttr(kNameSpaceID_None, nsGkAtoms::tag, tagstr);
  1.1236 +
  1.1237 +            nsCOMPtr<nsIAtom> tag;
  1.1238 +            if (! tagstr.IsEmpty()) {
  1.1239 +                tag = do_GetAtom(tagstr);
  1.1240 +            }
  1.1241 +
  1.1242 +            nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(condition->GetDocument());
  1.1243 +            if (! doc)
  1.1244 +                return NS_ERROR_FAILURE;
  1.1245 +
  1.1246 +            idnode->SetTag(tag, doc);
  1.1247 +            continue;
  1.1248 +        }
  1.1249 +
  1.1250 +        TestNode* testnode = nullptr;
  1.1251 +        nsresult rv = CompileQueryChild(condition->Tag(), aQuery, condition,
  1.1252 +                                        prevnode, &testnode);
  1.1253 +        if (NS_FAILED(rv))
  1.1254 +            return rv;
  1.1255 +
  1.1256 +        if (testnode) {
  1.1257 +            rv = prevnode->AddChild(testnode);
  1.1258 +            if (NS_FAILED(rv))
  1.1259 +                return rv;
  1.1260 +
  1.1261 +            prevnode = testnode;
  1.1262 +        }
  1.1263 +    }
  1.1264 +
  1.1265 +    *aLastNode = prevnode;
  1.1266 +
  1.1267 +    return NS_OK;
  1.1268 +}
  1.1269 +
  1.1270 +nsresult
  1.1271 +nsXULTemplateQueryProcessorRDF::CompileQueryChild(nsIAtom* aTag,
  1.1272 +                                                  nsRDFQuery* aQuery,
  1.1273 +                                                  nsIContent* aCondition,
  1.1274 +                                                  TestNode* aParentNode,
  1.1275 +                                                  TestNode** aResult)
  1.1276 +{
  1.1277 +    nsresult rv;
  1.1278 +
  1.1279 +    if (aTag == nsGkAtoms::triple) {
  1.1280 +        rv = CompileTripleCondition(aQuery, aCondition, aParentNode, aResult);
  1.1281 +    }
  1.1282 +    else if (aTag == nsGkAtoms::member) {
  1.1283 +        rv = CompileMemberCondition(aQuery, aCondition, aParentNode, aResult);
  1.1284 +    }
  1.1285 +    else {
  1.1286 +#ifdef PR_LOGGING
  1.1287 +        nsAutoString tagstr;
  1.1288 +        aTag->ToString(tagstr);
  1.1289 +
  1.1290 +        nsAutoCString tagstrC;
  1.1291 +        tagstrC.AssignWithConversion(tagstr);
  1.1292 +        PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
  1.1293 +               ("xultemplate[%p] unrecognized condition test <%s>",
  1.1294 +                this, tagstrC.get()));
  1.1295 +#endif
  1.1296 +
  1.1297 +        rv = NS_OK;
  1.1298 +    }
  1.1299 +
  1.1300 +    return rv;
  1.1301 +}
  1.1302 +
  1.1303 +nsresult
  1.1304 +nsXULTemplateQueryProcessorRDF::ParseLiteral(const nsString& aParseType, 
  1.1305 +                                             const nsString& aValue,
  1.1306 +                                             nsIRDFNode** aResult)
  1.1307 +{
  1.1308 +    nsresult rv = NS_OK;
  1.1309 +    *aResult = nullptr;
  1.1310 +
  1.1311 +    if (aParseType.EqualsLiteral(PARSE_TYPE_INTEGER)) {
  1.1312 +        nsCOMPtr<nsIRDFInt> intLiteral;
  1.1313 +        nsresult errorCode;
  1.1314 +        int32_t intValue = aValue.ToInteger(&errorCode);
  1.1315 +        if (NS_FAILED(errorCode))
  1.1316 +            return NS_ERROR_FAILURE;
  1.1317 +        rv = gRDFService->GetIntLiteral(intValue, getter_AddRefs(intLiteral));
  1.1318 +        if (NS_FAILED(rv)) 
  1.1319 +            return rv;
  1.1320 +        rv = CallQueryInterface(intLiteral, aResult);
  1.1321 +    }
  1.1322 +    else {
  1.1323 +        nsCOMPtr<nsIRDFLiteral> literal;
  1.1324 +        rv = gRDFService->GetLiteral(aValue.get(), getter_AddRefs(literal));
  1.1325 +        if (NS_FAILED(rv)) 
  1.1326 +            return rv;
  1.1327 +        rv = CallQueryInterface(literal, aResult);
  1.1328 +    }
  1.1329 +    return rv;
  1.1330 +}
  1.1331 +
  1.1332 +nsresult
  1.1333 +nsXULTemplateQueryProcessorRDF::CompileTripleCondition(nsRDFQuery* aQuery,
  1.1334 +                                                       nsIContent* aCondition,
  1.1335 +                                                       TestNode* aParentNode,
  1.1336 +                                                       TestNode** aResult)
  1.1337 +{
  1.1338 +    // Compile a <triple> condition, which must be of the form:
  1.1339 +    //
  1.1340 +    //   <triple subject="?var1|resource"
  1.1341 +    //           predicate="resource"
  1.1342 +    //           object="?var2|resource|literal" />
  1.1343 +    //
  1.1344 +    // XXXwaterson Some day it would be cool to allow the 'predicate'
  1.1345 +    // to be bound to a variable.
  1.1346 +
  1.1347 +    // subject
  1.1348 +    nsAutoString subject;
  1.1349 +    aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::subject, subject);
  1.1350 +
  1.1351 +    nsCOMPtr<nsIAtom> svar;
  1.1352 +    nsCOMPtr<nsIRDFResource> sres;
  1.1353 +    if (subject.IsEmpty()) {
  1.1354 +        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_BAD_SUBJECT);
  1.1355 +        return NS_OK;
  1.1356 +    }
  1.1357 +    if (subject[0] == char16_t('?'))
  1.1358 +        svar = do_GetAtom(subject);
  1.1359 +    else
  1.1360 +        gRDFService->GetUnicodeResource(subject, getter_AddRefs(sres));
  1.1361 +
  1.1362 +    // predicate
  1.1363 +    nsAutoString predicate;
  1.1364 +    aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::predicate, predicate);
  1.1365 +
  1.1366 +    nsCOMPtr<nsIRDFResource> pres;
  1.1367 +    if (predicate.IsEmpty() || predicate[0] == char16_t('?')) {
  1.1368 +        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_BAD_PREDICATE);
  1.1369 +        return NS_OK;
  1.1370 +    }
  1.1371 +    gRDFService->GetUnicodeResource(predicate, getter_AddRefs(pres));
  1.1372 +
  1.1373 +    // object
  1.1374 +    nsAutoString object;
  1.1375 +    aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::object, object);
  1.1376 +
  1.1377 +    nsCOMPtr<nsIAtom> ovar;
  1.1378 +    nsCOMPtr<nsIRDFNode> onode;
  1.1379 +    if (object.IsEmpty()) {
  1.1380 +        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_BAD_OBJECT);
  1.1381 +        return NS_OK;
  1.1382 +    }
  1.1383 +
  1.1384 +    if (object[0] == char16_t('?')) {
  1.1385 +        ovar = do_GetAtom(object);
  1.1386 +    }
  1.1387 +    else if (object.FindChar(':') != -1) { // XXXwaterson evil.
  1.1388 +        // treat as resource
  1.1389 +        nsCOMPtr<nsIRDFResource> resource;
  1.1390 +        gRDFService->GetUnicodeResource(object, getter_AddRefs(resource));
  1.1391 +        onode = do_QueryInterface(resource);
  1.1392 +    }
  1.1393 +    else {
  1.1394 +        nsAutoString parseType;
  1.1395 +        aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::parsetype, parseType);
  1.1396 +        nsresult rv = ParseLiteral(parseType, object, getter_AddRefs(onode));
  1.1397 +        if (NS_FAILED(rv))
  1.1398 +            return rv;
  1.1399 +    }
  1.1400 +
  1.1401 +    nsRDFPropertyTestNode* testnode = nullptr;
  1.1402 +
  1.1403 +    if (svar && ovar) {
  1.1404 +        testnode = new nsRDFPropertyTestNode(aParentNode, this, svar, pres, ovar);
  1.1405 +    }
  1.1406 +    else if (svar) {
  1.1407 +        testnode = new nsRDFPropertyTestNode(aParentNode, this, svar, pres, onode);
  1.1408 +    }
  1.1409 +    else if (ovar) {
  1.1410 +        testnode = new nsRDFPropertyTestNode(aParentNode, this, sres, pres, ovar);
  1.1411 +    }
  1.1412 +    else {
  1.1413 +        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_NO_VAR);
  1.1414 +        return NS_OK;
  1.1415 +    }
  1.1416 +
  1.1417 +    if (! testnode)
  1.1418 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1419 +
  1.1420 +    // add testnode to mAllTests first. If adding to mRDFTests fails, just
  1.1421 +    // leave it in the list so that it can be deleted later.
  1.1422 +    nsresult rv = mAllTests.Add(testnode);
  1.1423 +    if (NS_FAILED(rv)) {
  1.1424 +        delete testnode;
  1.1425 +        return rv;
  1.1426 +    }
  1.1427 +
  1.1428 +    rv = mRDFTests.Add(testnode);
  1.1429 +    if (NS_FAILED(rv))
  1.1430 +        return rv;
  1.1431 +
  1.1432 +    *aResult = testnode;
  1.1433 +    return NS_OK;
  1.1434 +}
  1.1435 +
  1.1436 +nsresult
  1.1437 +nsXULTemplateQueryProcessorRDF::CompileMemberCondition(nsRDFQuery* aQuery,
  1.1438 +                                                       nsIContent* aCondition,
  1.1439 +                                                       TestNode* aParentNode,
  1.1440 +                                                       TestNode** aResult)
  1.1441 +{
  1.1442 +    // Compile a <member> condition, which must be of the form:
  1.1443 +    //
  1.1444 +    //   <member container="?var1" child="?var2" />
  1.1445 +    //
  1.1446 +
  1.1447 +    // container
  1.1448 +    nsAutoString container;
  1.1449 +    aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::container, container);
  1.1450 +
  1.1451 +    if (!container.IsEmpty() && container[0] != char16_t('?')) {
  1.1452 +        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_MEMBER_NOCONTAINERVAR);
  1.1453 +        return NS_OK;
  1.1454 +    }
  1.1455 +
  1.1456 +    nsCOMPtr<nsIAtom> containervar = do_GetAtom(container);
  1.1457 +
  1.1458 +    // child
  1.1459 +    nsAutoString child;
  1.1460 +    aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::child, child);
  1.1461 +
  1.1462 +    if (!child.IsEmpty() && child[0] != char16_t('?')) {
  1.1463 +        nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_MEMBER_NOCHILDVAR);
  1.1464 +        return NS_OK;
  1.1465 +    }
  1.1466 +
  1.1467 +    nsCOMPtr<nsIAtom> childvar = do_GetAtom(child);
  1.1468 +
  1.1469 +    TestNode* testnode =
  1.1470 +        new nsRDFConMemberTestNode(aParentNode,
  1.1471 +                                   this,
  1.1472 +                                   containervar,
  1.1473 +                                   childvar);
  1.1474 +
  1.1475 +    if (! testnode)
  1.1476 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1477 +
  1.1478 +    // add testnode to mAllTests first. If adding to mRDFTests fails, just
  1.1479 +    // leave it in the list so that it can be deleted later.
  1.1480 +    nsresult rv = mAllTests.Add(testnode);
  1.1481 +    if (NS_FAILED(rv)) {
  1.1482 +        delete testnode;
  1.1483 +        return rv;
  1.1484 +    }
  1.1485 +
  1.1486 +    rv = mRDFTests.Add(testnode);
  1.1487 +    if (NS_FAILED(rv))
  1.1488 +        return rv;
  1.1489 +
  1.1490 +    *aResult = testnode;
  1.1491 +    return NS_OK;
  1.1492 +}
  1.1493 +
  1.1494 +nsresult
  1.1495 +nsXULTemplateQueryProcessorRDF::AddDefaultSimpleRules(nsRDFQuery* aQuery,
  1.1496 +                                                      TestNode** aChildNode)
  1.1497 +{
  1.1498 +    // XXXndeakin should check for tag in query processor instead of builder?
  1.1499 +    nsContentTestNode* idnode =
  1.1500 +        new nsContentTestNode(this,
  1.1501 +                              aQuery->mRefVariable);
  1.1502 +    if (! idnode)
  1.1503 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1504 +
  1.1505 +    // Create (?container ^member ?member)
  1.1506 +    nsRDFConMemberTestNode* membernode =
  1.1507 +        new nsRDFConMemberTestNode(idnode,
  1.1508 +                                   this,
  1.1509 +                                   aQuery->mRefVariable,
  1.1510 +                                   aQuery->mMemberVariable);
  1.1511 +
  1.1512 +    if (! membernode) {
  1.1513 +        delete idnode;
  1.1514 +        return NS_ERROR_OUT_OF_MEMORY;
  1.1515 +    }
  1.1516 +
  1.1517 +    // add nodes to mAllTests first. If later calls fail, just leave them in
  1.1518 +    // the list so that they can be deleted later.
  1.1519 +    nsresult rv = mAllTests.Add(idnode);
  1.1520 +    if (NS_FAILED(rv)) {
  1.1521 +        delete idnode;
  1.1522 +        delete membernode;
  1.1523 +        return rv;
  1.1524 +    }
  1.1525 +
  1.1526 +    rv = mAllTests.Add(membernode);
  1.1527 +    if (NS_FAILED(rv)) {
  1.1528 +        delete membernode;
  1.1529 +        return rv;
  1.1530 +    }
  1.1531 +
  1.1532 +    rv = mRDFTests.Add(membernode);
  1.1533 +    if (NS_FAILED(rv))
  1.1534 +        return rv;
  1.1535 +
  1.1536 +    rv = idnode->AddChild(membernode);
  1.1537 +    if (NS_FAILED(rv))
  1.1538 +        return rv;
  1.1539 +
  1.1540 +    mSimpleRuleMemberTest = membernode;
  1.1541 +    *aChildNode = membernode;
  1.1542 +
  1.1543 +    return NS_OK;
  1.1544 +}
  1.1545 +
  1.1546 +nsresult
  1.1547 +nsXULTemplateQueryProcessorRDF::CompileSimpleQuery(nsRDFQuery* aQuery,
  1.1548 +                                                   nsIContent* aQueryElement,
  1.1549 +                                                   TestNode** aLastNode)
  1.1550 +{
  1.1551 +    // Compile a "simple" (or old-school style) <template> query.
  1.1552 +    nsresult rv;
  1.1553 +
  1.1554 +    TestNode* parentNode;
  1.1555 +
  1.1556 +    if (! mSimpleRuleMemberTest) {
  1.1557 +        rv = AddDefaultSimpleRules(aQuery, &parentNode);
  1.1558 +        if (NS_FAILED(rv))
  1.1559 +            return rv;
  1.1560 +    }
  1.1561 +
  1.1562 +    bool hasContainerTest = false;
  1.1563 +
  1.1564 +    TestNode* prevnode = mSimpleRuleMemberTest;
  1.1565 +
  1.1566 +    // Add constraints for the LHS
  1.1567 +    const nsAttrName* name;
  1.1568 +    for (uint32_t i = 0; (name = aQueryElement->GetAttrNameAt(i)); ++i) {
  1.1569 +        // Note: some attributes must be skipped on XUL template query subtree
  1.1570 +
  1.1571 +        // never compare against rdf:property, rdf:instanceOf, {}:id or {}:parsetype attribute
  1.1572 +        if (name->Equals(nsGkAtoms::property, kNameSpaceID_RDF) ||
  1.1573 +            name->Equals(nsGkAtoms::instanceOf, kNameSpaceID_RDF) ||
  1.1574 +            name->Equals(nsGkAtoms::id, kNameSpaceID_None) ||
  1.1575 +            name->Equals(nsGkAtoms::parsetype, kNameSpaceID_None)) {
  1.1576 +            continue;
  1.1577 +        }
  1.1578 +
  1.1579 +        int32_t attrNameSpaceID = name->NamespaceID();
  1.1580 +        if (attrNameSpaceID == kNameSpaceID_XMLNS)
  1.1581 +          continue;
  1.1582 +        nsIAtom* attr = name->LocalName();
  1.1583 +
  1.1584 +        nsAutoString value;
  1.1585 +        aQueryElement->GetAttr(attrNameSpaceID, attr, value);
  1.1586 +
  1.1587 +        TestNode* testnode = nullptr;
  1.1588 +
  1.1589 +        if (name->Equals(nsGkAtoms::iscontainer, kNameSpaceID_None) ||
  1.1590 +            name->Equals(nsGkAtoms::isempty, kNameSpaceID_None)) {
  1.1591 +            // Tests about containerhood and emptiness. These can be
  1.1592 +            // globbed together, mostly. Check to see if we've already
  1.1593 +            // added a container test: we only need one.
  1.1594 +            if (hasContainerTest)
  1.1595 +                continue;
  1.1596 +
  1.1597 +            nsRDFConInstanceTestNode::Test iscontainer =
  1.1598 +                nsRDFConInstanceTestNode::eDontCare;
  1.1599 +
  1.1600 +            static nsIContent::AttrValuesArray strings[] =
  1.1601 +              {&nsGkAtoms::_true, &nsGkAtoms::_false, nullptr};
  1.1602 +            switch (aQueryElement->FindAttrValueIn(kNameSpaceID_None,
  1.1603 +                                                   nsGkAtoms::iscontainer,
  1.1604 +                                                   strings, eCaseMatters)) {
  1.1605 +                case 0: iscontainer = nsRDFConInstanceTestNode::eTrue; break;
  1.1606 +                case 1: iscontainer = nsRDFConInstanceTestNode::eFalse; break;
  1.1607 +            }
  1.1608 +
  1.1609 +            nsRDFConInstanceTestNode::Test isempty =
  1.1610 +                nsRDFConInstanceTestNode::eDontCare;
  1.1611 +
  1.1612 +            switch (aQueryElement->FindAttrValueIn(kNameSpaceID_None,
  1.1613 +                                                   nsGkAtoms::isempty,
  1.1614 +                                                   strings, eCaseMatters)) {
  1.1615 +                case 0: isempty = nsRDFConInstanceTestNode::eTrue; break;
  1.1616 +                case 1: isempty = nsRDFConInstanceTestNode::eFalse; break;
  1.1617 +            }
  1.1618 +
  1.1619 +            testnode = new nsRDFConInstanceTestNode(prevnode,
  1.1620 +                                                    this,
  1.1621 +                                                    aQuery->mMemberVariable,
  1.1622 +                                                    iscontainer,
  1.1623 +                                                    isempty);
  1.1624 +
  1.1625 +            if (! testnode)
  1.1626 +                return NS_ERROR_OUT_OF_MEMORY;
  1.1627 +
  1.1628 +            rv = mAllTests.Add(testnode);
  1.1629 +            if (NS_FAILED(rv)) {
  1.1630 +                delete testnode;
  1.1631 +                return rv;
  1.1632 +            }
  1.1633 +
  1.1634 +            rv = mRDFTests.Add(testnode);
  1.1635 +            if (NS_FAILED(rv))
  1.1636 +                return rv;
  1.1637 +        }
  1.1638 +        else if (attrNameSpaceID != kNameSpaceID_None || attr != nsGkAtoms::parent) {
  1.1639 +            // It's a simple RDF test
  1.1640 +            nsCOMPtr<nsIRDFResource> property;
  1.1641 +            rv = nsXULContentUtils::GetResource(attrNameSpaceID, attr, getter_AddRefs(property));
  1.1642 +            if (NS_FAILED(rv))
  1.1643 +                return rv;
  1.1644 +
  1.1645 +            // XXXwaterson this is so manky
  1.1646 +            nsCOMPtr<nsIRDFNode> target;
  1.1647 +            if (value.FindChar(':') != -1) { // XXXwaterson WRONG WRONG WRONG!
  1.1648 +                nsCOMPtr<nsIRDFResource> resource;
  1.1649 +                rv = gRDFService->GetUnicodeResource(value, getter_AddRefs(resource));
  1.1650 +                if (NS_FAILED(rv))
  1.1651 +                    return rv;
  1.1652 +
  1.1653 +                target = do_QueryInterface(resource);
  1.1654 +            }
  1.1655 +            else {                
  1.1656 +              nsAutoString parseType;
  1.1657 +              aQueryElement->GetAttr(kNameSpaceID_None, nsGkAtoms::parsetype, parseType);
  1.1658 +              rv = ParseLiteral(parseType, value, getter_AddRefs(target));
  1.1659 +              if (NS_FAILED(rv))
  1.1660 +                  return rv;
  1.1661 +            }
  1.1662 +
  1.1663 +            testnode = new nsRDFPropertyTestNode(prevnode, this,
  1.1664 +                                                 aQuery->mMemberVariable, property, target);
  1.1665 +            if (! testnode)
  1.1666 +                return NS_ERROR_OUT_OF_MEMORY;
  1.1667 +
  1.1668 +            rv = mAllTests.Add(testnode);
  1.1669 +            if (NS_FAILED(rv)) {
  1.1670 +                delete testnode;
  1.1671 +                return rv;
  1.1672 +            }
  1.1673 +
  1.1674 +            rv = mRDFTests.Add(testnode);
  1.1675 +            if (NS_FAILED(rv))
  1.1676 +                return rv;
  1.1677 +        }
  1.1678 +
  1.1679 +        if (testnode) {
  1.1680 +            if (prevnode) {
  1.1681 +                rv = prevnode->AddChild(testnode);
  1.1682 +                if (NS_FAILED(rv))
  1.1683 +                    return rv;
  1.1684 +            }                
  1.1685 +            else {
  1.1686 +                aQuery->SetRoot(testnode);
  1.1687 +            }
  1.1688 +
  1.1689 +            prevnode = testnode;
  1.1690 +        }
  1.1691 +    }
  1.1692 +
  1.1693 +    *aLastNode = prevnode;
  1.1694 +
  1.1695 +    return NS_OK;
  1.1696 +}
  1.1697 +
  1.1698 +RDFBindingSet*
  1.1699 +nsXULTemplateQueryProcessorRDF::GetBindingsForRule(nsIDOMNode* aRuleNode)
  1.1700 +{
  1.1701 +    return mRuleToBindingsMap.GetWeak(aRuleNode);
  1.1702 +}
  1.1703 +
  1.1704 +void
  1.1705 +nsXULTemplateQueryProcessorRDF::AddBindingDependency(nsXULTemplateResultRDF* aResult,
  1.1706 +                                                     nsIRDFResource* aResource)
  1.1707 +{
  1.1708 +    ResultArray* arr;
  1.1709 +    if (!mBindingDependencies.Get(aResource, &arr)) {
  1.1710 +        arr = new ResultArray();
  1.1711 +
  1.1712 +        mBindingDependencies.Put(aResource, arr);
  1.1713 +    }
  1.1714 +
  1.1715 +    int32_t index = arr->IndexOf(aResult);
  1.1716 +    if (index == -1)
  1.1717 +        arr->AppendElement(aResult);
  1.1718 +}
  1.1719 +
  1.1720 +void
  1.1721 +nsXULTemplateQueryProcessorRDF::RemoveBindingDependency(nsXULTemplateResultRDF* aResult,
  1.1722 +                                                        nsIRDFResource* aResource)
  1.1723 +{
  1.1724 +    ResultArray* arr;
  1.1725 +    if (mBindingDependencies.Get(aResource, &arr)) {
  1.1726 +        int32_t index = arr->IndexOf(aResult);
  1.1727 +        if (index >= 0)
  1.1728 +            arr->RemoveElementAt(index);
  1.1729 +    }
  1.1730 +}
  1.1731 +
  1.1732 +
  1.1733 +nsresult
  1.1734 +nsXULTemplateQueryProcessorRDF::AddMemoryElements(const Instantiation& aInst,
  1.1735 +                                                  nsXULTemplateResultRDF* aResult)
  1.1736 +{
  1.1737 +    // Add the result to a table indexed by supporting MemoryElement
  1.1738 +    MemoryElementSet::ConstIterator last = aInst.mSupport.Last();
  1.1739 +    for (MemoryElementSet::ConstIterator element = aInst.mSupport.First();
  1.1740 +                                         element != last; ++element) {
  1.1741 +
  1.1742 +        PLHashNumber hash = (element.operator->())->Hash();
  1.1743 +
  1.1744 +        nsCOMArray<nsXULTemplateResultRDF>* arr;
  1.1745 +        if (!mMemoryElementToResultMap.Get(hash, &arr)) {
  1.1746 +            arr = new nsCOMArray<nsXULTemplateResultRDF>();
  1.1747 +            if (!arr)
  1.1748 +                return NS_ERROR_OUT_OF_MEMORY;
  1.1749 +
  1.1750 +            mMemoryElementToResultMap.Put(hash, arr);
  1.1751 +        }
  1.1752 +
  1.1753 +        // results may be added more than once so they will all get deleted properly
  1.1754 +        arr->AppendObject(aResult);
  1.1755 +    }
  1.1756 +
  1.1757 +    return NS_OK;
  1.1758 +}
  1.1759 +
  1.1760 +nsresult
  1.1761 +nsXULTemplateQueryProcessorRDF::RemoveMemoryElements(const Instantiation& aInst,
  1.1762 +                                                     nsXULTemplateResultRDF* aResult)
  1.1763 +{
  1.1764 +    // Remove the results mapped by the supporting MemoryElement
  1.1765 +    MemoryElementSet::ConstIterator last = aInst.mSupport.Last();
  1.1766 +    for (MemoryElementSet::ConstIterator element = aInst.mSupport.First();
  1.1767 +                                         element != last; ++element) {
  1.1768 +
  1.1769 +        PLHashNumber hash = (element.operator->())->Hash();
  1.1770 +
  1.1771 +        nsCOMArray<nsXULTemplateResultRDF>* arr;
  1.1772 +        if (mMemoryElementToResultMap.Get(hash, &arr)) {
  1.1773 +            int32_t index = arr->IndexOf(aResult);
  1.1774 +            if (index >= 0)
  1.1775 +                arr->RemoveObjectAt(index);
  1.1776 +
  1.1777 +            uint32_t length = arr->Count();
  1.1778 +            if (! length)
  1.1779 +                mMemoryElementToResultMap.Remove(hash);
  1.1780 +        }
  1.1781 +    }
  1.1782 +
  1.1783 +    return NS_OK;
  1.1784 +}
  1.1785 +
  1.1786 +void
  1.1787 +nsXULTemplateQueryProcessorRDF::RetractElement(const MemoryElement& aMemoryElement)
  1.1788 +{
  1.1789 +    if (! mBuilder)
  1.1790 +        return;
  1.1791 +
  1.1792 +    // when an assertion is removed, look through the memory elements and
  1.1793 +    // find results that are associated with them. Those results will need
  1.1794 +    // to be removed because they no longer match.
  1.1795 +    PLHashNumber hash = aMemoryElement.Hash();
  1.1796 +
  1.1797 +    nsCOMArray<nsXULTemplateResultRDF>* arr;
  1.1798 +    if (mMemoryElementToResultMap.Get(hash, &arr)) {
  1.1799 +        uint32_t length = arr->Count();
  1.1800 +
  1.1801 +        for (int32_t r = length - 1; r >= 0; r--) {
  1.1802 +            nsXULTemplateResultRDF* result = (*arr)[r];
  1.1803 +            if (result) {
  1.1804 +                // because the memory elements are hashed by an integer,
  1.1805 +                // sometimes two different memory elements will have the same
  1.1806 +                // hash code. In this case we check the result to make sure
  1.1807 +                // and only remove those that refer to that memory element.
  1.1808 +                if (result->HasMemoryElement(aMemoryElement)) {
  1.1809 +                    nsITemplateRDFQuery* query = result->Query();
  1.1810 +                    if (query) {
  1.1811 +                        nsCOMPtr<nsIDOMNode> querynode;
  1.1812 +                        query->GetQueryNode(getter_AddRefs(querynode));
  1.1813 +
  1.1814 +                        mBuilder->RemoveResult(result);
  1.1815 +                    }
  1.1816 +
  1.1817 +                    // a call to RemoveMemoryElements may have removed it
  1.1818 +                    if (!mMemoryElementToResultMap.Get(hash, nullptr))
  1.1819 +                        return;
  1.1820 +
  1.1821 +                    // the array should have been reduced by one, but check
  1.1822 +                    // just to make sure
  1.1823 +                    uint32_t newlength = arr->Count();
  1.1824 +                    if (r > (int32_t)newlength)
  1.1825 +                        r = newlength;
  1.1826 +                }
  1.1827 +            }
  1.1828 +        }
  1.1829 +
  1.1830 +        // if there are no items left, remove the memory element from the hashtable
  1.1831 +        if (!arr->Count())
  1.1832 +            mMemoryElementToResultMap.Remove(hash);
  1.1833 +    }
  1.1834 +}
  1.1835 +
  1.1836 +int32_t
  1.1837 +nsXULTemplateQueryProcessorRDF::GetContainerIndexOf(nsIXULTemplateResult* aResult)
  1.1838 +{
  1.1839 +    // get the reference variable and look up the container in the result
  1.1840 +    nsCOMPtr<nsISupports> ref;
  1.1841 +    nsresult rv = aResult->GetBindingObjectFor(mRefVariable,
  1.1842 +                                               getter_AddRefs(ref));
  1.1843 +    if (NS_FAILED(rv) || !mDB)
  1.1844 +        return -1;
  1.1845 +
  1.1846 +    nsCOMPtr<nsIRDFResource> container = do_QueryInterface(ref);
  1.1847 +    if (container) {
  1.1848 +        // if the container is an RDF Seq, return the index of the result
  1.1849 +        // in the container.
  1.1850 +        bool isSequence = false;
  1.1851 +        gRDFContainerUtils->IsSeq(mDB, container, &isSequence);
  1.1852 +        if (isSequence) {
  1.1853 +            nsCOMPtr<nsIRDFResource> resource;
  1.1854 +            aResult->GetResource(getter_AddRefs(resource));
  1.1855 +            if (resource) {
  1.1856 +                int32_t index;
  1.1857 +                gRDFContainerUtils->IndexOf(mDB, container, resource, &index);
  1.1858 +                return index;
  1.1859 +            }
  1.1860 +        }
  1.1861 +    }
  1.1862 +
  1.1863 +    // if the container isn't a Seq, or the result isn't in the container,
  1.1864 +    // return -1 indicating no index.
  1.1865 +    return -1;
  1.1866 +}
  1.1867 +
  1.1868 +nsresult
  1.1869 +nsXULTemplateQueryProcessorRDF::GetSortValue(nsIXULTemplateResult* aResult,
  1.1870 +                                             nsIRDFResource* aPredicate,
  1.1871 +                                             nsIRDFResource* aSortPredicate,
  1.1872 +                                             nsISupports** aResultNode)
  1.1873 +{
  1.1874 +    nsCOMPtr<nsIRDFResource> source;
  1.1875 +    nsresult rv = aResult->GetResource(getter_AddRefs(source));
  1.1876 +    if (NS_FAILED(rv))
  1.1877 +        return rv;
  1.1878 +    
  1.1879 +    nsCOMPtr<nsIRDFNode> value;
  1.1880 +    if (source && mDB) {
  1.1881 +        // first check predicate?sort=true so that datasources may use a
  1.1882 +        // custom value for sorting
  1.1883 +        rv = mDB->GetTarget(source, aSortPredicate, true,
  1.1884 +                            getter_AddRefs(value));
  1.1885 +        if (NS_FAILED(rv))
  1.1886 +            return rv;
  1.1887 +
  1.1888 +        if (!value) {
  1.1889 +            rv = mDB->GetTarget(source, aPredicate, true,
  1.1890 +                                getter_AddRefs(value));
  1.1891 +            if (NS_FAILED(rv))
  1.1892 +                return rv;
  1.1893 +        }
  1.1894 +    }
  1.1895 +
  1.1896 +    *aResultNode = value;
  1.1897 +    NS_IF_ADDREF(*aResultNode);
  1.1898 +    return NS_OK;
  1.1899 +}

mercurial