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