content/xul/templates/src/nsXULTemplateQueryProcessorRDF.cpp

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

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

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

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

mercurial