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

mercurial