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