123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830 |
- /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "nsCOMPtr.h"
- #include "nsICollation.h"
- #include "nsIDOMNode.h"
- #include "nsIRDFNode.h"
- #include "nsIRDFObserver.h"
- #include "nsIRDFRemoteDataSource.h"
- #include "nsIRDFInferDataSource.h"
- #include "nsIRDFService.h"
- #include "nsRDFCID.h"
- #include "nsIServiceManager.h"
- #include "nsNameSpaceManager.h"
- #include "nsGkAtoms.h"
- #include "nsIDOMDocument.h"
- #include "nsAttrName.h"
- #include "rdf.h"
- #include "nsArrayUtils.h"
- #include "nsIURI.h"
- #include "nsContentTestNode.h"
- #include "nsRDFConInstanceTestNode.h"
- #include "nsRDFConMemberTestNode.h"
- #include "nsRDFPropertyTestNode.h"
- #include "nsInstantiationNode.h"
- #include "nsRDFTestNode.h"
- #include "nsXULContentUtils.h"
- #include "nsXULTemplateBuilder.h"
- #include "nsXULTemplateResultRDF.h"
- #include "nsXULTemplateResultSetRDF.h"
- #include "nsXULTemplateQueryProcessorRDF.h"
- #include "nsXULSortService.h"
- #include "nsIDocument.h"
- #include "mozilla/Logging.h"
- using namespace mozilla;
- //----------------------------------------------------------------------
- #define PARSE_TYPE_INTEGER "Integer"
- nsrefcnt nsXULTemplateQueryProcessorRDF::gRefCnt = 0;
- nsIRDFService* nsXULTemplateQueryProcessorRDF::gRDFService;
- nsIRDFContainerUtils* nsXULTemplateQueryProcessorRDF::gRDFContainerUtils;
- nsIRDFResource* nsXULTemplateQueryProcessorRDF::kNC_BookmarkSeparator;
- nsIRDFResource* nsXULTemplateQueryProcessorRDF::kRDF_type;
- NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULTemplateQueryProcessorRDF)
- NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULTemplateQueryProcessorRDF)
- tmp->Done();
- NS_IMPL_CYCLE_COLLECTION_UNLINK_END
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULTemplateQueryProcessorRDF)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDB)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLastRef)
- for (auto it = tmp->mBindingDependencies.Iter(); !it.Done(); it.Next()) {
- nsXULTemplateQueryProcessorRDF::ResultArray* array = it.UserData();
- int32_t count = array->Length();
- for (int32_t i = 0; i < count; ++i) {
- cb.NoteXPCOMChild(array->ElementAt(i));
- }
- }
- for (auto it = tmp->mMemoryElementToResultMap.Iter();
- !it.Done();
- it.Next()) {
- nsCOMArray<nsXULTemplateResultRDF>* array = it.UserData();
- int32_t count = array->Count();
- for (int32_t i = 0; i < count; ++i) {
- cb.NoteXPCOMChild(array->ObjectAt(i));
- }
- }
- for (auto it = tmp->mRuleToBindingsMap.Iter(); !it.Done(); it.Next()) {
- cb.NoteXPCOMChild(it.Key());
- }
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueries)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
- NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULTemplateQueryProcessorRDF)
- NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULTemplateQueryProcessorRDF)
- NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULTemplateQueryProcessorRDF)
- NS_INTERFACE_MAP_ENTRY(nsIXULTemplateQueryProcessor)
- NS_INTERFACE_MAP_ENTRY(nsIRDFObserver)
- NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULTemplateQueryProcessor)
- NS_INTERFACE_MAP_END
- nsXULTemplateQueryProcessorRDF::nsXULTemplateQueryProcessorRDF(void)
- : mDB(nullptr),
- mBuilder(nullptr),
- mQueryProcessorRDFInited(false),
- mGenerationStarted(false),
- mUpdateBatchNest(0),
- mSimpleRuleMemberTest(nullptr)
- {
- gRefCnt++;
- }
- nsXULTemplateQueryProcessorRDF::~nsXULTemplateQueryProcessorRDF(void)
- {
- if (--gRefCnt == 0) {
- NS_IF_RELEASE(gRDFService);
- NS_IF_RELEASE(gRDFContainerUtils);
- NS_IF_RELEASE(kNC_BookmarkSeparator);
- NS_IF_RELEASE(kRDF_type);
- }
- }
- nsresult
- nsXULTemplateQueryProcessorRDF::InitGlobals()
- {
- nsresult rv;
- // Initialize the global shared reference to the service
- // manager and get some shared resource objects.
- if (!gRDFService) {
- NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
- rv = CallGetService(kRDFServiceCID, &gRDFService);
- if (NS_FAILED(rv))
- return rv;
- }
- if (!gRDFContainerUtils) {
- NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
- rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils);
- if (NS_FAILED(rv))
- return rv;
- }
-
- if (!kNC_BookmarkSeparator) {
- gRDFService->GetResource(
- NS_LITERAL_CSTRING(NC_NAMESPACE_URI "BookmarkSeparator"),
- &kNC_BookmarkSeparator);
- }
- if (!kRDF_type) {
- gRDFService->GetResource(
- NS_LITERAL_CSTRING(RDF_NAMESPACE_URI "type"),
- &kRDF_type);
- }
- return NS_OK;
- }
- //----------------------------------------------------------------------
- //
- // nsIXULTemplateQueryProcessor interface
- //
- NS_IMETHODIMP
- nsXULTemplateQueryProcessorRDF::GetDatasource(nsIArray* aDataSources,
- nsIDOMNode* aRootNode,
- bool aIsTrusted,
- nsIXULTemplateBuilder* aBuilder,
- bool* aShouldDelayBuilding,
- nsISupports** aResult)
- {
- nsCOMPtr<nsIRDFCompositeDataSource> compDB;
- nsCOMPtr<nsIContent> root = do_QueryInterface(aRootNode);
- nsresult rv;
- *aResult = nullptr;
- *aShouldDelayBuilding = false;
- NS_ENSURE_TRUE(root, NS_ERROR_UNEXPECTED);
- // make sure the RDF service is set up
- rv = InitGlobals();
- NS_ENSURE_SUCCESS(rv, rv);
- // create a database for the builder
- compDB = do_CreateInstance(NS_RDF_DATASOURCE_CONTRACTID_PREFIX
- "composite-datasource");
- if (!compDB) {
- NS_ERROR("unable to construct new composite data source");
- return NS_ERROR_UNEXPECTED;
- }
- // check for magical attributes. XXX move to ``flags''?
- if (root->AttrValueIs(kNameSpaceID_None,
- nsGkAtoms::coalesceduplicatearcs,
- nsGkAtoms::_false, eCaseMatters))
- compDB->SetCoalesceDuplicateArcs(false);
- if (root->AttrValueIs(kNameSpaceID_None,
- nsGkAtoms::allownegativeassertions,
- nsGkAtoms::_false, eCaseMatters))
- compDB->SetAllowNegativeAssertions(false);
- if (aIsTrusted) {
- // If we're a privileged (e.g., chrome) document, then add the
- // local store as the first data source in the db. Note that
- // we _might_ not be able to get a local store if we haven't
- // got a profile to read from yet.
- nsCOMPtr<nsIRDFDataSource> localstore;
- rv = gRDFService->GetDataSource("rdf:local-store",
- getter_AddRefs(localstore));
- NS_ENSURE_SUCCESS(rv, rv);
- rv = compDB->AddDataSource(localstore);
- NS_ASSERTION(NS_SUCCEEDED(rv), "unable to add local store to db");
- NS_ENSURE_SUCCESS(rv, rv);
- }
- uint32_t length, index;
- rv = aDataSources->GetLength(&length);
- NS_ENSURE_SUCCESS(rv,rv);
- for (index = 0; index < length; index++) {
- nsCOMPtr<nsIURI> uri = do_QueryElementAt(aDataSources, index);
- if (!uri) // we ignore other datasources than uri
- continue;
- nsCOMPtr<nsIRDFDataSource> ds;
- nsAutoCString uristrC;
- uri->GetSpec(uristrC);
- rv = gRDFService->GetDataSource(uristrC.get(), getter_AddRefs(ds));
- if (NS_FAILED(rv)) {
- // This is only a warning because the data source may not
- // be accessible for any number of reasons, including
- // security, a bad URL, etc.
- #ifdef DEBUG
- nsAutoCString msg;
- msg.AppendLiteral("unable to load datasource '");
- msg.Append(uristrC);
- msg.Append('\'');
- NS_WARNING(msg.get());
- #endif
- continue;
- }
- compDB->AddDataSource(ds);
- }
- // check if we were given an inference engine type
- nsAutoString infer;
- nsCOMPtr<nsIRDFDataSource> db;
- root->GetAttr(kNameSpaceID_None, nsGkAtoms::infer, infer);
- if (!infer.IsEmpty()) {
- nsCString inferCID(NS_RDF_INFER_DATASOURCE_CONTRACTID_PREFIX);
- AppendUTF16toUTF8(infer, inferCID);
- nsCOMPtr<nsIRDFInferDataSource> inferDB =
- do_CreateInstance(inferCID.get());
- if (inferDB) {
- inferDB->SetBaseDataSource(compDB);
- db = do_QueryInterface(inferDB);
- }
- else {
- NS_WARNING("failed to construct inference engine specified on template");
- }
- }
- if (!db)
- db = compDB;
- return CallQueryInterface(db, aResult);
- }
- NS_IMETHODIMP
- nsXULTemplateQueryProcessorRDF::InitializeForBuilding(nsISupports* aDatasource,
- nsIXULTemplateBuilder* aBuilder,
- nsIDOMNode* aRootNode)
- {
- if (!mQueryProcessorRDFInited) {
- nsresult rv = InitGlobals();
- if (NS_FAILED(rv))
- return rv;
- mQueryProcessorRDFInited = true;
- }
- // don't do anything if generation has already been done
- if (mGenerationStarted)
- return NS_ERROR_UNEXPECTED;
- mDB = do_QueryInterface(aDatasource);
- mBuilder = aBuilder;
- ComputeContainmentProperties(aRootNode);
- // Add ourselves as a datasource observer
- if (mDB)
- mDB->AddObserver(this);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsXULTemplateQueryProcessorRDF::Done()
- {
- if (!mQueryProcessorRDFInited)
- return NS_OK;
- if (mDB)
- mDB->RemoveObserver(this);
- mDB = nullptr;
- mBuilder = nullptr;
- mRefVariable = nullptr;
- mLastRef = nullptr;
- mGenerationStarted = false;
- mUpdateBatchNest = 0;
- mContainmentProperties.Clear();
- for (ReteNodeSet::Iterator node = mAllTests.First();
- node != mAllTests.Last(); ++node)
- delete *node;
- mAllTests.Clear();
- mRDFTests.Clear();
- mQueries.Clear();
- mSimpleRuleMemberTest = nullptr;
- mBindingDependencies.Clear();
- mMemoryElementToResultMap.Clear();
- mRuleToBindingsMap.Clear();
- return NS_OK;
- }
- NS_IMETHODIMP
- nsXULTemplateQueryProcessorRDF::CompileQuery(nsIXULTemplateBuilder* aBuilder,
- nsIDOMNode* aQueryNode,
- nsIAtom* aRefVariable,
- nsIAtom* aMemberVariable,
- nsISupports** _retval)
- {
- RefPtr<nsRDFQuery> query = new nsRDFQuery(this);
- if (!query)
- return NS_ERROR_OUT_OF_MEMORY;
- query->mRefVariable = aRefVariable;
- if (!mRefVariable)
- mRefVariable = aRefVariable;
- if (!aMemberVariable)
- query->mMemberVariable = NS_Atomize("?");
- else
- query->mMemberVariable = aMemberVariable;
- nsresult rv;
- TestNode *lastnode = nullptr;
- nsCOMPtr<nsIContent> content = do_QueryInterface(aQueryNode);
- if (content->NodeInfo()->Equals(nsGkAtoms::_template, kNameSpaceID_XUL)) {
- // simplified syntax with no rules
- query->SetSimple();
- NS_ASSERTION(!mSimpleRuleMemberTest,
- "CompileQuery called twice with the same template");
- if (!mSimpleRuleMemberTest)
- rv = CompileSimpleQuery(query, content, &lastnode);
- else
- rv = NS_ERROR_FAILURE;
- }
- else if (content->NodeInfo()->Equals(nsGkAtoms::rule, kNameSpaceID_XUL)) {
- // simplified syntax with at least one rule
- query->SetSimple();
- rv = CompileSimpleQuery(query, content, &lastnode);
- }
- else {
- rv = CompileExtendedQuery(query, content, &lastnode);
- }
- if (NS_FAILED(rv))
- return rv;
- query->SetQueryNode(aQueryNode);
- nsInstantiationNode* instnode = new nsInstantiationNode(this, query);
- // this and other functions always add nodes to mAllTests first. That
- // way if something fails, the node will just sit harmlessly in mAllTests
- // where it can be deleted later.
- rv = mAllTests.Add(instnode);
- if (NS_FAILED(rv)) {
- delete instnode;
- return rv;
- }
- rv = lastnode->AddChild(instnode);
- if (NS_FAILED(rv))
- return rv;
- mQueries.AppendElement(query);
- query.forget(_retval);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsXULTemplateQueryProcessorRDF::GenerateResults(nsISupports* aDatasource,
- nsIXULTemplateResult* aRef,
- nsISupports* aQuery,
- nsISimpleEnumerator** aResults)
- {
- nsCOMPtr<nsITemplateRDFQuery> rdfquery = do_QueryInterface(aQuery);
- if (! rdfquery)
- return NS_ERROR_INVALID_ARG;
- mGenerationStarted = true;
- // should be safe to cast here since the query is a
- // non-scriptable nsITemplateRDFQuery
- nsRDFQuery* query = static_cast<nsRDFQuery *>(aQuery);
- *aResults = nullptr;
- nsCOMPtr<nsISimpleEnumerator> results;
- if (aRef) {
- // make sure that cached results were generated for this ref, and if not,
- // regenerate them. Otherwise, things will go wrong for templates bound to
- // an HTML element as they are not generated lazily.
- if (aRef == mLastRef) {
- query->UseCachedResults(getter_AddRefs(results));
- }
- else {
- // clear the cached results
- int32_t count = mQueries.Length();
- for (int32_t r = 0; r < count; r++) {
- mQueries[r]->ClearCachedResults();
- }
- }
- if (! results) {
- if (! query->mRefVariable)
- query->mRefVariable = NS_Atomize("?uri");
- nsCOMPtr<nsIRDFResource> refResource;
- aRef->GetResource(getter_AddRefs(refResource));
- if (! refResource)
- return NS_ERROR_FAILURE;
- // Propagate the assignments through the network
- TestNode* root = query->GetRoot();
- if (query->IsSimple() && mSimpleRuleMemberTest) {
- // get the top node in the simple rule tree
- root = mSimpleRuleMemberTest->GetParent();
- mLastRef = aRef;
- }
- if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) {
- nsAutoString id;
- aRef->GetId(id);
- nsAutoString rvar;
- query->mRefVariable->ToString(rvar);
- nsAutoString mvar;
- query->mMemberVariable->ToString(mvar);
- MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
- ("QueryProcessor::GenerateResults using ref %s and vars [ ref: %s member: %s]",
- NS_ConvertUTF16toUTF8(id).get(),
- NS_ConvertUTF16toUTF8(rvar).get(),
- NS_ConvertUTF16toUTF8(mvar).get()));
- }
- if (root) {
- // the seed is the initial instantiation, which has a single
- // assignment holding the reference point
- Instantiation seed;
- seed.AddAssignment(query->mRefVariable, refResource);
- InstantiationSet* instantiations = new InstantiationSet();
- instantiations->Append(seed);
- // if the propagation caused a match, then the results will be
- // cached in the query, retrieved below by calling
- // UseCachedResults. The matching result set owns the
- // instantiations and will delete them when results have been
- // iterated over. If the propagation did not match, the
- // instantiations need to be deleted.
- bool owned = false;
- nsresult rv = root->Propagate(*instantiations, false, owned);
- if (! owned)
- delete instantiations;
- if (NS_FAILED(rv))
- return rv;
- query->UseCachedResults(getter_AddRefs(results));
- }
- }
- }
- if (! results) {
- // no results were found so create an empty set
- results = new nsXULTemplateResultSetRDF(this, query, nullptr);
- }
- results.swap(*aResults);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsXULTemplateQueryProcessorRDF::AddBinding(nsIDOMNode* aRuleNode,
- nsIAtom* aVar,
- nsIAtom* aRef,
- const nsAString& aExpr)
- {
- // add a <binding> to a rule. When a result is matched, the bindings are
- // examined to add additional variable assignments
- // bindings can't be added once result generation has started, otherwise
- // the array sizes will get out of sync
- if (mGenerationStarted)
- return NS_ERROR_UNEXPECTED;
- nsCOMPtr<nsIRDFResource> property;
- nsresult rv = gRDFService->GetUnicodeResource(aExpr, getter_AddRefs(property));
- if (NS_FAILED(rv))
- return rv;
- RefPtr<RDFBindingSet> bindings = mRuleToBindingsMap.GetWeak(aRuleNode);
- if (!bindings) {
- bindings = new RDFBindingSet();
- mRuleToBindingsMap.Put(aRuleNode, bindings);
- }
- return bindings->AddBinding(aVar, aRef, property);
- }
- NS_IMETHODIMP
- nsXULTemplateQueryProcessorRDF::TranslateRef(nsISupports* aDatasource,
- const nsAString& aRefString,
- nsIXULTemplateResult** aRef)
- {
- // make sure the RDF service is set up
- nsresult rv = InitGlobals();
- if (NS_FAILED(rv))
- return rv;
- nsCOMPtr<nsIRDFResource> uri;
- gRDFService->GetUnicodeResource(aRefString, getter_AddRefs(uri));
- RefPtr<nsXULTemplateResultRDF> refresult = new nsXULTemplateResultRDF(uri);
- if (! refresult)
- return NS_ERROR_OUT_OF_MEMORY;
- refresult.forget(aRef);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsXULTemplateQueryProcessorRDF::CompareResults(nsIXULTemplateResult* aLeft,
- nsIXULTemplateResult* aRight,
- nsIAtom* aVar,
- uint32_t aSortHints,
- int32_t* aResult)
- {
- NS_ENSURE_ARG_POINTER(aLeft);
- NS_ENSURE_ARG_POINTER(aRight);
- *aResult = 0;
- // for natural order sorting, use the index in the RDF container for the
- // order. If there is no container, just sort them arbitrarily.
- if (!aVar) {
- // if a result has a negative index, just sort it first
- int32_t leftindex = GetContainerIndexOf(aLeft);
- int32_t rightindex = GetContainerIndexOf(aRight);
- *aResult = leftindex == rightindex ? 0 :
- leftindex > rightindex ? 1 :
- -1;
- return NS_OK;
- }
- nsDependentAtomString sortkey(aVar);
- nsCOMPtr<nsISupports> leftNode, rightNode;
- if (!sortkey.IsEmpty() && sortkey[0] != '?' &&
- !StringBeginsWith(sortkey, NS_LITERAL_STRING("rdf:")) &&
- mDB) {
- // if the sort key is not a template variable, it should be an RDF
- // predicate. Get the targets and compare those instead.
- nsCOMPtr<nsIRDFResource> predicate;
- nsresult rv = gRDFService->GetUnicodeResource(sortkey, getter_AddRefs(predicate));
- NS_ENSURE_SUCCESS(rv, rv);
- // create a predicate with '?sort=true' appended. This special
- // predicate may be used to have a different sort value than the
- // displayed value
- sortkey.AppendLiteral("?sort=true");
- nsCOMPtr<nsIRDFResource> sortPredicate;
- rv = gRDFService->GetUnicodeResource(sortkey, getter_AddRefs(sortPredicate));
- NS_ENSURE_SUCCESS(rv, rv);
- rv = GetSortValue(aLeft, predicate, sortPredicate, getter_AddRefs(leftNode));
- NS_ENSURE_SUCCESS(rv, rv);
- rv = GetSortValue(aRight, predicate, sortPredicate, getter_AddRefs(rightNode));
- NS_ENSURE_SUCCESS(rv, rv);
- }
- else {
- // get the values for the sort key from the results
- aLeft->GetBindingObjectFor(aVar, getter_AddRefs(leftNode));
- aRight->GetBindingObjectFor(aVar, getter_AddRefs(rightNode));
- }
- {
- // Literals?
- nsCOMPtr<nsIRDFLiteral> l = do_QueryInterface(leftNode);
- if (l) {
- nsCOMPtr<nsIRDFLiteral> r = do_QueryInterface(rightNode);
- if (r) {
- const char16_t *lstr, *rstr;
- l->GetValueConst(&lstr);
- r->GetValueConst(&rstr);
- *aResult = XULSortServiceImpl::CompareValues(
- nsDependentString(lstr),
- nsDependentString(rstr), aSortHints);
- }
- return NS_OK;
- }
- }
- {
- // Dates?
- nsCOMPtr<nsIRDFDate> l = do_QueryInterface(leftNode);
- if (l) {
- nsCOMPtr<nsIRDFDate> r = do_QueryInterface(rightNode);
- if (r) {
- PRTime ldate, rdate;
- l->GetValue(&ldate);
- r->GetValue(&rdate);
- int64_t delta = ldate - rdate;
- if (delta == 0)
- *aResult = 0;
- else if (delta >= 0)
- *aResult = 1;
- else
- *aResult = -1;
- }
- return NS_OK;
- }
- }
- {
- // Integers?
- nsCOMPtr<nsIRDFInt> l = do_QueryInterface(leftNode);
- if (l) {
- nsCOMPtr<nsIRDFInt> r = do_QueryInterface(rightNode);
- if (r) {
- int32_t lval, rval;
- l->GetValue(&lval);
- r->GetValue(&rval);
- *aResult = lval - rval;
- }
- return NS_OK;
- }
- }
- nsICollation* collation = nsXULContentUtils::GetCollation();
- if (collation) {
- // Blobs? (We can only compare these reasonably if we have a
- // collation object.)
- nsCOMPtr<nsIRDFBlob> l = do_QueryInterface(leftNode);
- if (l) {
- nsCOMPtr<nsIRDFBlob> r = do_QueryInterface(rightNode);
- if (r) {
- const uint8_t *lval, *rval;
- int32_t llen, rlen;
- l->GetValue(&lval);
- l->GetLength(&llen);
- r->GetValue(&rval);
- r->GetLength(&rlen);
-
- collation->CompareRawSortKey(lval, llen, rval, rlen, aResult);
- }
- }
- }
- // if the results are none of the above, just pretend that they are equal
- return NS_OK;
- }
- //----------------------------------------------------------------------
- //
- // nsIRDFObserver interface
- //
- NS_IMETHODIMP
- nsXULTemplateQueryProcessorRDF::OnAssert(nsIRDFDataSource* aDataSource,
- nsIRDFResource* aSource,
- nsIRDFResource* aProperty,
- nsIRDFNode* aTarget)
- {
- // Ignore updates if we're batching
- if (mUpdateBatchNest)
- return(NS_OK);
- if (! mBuilder)
- return NS_OK;
- LOG("onassert", aSource, aProperty, aTarget);
- Propagate(aSource, aProperty, aTarget);
- SynchronizeAll(aSource, aProperty, nullptr, aTarget);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsXULTemplateQueryProcessorRDF::OnUnassert(nsIRDFDataSource* aDataSource,
- nsIRDFResource* aSource,
- nsIRDFResource* aProperty,
- nsIRDFNode* aTarget)
- {
- // Ignore updates if we're batching
- if (mUpdateBatchNest)
- return NS_OK;
- if (! mBuilder)
- return NS_OK;
- LOG("onunassert", aSource, aProperty, aTarget);
- Retract(aSource, aProperty, aTarget);
- SynchronizeAll(aSource, aProperty, aTarget, nullptr);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsXULTemplateQueryProcessorRDF::OnChange(nsIRDFDataSource* aDataSource,
- nsIRDFResource* aSource,
- nsIRDFResource* aProperty,
- nsIRDFNode* aOldTarget,
- nsIRDFNode* aNewTarget)
- {
- // Ignore updates if we're batching
- if (mUpdateBatchNest)
- return NS_OK;
- if (! mBuilder)
- return NS_OK;
- LOG("onchange", aSource, aProperty, aNewTarget);
- if (aOldTarget) {
- // Pull any old results that were relying on aOldTarget
- Retract(aSource, aProperty, aOldTarget);
- }
- if (aNewTarget) {
- // Fire any new results that are activated by aNewTarget
- Propagate(aSource, aProperty, aNewTarget);
- }
- // Synchronize any of the content model that may have changed.
- SynchronizeAll(aSource, aProperty, aOldTarget, aNewTarget);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsXULTemplateQueryProcessorRDF::OnMove(nsIRDFDataSource* aDataSource,
- nsIRDFResource* aOldSource,
- nsIRDFResource* aNewSource,
- nsIRDFResource* aProperty,
- nsIRDFNode* aTarget)
- {
- // Ignore updates if we're batching
- if (mUpdateBatchNest)
- return NS_OK;
- NS_NOTYETIMPLEMENTED("write me");
- return NS_ERROR_NOT_IMPLEMENTED;
- }
- NS_IMETHODIMP
- nsXULTemplateQueryProcessorRDF::OnBeginUpdateBatch(nsIRDFDataSource* aDataSource)
- {
- mUpdateBatchNest++;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsXULTemplateQueryProcessorRDF::OnEndUpdateBatch(nsIRDFDataSource* aDataSource)
- {
- NS_ASSERTION(mUpdateBatchNest > 0, "badly nested update batch");
- if (--mUpdateBatchNest <= 0) {
- mUpdateBatchNest = 0;
- if (mBuilder)
- mBuilder->Rebuild();
- }
- return NS_OK;
- }
- nsresult
- nsXULTemplateQueryProcessorRDF::Propagate(nsIRDFResource* aSource,
- nsIRDFResource* aProperty,
- nsIRDFNode* aTarget)
- {
- // When a new assertion is added to the graph, determine any new matches
- // that must be added to the template builder. First, iterate through all
- // the RDF tests (<member> and <triple> tests), and find the topmost test
- // that would be affected by the new assertion.
- nsresult rv;
- ReteNodeSet livenodes;
- if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) {
- const char* sourceStr;
- aSource->GetValueConst(&sourceStr);
- const char* propertyStr;
- aProperty->GetValueConst(&propertyStr);
- nsAutoString targetStr;
- nsXULContentUtils::GetTextForNode(aTarget, targetStr);
- MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
- ("nsXULTemplateQueryProcessorRDF::Propagate: [%s] -> [%s] -> [%s]\n",
- sourceStr, propertyStr, NS_ConvertUTF16toUTF8(targetStr).get()));
- }
- {
- ReteNodeSet::Iterator last = mRDFTests.Last();
- for (ReteNodeSet::Iterator i = mRDFTests.First(); i != last; ++i) {
- nsRDFTestNode* rdftestnode = static_cast<nsRDFTestNode*>(*i);
- Instantiation seed;
- if (rdftestnode->CanPropagate(aSource, aProperty, aTarget, seed)) {
- rv = livenodes.Add(rdftestnode);
- if (NS_FAILED(rv))
- return rv;
- }
- }
- }
- // Now, we'll go through each, and any that aren't dominated by
- // another live node will be used to propagate the assertion
- // through the rule network
- {
- ReteNodeSet::Iterator last = livenodes.Last();
- for (ReteNodeSet::Iterator i = livenodes.First(); i != last; ++i) {
- nsRDFTestNode* rdftestnode = static_cast<nsRDFTestNode*>(*i);
- // What happens here is we create an instantiation as if we were
- // at the found test in the rule network. For example, if the
- // found test was a member test (parent => child), the parent
- // and child variables are assigned the values provided by the new
- // RDF assertion in the graph. The Constrain call is used to go
- // up to earlier RDF tests, filling in variables as it goes.
- // Constrain will eventually get up to the top node, an
- // nsContentTestNode, which takes the value of the reference
- // variable and calls the template builder to see if a result has
- // been generated already for the reference value. If it hasn't,
- // the new assertion couldn't cause a new match. If the result
- // exists, call Propagate to continue to the later RDF tests to
- // fill in the rest of the variable assignments.
- // Bogus, to get the seed instantiation
- Instantiation seed;
- rdftestnode->CanPropagate(aSource, aProperty, aTarget, seed);
- InstantiationSet* instantiations = new InstantiationSet();
- instantiations->Append(seed);
- rv = rdftestnode->Constrain(*instantiations);
- if (NS_FAILED(rv)) {
- delete instantiations;
- return rv;
- }
- bool owned = false;
- if (!instantiations->Empty())
- rv = rdftestnode->Propagate(*instantiations, true, owned);
- // owned should always be false in update mode, but check just
- // to be sure
- if (!owned)
- delete instantiations;
- if (NS_FAILED(rv))
- return rv;
- }
- }
- return NS_OK;
- }
- nsresult
- nsXULTemplateQueryProcessorRDF::Retract(nsIRDFResource* aSource,
- nsIRDFResource* aProperty,
- nsIRDFNode* aTarget)
- {
- if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) {
- const char* sourceStr;
- aSource->GetValueConst(&sourceStr);
- const char* propertyStr;
- aProperty->GetValueConst(&propertyStr);
- nsAutoString targetStr;
- nsXULContentUtils::GetTextForNode(aTarget, targetStr);
- MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
- ("nsXULTemplateQueryProcessorRDF::Retract: [%s] -> [%s] -> [%s]\n",
- sourceStr, propertyStr, NS_ConvertUTF16toUTF8(targetStr).get()));
- }
- // Retract any currently active rules that will no longer be matched.
- ReteNodeSet::ConstIterator lastnode = mRDFTests.Last();
- for (ReteNodeSet::ConstIterator node = mRDFTests.First(); node != lastnode; ++node) {
- const nsRDFTestNode* rdftestnode = static_cast<const nsRDFTestNode*>(*node);
- rdftestnode->Retract(aSource, aProperty, aTarget);
- // Now fire any newly revealed rules
- // XXXwaterson yo. write me.
- // The intent here is to handle any rules that might be
- // "revealed" by the removal of an assertion from the datasource.
- // Waterson doesn't think we support negated conditions in a rule.
- // Nor is he sure that this is currently useful.
- }
- return NS_OK;
- }
- nsresult
- nsXULTemplateQueryProcessorRDF::SynchronizeAll(nsIRDFResource* aSource,
- nsIRDFResource* aProperty,
- nsIRDFNode* aOldTarget,
- nsIRDFNode* aNewTarget)
- {
- // Update each match that contains <aSource, aProperty, aOldTarget>.
- // Get all the matches whose assignments are currently supported
- // by aSource and aProperty: we'll need to recompute them.
- ResultArray* results;
- if (!mBindingDependencies.Get(aSource, &results) || !mBuilder)
- return NS_OK;
- uint32_t length = results->Length();
- for (uint32_t r = 0; r < length; r++) {
- nsXULTemplateResultRDF* result = (*results)[r];
- if (result) {
- // synchronize the result's bindings and then update the builder
- // so that content can be updated
- if (result->SyncAssignments(aSource, aProperty, aNewTarget)) {
- nsITemplateRDFQuery* query = result->Query();
- if (query) {
- nsCOMPtr<nsIDOMNode> querynode;
- query->GetQueryNode(getter_AddRefs(querynode));
- mBuilder->ResultBindingChanged(result);
- }
- }
- }
- }
- return NS_OK;
- }
- nsresult
- nsXULTemplateQueryProcessorRDF::Log(const char* aOperation,
- nsIRDFResource* aSource,
- nsIRDFResource* aProperty,
- nsIRDFNode* aTarget)
- {
- if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) {
- nsresult rv;
- const char* sourceStr;
- rv = aSource->GetValueConst(&sourceStr);
- if (NS_FAILED(rv))
- return rv;
- MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
- ("xultemplate[%p] %8s [%s]--", this, aOperation, sourceStr));
- const char* propertyStr;
- rv = aProperty->GetValueConst(&propertyStr);
- if (NS_FAILED(rv))
- return rv;
- nsAutoString targetStr;
- rv = nsXULContentUtils::GetTextForNode(aTarget, targetStr);
- if (NS_FAILED(rv))
- return rv;
- nsAutoCString targetstrC;
- targetstrC.AssignWithConversion(targetStr);
- MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
- (" --[%s]-->[%s]",
- propertyStr,
- targetstrC.get()));
- }
- return NS_OK;
- }
- nsresult
- nsXULTemplateQueryProcessorRDF::CheckContainer(nsIRDFResource* aResource,
- bool* aIsContainer)
- {
- NS_ENSURE_ARG_POINTER(aIsContainer);
- NS_ENSURE_STATE(mDB);
- // We have to look at all of the arcs extending out of the
- // resource: if any of them are that "containment" property, then
- // we know we'll have children.
- bool isContainer = false;
- for (nsResourceSet::ConstIterator property = mContainmentProperties.First();
- property != mContainmentProperties.Last();
- property++) {
- bool hasArc = false;
- mDB->HasArcOut(aResource, *property, &hasArc);
- if (hasArc) {
- // Well, it's a container...
- isContainer = true;
- break;
- }
- }
- // If we get here, and we're still not sure if it's a container,
- // then see if it's an RDF container
- if (! isContainer) {
- gRDFContainerUtils->IsContainer(mDB, aResource, &isContainer);
- }
- *aIsContainer = isContainer;
- return NS_OK;
- }
- nsresult
- nsXULTemplateQueryProcessorRDF::CheckEmpty(nsIRDFResource* aResource,
- bool* aIsEmpty)
- {
- NS_ENSURE_STATE(mDB);
- *aIsEmpty = true;
- for (nsResourceSet::ConstIterator property = mContainmentProperties.First();
- property != mContainmentProperties.Last();
- property++) {
- nsCOMPtr<nsIRDFNode> dummy;
- mDB->GetTarget(aResource, *property, true, getter_AddRefs(dummy));
- if (dummy) {
- *aIsEmpty = false;
- break;
- }
- }
- if (*aIsEmpty){
- return nsXULTemplateQueryProcessorRDF::gRDFContainerUtils->
- IsEmpty(mDB, aResource, aIsEmpty);
- }
- return NS_OK;
- }
- nsresult
- nsXULTemplateQueryProcessorRDF::CheckIsSeparator(nsIRDFResource* aResource,
- bool* aIsSeparator)
- {
- NS_ENSURE_STATE(mDB);
- return mDB->HasAssertion(aResource, kRDF_type, kNC_BookmarkSeparator,
- true, aIsSeparator);
- }
- //----------------------------------------------------------------------
- nsresult
- nsXULTemplateQueryProcessorRDF::ComputeContainmentProperties(nsIDOMNode* aRootNode)
- {
- // The 'containment' attribute on the root node is a
- // whitespace-separated list that tells us which properties we
- // should use to test for containment.
- nsresult rv;
- mContainmentProperties.Clear();
- nsCOMPtr<nsIContent> content = do_QueryInterface(aRootNode);
- nsAutoString containment;
- content->GetAttr(kNameSpaceID_None, nsGkAtoms::containment, containment);
- uint32_t len = containment.Length();
- uint32_t offset = 0;
- while (offset < len) {
- while (offset < len && nsCRT::IsAsciiSpace(containment[offset]))
- ++offset;
- if (offset >= len)
- break;
- uint32_t end = offset;
- while (end < len && !nsCRT::IsAsciiSpace(containment[end]))
- ++end;
- nsAutoString propertyStr;
- containment.Mid(propertyStr, offset, end - offset);
- nsCOMPtr<nsIRDFResource> property;
- rv = gRDFService->GetUnicodeResource(propertyStr, getter_AddRefs(property));
- if (NS_FAILED(rv))
- return rv;
- rv = mContainmentProperties.Add(property);
- if (NS_FAILED(rv))
- return rv;
- offset = end;
- }
- #define TREE_PROPERTY_HACK 1
- #if defined(TREE_PROPERTY_HACK)
- if (! len) {
- // Some ever-present membership tests.
- mContainmentProperties.Add(nsXULContentUtils::NC_child);
- mContainmentProperties.Add(nsXULContentUtils::NC_Folder);
- }
- #endif
- return NS_OK;
- }
- nsresult
- nsXULTemplateQueryProcessorRDF::CompileExtendedQuery(nsRDFQuery* aQuery,
- nsIContent* aConditions,
- TestNode** aLastNode)
- {
- // Compile an extended query's children
- nsContentTestNode* idnode =
- new nsContentTestNode(this, aQuery->mRefVariable);
- aQuery->SetRoot(idnode);
- nsresult rv = mAllTests.Add(idnode);
- if (NS_FAILED(rv)) {
- delete idnode;
- return rv;
- }
- TestNode* prevnode = idnode;
- for (nsIContent* condition = aConditions->GetFirstChild();
- condition;
- condition = condition->GetNextSibling()) {
- // the <content> condition should always be the first child
- if (condition->IsXULElement(nsGkAtoms::content)) {
- if (condition != aConditions->GetFirstChild()) {
- nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_CONTENT_NOT_FIRST);
- continue;
- }
- // check for <content tag='tag'/> which indicates that matches
- // should only be generated for items inside content with that tag
- nsAutoString tagstr;
- condition->GetAttr(kNameSpaceID_None, nsGkAtoms::tag, tagstr);
- nsCOMPtr<nsIAtom> tag;
- if (! tagstr.IsEmpty()) {
- tag = NS_Atomize(tagstr);
- }
- nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(condition->GetComposedDoc());
- if (! doc)
- return NS_ERROR_FAILURE;
- idnode->SetTag(tag, doc);
- continue;
- }
- TestNode* testnode = nullptr;
- nsresult rv = CompileQueryChild(condition->NodeInfo()->NameAtom(),
- aQuery, condition, prevnode, &testnode);
- if (NS_FAILED(rv))
- return rv;
- if (testnode) {
- rv = prevnode->AddChild(testnode);
- if (NS_FAILED(rv))
- return rv;
- prevnode = testnode;
- }
- }
- *aLastNode = prevnode;
- return NS_OK;
- }
- nsresult
- nsXULTemplateQueryProcessorRDF::CompileQueryChild(nsIAtom* aTag,
- nsRDFQuery* aQuery,
- nsIContent* aCondition,
- TestNode* aParentNode,
- TestNode** aResult)
- {
- nsresult rv = NS_OK;
- if (aTag == nsGkAtoms::triple) {
- rv = CompileTripleCondition(aQuery, aCondition, aParentNode, aResult);
- }
- else if (aTag == nsGkAtoms::member) {
- rv = CompileMemberCondition(aQuery, aCondition, aParentNode, aResult);
- }
- else if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Info)) {
- nsAutoString tagstr;
- aTag->ToString(tagstr);
- nsAutoCString tagstrC;
- tagstrC.AssignWithConversion(tagstr);
- MOZ_LOG(gXULTemplateLog, LogLevel::Info,
- ("xultemplate[%p] unrecognized condition test <%s>",
- this, tagstrC.get()));
- }
- return rv;
- }
- nsresult
- nsXULTemplateQueryProcessorRDF::ParseLiteral(const nsString& aParseType,
- const nsString& aValue,
- nsIRDFNode** aResult)
- {
- nsresult rv = NS_OK;
- *aResult = nullptr;
- if (aParseType.EqualsLiteral(PARSE_TYPE_INTEGER)) {
- nsCOMPtr<nsIRDFInt> intLiteral;
- nsresult errorCode;
- int32_t intValue = aValue.ToInteger(&errorCode);
- if (NS_FAILED(errorCode))
- return NS_ERROR_FAILURE;
- rv = gRDFService->GetIntLiteral(intValue, getter_AddRefs(intLiteral));
- if (NS_FAILED(rv))
- return rv;
- intLiteral.forget(aResult);
- }
- else {
- nsCOMPtr<nsIRDFLiteral> literal;
- rv = gRDFService->GetLiteral(aValue.get(), getter_AddRefs(literal));
- if (NS_FAILED(rv))
- return rv;
- literal.forget(aResult);
- }
- return rv;
- }
- nsresult
- nsXULTemplateQueryProcessorRDF::CompileTripleCondition(nsRDFQuery* aQuery,
- nsIContent* aCondition,
- TestNode* aParentNode,
- TestNode** aResult)
- {
- // Compile a <triple> condition, which must be of the form:
- //
- // <triple subject="?var1|resource"
- // predicate="resource"
- // object="?var2|resource|literal" />
- //
- // XXXwaterson Some day it would be cool to allow the 'predicate'
- // to be bound to a variable.
- // subject
- nsAutoString subject;
- aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::subject, subject);
- nsCOMPtr<nsIAtom> svar;
- nsCOMPtr<nsIRDFResource> sres;
- if (subject.IsEmpty()) {
- nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_BAD_SUBJECT);
- return NS_OK;
- }
- if (subject[0] == char16_t('?'))
- svar = NS_Atomize(subject);
- else
- gRDFService->GetUnicodeResource(subject, getter_AddRefs(sres));
- // predicate
- nsAutoString predicate;
- aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::predicate, predicate);
- nsCOMPtr<nsIRDFResource> pres;
- if (predicate.IsEmpty() || predicate[0] == char16_t('?')) {
- nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_BAD_PREDICATE);
- return NS_OK;
- }
- gRDFService->GetUnicodeResource(predicate, getter_AddRefs(pres));
- // object
- nsAutoString object;
- aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::object, object);
- nsCOMPtr<nsIAtom> ovar;
- nsCOMPtr<nsIRDFNode> onode;
- if (object.IsEmpty()) {
- nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_BAD_OBJECT);
- return NS_OK;
- }
- if (object[0] == char16_t('?')) {
- ovar = NS_Atomize(object);
- }
- else if (object.FindChar(':') != -1) { // XXXwaterson evil.
- // treat as resource
- nsCOMPtr<nsIRDFResource> resource;
- gRDFService->GetUnicodeResource(object, getter_AddRefs(resource));
- onode = do_QueryInterface(resource);
- }
- else {
- nsAutoString parseType;
- aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::parsetype, parseType);
- nsresult rv = ParseLiteral(parseType, object, getter_AddRefs(onode));
- if (NS_FAILED(rv))
- return rv;
- }
- nsRDFPropertyTestNode* testnode = nullptr;
- if (svar && ovar) {
- testnode = new nsRDFPropertyTestNode(aParentNode, this, svar, pres, ovar);
- }
- else if (svar) {
- testnode = new nsRDFPropertyTestNode(aParentNode, this, svar, pres, onode);
- }
- else if (ovar) {
- testnode = new nsRDFPropertyTestNode(aParentNode, this, sres, pres, ovar);
- }
- else {
- nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_TRIPLE_NO_VAR);
- return NS_OK;
- }
- // add testnode to mAllTests first. If adding to mRDFTests fails, just
- // leave it in the list so that it can be deleted later.
- MOZ_ASSERT(testnode);
- nsresult rv = mAllTests.Add(testnode);
- if (NS_FAILED(rv)) {
- delete testnode;
- return rv;
- }
- rv = mRDFTests.Add(testnode);
- if (NS_FAILED(rv))
- return rv;
- *aResult = testnode;
- return NS_OK;
- }
- nsresult
- nsXULTemplateQueryProcessorRDF::CompileMemberCondition(nsRDFQuery* aQuery,
- nsIContent* aCondition,
- TestNode* aParentNode,
- TestNode** aResult)
- {
- // Compile a <member> condition, which must be of the form:
- //
- // <member container="?var1" child="?var2" />
- //
- // container
- nsAutoString container;
- aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::container, container);
- if (!container.IsEmpty() && container[0] != char16_t('?')) {
- nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_MEMBER_NOCONTAINERVAR);
- return NS_OK;
- }
- nsCOMPtr<nsIAtom> containervar = NS_Atomize(container);
- // child
- nsAutoString child;
- aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::child, child);
- if (!child.IsEmpty() && child[0] != char16_t('?')) {
- nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_MEMBER_NOCHILDVAR);
- return NS_OK;
- }
- nsCOMPtr<nsIAtom> childvar = NS_Atomize(child);
- TestNode* testnode =
- new nsRDFConMemberTestNode(aParentNode,
- this,
- containervar,
- childvar);
- // add testnode to mAllTests first. If adding to mRDFTests fails, just
- // leave it in the list so that it can be deleted later.
- nsresult rv = mAllTests.Add(testnode);
- if (NS_FAILED(rv)) {
- delete testnode;
- return rv;
- }
- rv = mRDFTests.Add(testnode);
- if (NS_FAILED(rv))
- return rv;
- *aResult = testnode;
- return NS_OK;
- }
- nsresult
- nsXULTemplateQueryProcessorRDF::AddDefaultSimpleRules(nsRDFQuery* aQuery,
- TestNode** aChildNode)
- {
- // XXXndeakin should check for tag in query processor instead of builder?
- nsContentTestNode* idnode =
- new nsContentTestNode(this,
- aQuery->mRefVariable);
- // Create (?container ^member ?member)
- nsRDFConMemberTestNode* membernode =
- new nsRDFConMemberTestNode(idnode,
- this,
- aQuery->mRefVariable,
- aQuery->mMemberVariable);
- // add nodes to mAllTests first. If later calls fail, just leave them in
- // the list so that they can be deleted later.
- nsresult rv = mAllTests.Add(idnode);
- if (NS_FAILED(rv)) {
- delete idnode;
- delete membernode;
- return rv;
- }
- rv = mAllTests.Add(membernode);
- if (NS_FAILED(rv)) {
- delete membernode;
- return rv;
- }
- rv = mRDFTests.Add(membernode);
- if (NS_FAILED(rv))
- return rv;
- rv = idnode->AddChild(membernode);
- if (NS_FAILED(rv))
- return rv;
- mSimpleRuleMemberTest = membernode;
- *aChildNode = membernode;
- return NS_OK;
- }
- nsresult
- nsXULTemplateQueryProcessorRDF::CompileSimpleQuery(nsRDFQuery* aQuery,
- nsIContent* aQueryElement,
- TestNode** aLastNode)
- {
- // Compile a "simple" (or old-school style) <template> query.
- nsresult rv;
- TestNode* parentNode;
- if (! mSimpleRuleMemberTest) {
- rv = AddDefaultSimpleRules(aQuery, &parentNode);
- if (NS_FAILED(rv))
- return rv;
- }
- bool hasContainerTest = false;
- TestNode* prevnode = mSimpleRuleMemberTest;
- // Add constraints for the LHS
- const nsAttrName* name;
- for (uint32_t i = 0; (name = aQueryElement->GetAttrNameAt(i)); ++i) {
- // Note: some attributes must be skipped on XUL template query subtree
- // never compare against rdf:property, rdf:instanceOf, {}:id or {}:parsetype attribute
- if (name->Equals(nsGkAtoms::property, kNameSpaceID_RDF) ||
- name->Equals(nsGkAtoms::instanceOf, kNameSpaceID_RDF) ||
- name->Equals(nsGkAtoms::id, kNameSpaceID_None) ||
- name->Equals(nsGkAtoms::parsetype, kNameSpaceID_None)) {
- continue;
- }
- int32_t attrNameSpaceID = name->NamespaceID();
- if (attrNameSpaceID == kNameSpaceID_XMLNS)
- continue;
- nsIAtom* attr = name->LocalName();
- nsAutoString value;
- aQueryElement->GetAttr(attrNameSpaceID, attr, value);
- TestNode* testnode = nullptr;
- if (name->Equals(nsGkAtoms::iscontainer, kNameSpaceID_None) ||
- name->Equals(nsGkAtoms::isempty, kNameSpaceID_None)) {
- // Tests about containerhood and emptiness. These can be
- // globbed together, mostly. Check to see if we've already
- // added a container test: we only need one.
- if (hasContainerTest)
- continue;
- nsRDFConInstanceTestNode::Test iscontainer =
- nsRDFConInstanceTestNode::eDontCare;
- static nsIContent::AttrValuesArray strings[] =
- {&nsGkAtoms::_true, &nsGkAtoms::_false, nullptr};
- switch (aQueryElement->FindAttrValueIn(kNameSpaceID_None,
- nsGkAtoms::iscontainer,
- strings, eCaseMatters)) {
- case 0: iscontainer = nsRDFConInstanceTestNode::eTrue; break;
- case 1: iscontainer = nsRDFConInstanceTestNode::eFalse; break;
- }
- nsRDFConInstanceTestNode::Test isempty =
- nsRDFConInstanceTestNode::eDontCare;
- switch (aQueryElement->FindAttrValueIn(kNameSpaceID_None,
- nsGkAtoms::isempty,
- strings, eCaseMatters)) {
- case 0: isempty = nsRDFConInstanceTestNode::eTrue; break;
- case 1: isempty = nsRDFConInstanceTestNode::eFalse; break;
- }
- testnode = new nsRDFConInstanceTestNode(prevnode,
- this,
- aQuery->mMemberVariable,
- iscontainer,
- isempty);
- rv = mAllTests.Add(testnode);
- if (NS_FAILED(rv)) {
- delete testnode;
- return rv;
- }
- rv = mRDFTests.Add(testnode);
- if (NS_FAILED(rv))
- return rv;
- }
- else if (attrNameSpaceID != kNameSpaceID_None || attr != nsGkAtoms::parent) {
- // It's a simple RDF test
- nsCOMPtr<nsIRDFResource> property;
- rv = nsXULContentUtils::GetResource(attrNameSpaceID, attr, getter_AddRefs(property));
- if (NS_FAILED(rv))
- return rv;
- // XXXwaterson this is so manky
- nsCOMPtr<nsIRDFNode> target;
- if (value.FindChar(':') != -1) { // XXXwaterson WRONG WRONG WRONG!
- nsCOMPtr<nsIRDFResource> resource;
- rv = gRDFService->GetUnicodeResource(value, getter_AddRefs(resource));
- if (NS_FAILED(rv))
- return rv;
- target = do_QueryInterface(resource);
- }
- else {
- nsAutoString parseType;
- aQueryElement->GetAttr(kNameSpaceID_None, nsGkAtoms::parsetype, parseType);
- rv = ParseLiteral(parseType, value, getter_AddRefs(target));
- if (NS_FAILED(rv))
- return rv;
- }
- testnode = new nsRDFPropertyTestNode(prevnode, this,
- aQuery->mMemberVariable, property, target);
- rv = mAllTests.Add(testnode);
- if (NS_FAILED(rv)) {
- delete testnode;
- return rv;
- }
- rv = mRDFTests.Add(testnode);
- if (NS_FAILED(rv))
- return rv;
- }
- if (testnode) {
- if (prevnode) {
- rv = prevnode->AddChild(testnode);
- if (NS_FAILED(rv))
- return rv;
- }
- else {
- aQuery->SetRoot(testnode);
- }
- prevnode = testnode;
- }
- }
- *aLastNode = prevnode;
- return NS_OK;
- }
- RDFBindingSet*
- nsXULTemplateQueryProcessorRDF::GetBindingsForRule(nsIDOMNode* aRuleNode)
- {
- return mRuleToBindingsMap.GetWeak(aRuleNode);
- }
- void
- nsXULTemplateQueryProcessorRDF::AddBindingDependency(nsXULTemplateResultRDF* aResult,
- nsIRDFResource* aResource)
- {
- ResultArray* arr;
- if (!mBindingDependencies.Get(aResource, &arr)) {
- arr = new ResultArray();
- mBindingDependencies.Put(aResource, arr);
- }
- int32_t index = arr->IndexOf(aResult);
- if (index == -1)
- arr->AppendElement(aResult);
- }
- void
- nsXULTemplateQueryProcessorRDF::RemoveBindingDependency(nsXULTemplateResultRDF* aResult,
- nsIRDFResource* aResource)
- {
- ResultArray* arr;
- if (mBindingDependencies.Get(aResource, &arr)) {
- int32_t index = arr->IndexOf(aResult);
- if (index >= 0)
- arr->RemoveElementAt(index);
- }
- }
- nsresult
- nsXULTemplateQueryProcessorRDF::AddMemoryElements(const Instantiation& aInst,
- nsXULTemplateResultRDF* aResult)
- {
- // Add the result to a table indexed by supporting MemoryElement
- MemoryElementSet::ConstIterator last = aInst.mSupport.Last();
- for (MemoryElementSet::ConstIterator element = aInst.mSupport.First();
- element != last; ++element) {
- PLHashNumber hash = (element.operator->())->Hash();
- nsCOMArray<nsXULTemplateResultRDF>* arr;
- if (!mMemoryElementToResultMap.Get(hash, &arr)) {
- arr = new nsCOMArray<nsXULTemplateResultRDF>();
- mMemoryElementToResultMap.Put(hash, arr);
- }
- // results may be added more than once so they will all get deleted properly
- arr->AppendObject(aResult);
- }
- return NS_OK;
- }
- nsresult
- nsXULTemplateQueryProcessorRDF::RemoveMemoryElements(const Instantiation& aInst,
- nsXULTemplateResultRDF* aResult)
- {
- // Remove the results mapped by the supporting MemoryElement
- MemoryElementSet::ConstIterator last = aInst.mSupport.Last();
- for (MemoryElementSet::ConstIterator element = aInst.mSupport.First();
- element != last; ++element) {
- PLHashNumber hash = (element.operator->())->Hash();
- nsCOMArray<nsXULTemplateResultRDF>* arr;
- if (mMemoryElementToResultMap.Get(hash, &arr)) {
- int32_t index = arr->IndexOf(aResult);
- if (index >= 0)
- arr->RemoveObjectAt(index);
- uint32_t length = arr->Count();
- if (! length)
- mMemoryElementToResultMap.Remove(hash);
- }
- }
- return NS_OK;
- }
- void
- nsXULTemplateQueryProcessorRDF::RetractElement(const MemoryElement& aMemoryElement)
- {
- if (! mBuilder)
- return;
- // when an assertion is removed, look through the memory elements and
- // find results that are associated with them. Those results will need
- // to be removed because they no longer match.
- PLHashNumber hash = aMemoryElement.Hash();
- nsCOMArray<nsXULTemplateResultRDF>* arr;
- if (mMemoryElementToResultMap.Get(hash, &arr)) {
- uint32_t length = arr->Count();
- for (int32_t r = length - 1; r >= 0; r--) {
- nsXULTemplateResultRDF* result = (*arr)[r];
- if (result) {
- // because the memory elements are hashed by an integer,
- // sometimes two different memory elements will have the same
- // hash code. In this case we check the result to make sure
- // and only remove those that refer to that memory element.
- if (result->HasMemoryElement(aMemoryElement)) {
- nsITemplateRDFQuery* query = result->Query();
- if (query) {
- nsCOMPtr<nsIDOMNode> querynode;
- query->GetQueryNode(getter_AddRefs(querynode));
- mBuilder->RemoveResult(result);
- }
- // a call to RemoveMemoryElements may have removed it
- if (!mMemoryElementToResultMap.Get(hash, nullptr))
- return;
- // the array should have been reduced by one, but check
- // just to make sure
- uint32_t newlength = arr->Count();
- if (r > (int32_t)newlength)
- r = newlength;
- }
- }
- }
- // if there are no items left, remove the memory element from the hashtable
- if (!arr->Count())
- mMemoryElementToResultMap.Remove(hash);
- }
- }
- int32_t
- nsXULTemplateQueryProcessorRDF::GetContainerIndexOf(nsIXULTemplateResult* aResult)
- {
- // get the reference variable and look up the container in the result
- nsCOMPtr<nsISupports> ref;
- nsresult rv = aResult->GetBindingObjectFor(mRefVariable,
- getter_AddRefs(ref));
- if (NS_FAILED(rv) || !mDB)
- return -1;
- nsCOMPtr<nsIRDFResource> container = do_QueryInterface(ref);
- if (container) {
- // if the container is an RDF Seq, return the index of the result
- // in the container.
- bool isSequence = false;
- gRDFContainerUtils->IsSeq(mDB, container, &isSequence);
- if (isSequence) {
- nsCOMPtr<nsIRDFResource> resource;
- aResult->GetResource(getter_AddRefs(resource));
- if (resource) {
- int32_t index;
- gRDFContainerUtils->IndexOf(mDB, container, resource, &index);
- return index;
- }
- }
- }
- // if the container isn't a Seq, or the result isn't in the container,
- // return -1 indicating no index.
- return -1;
- }
- nsresult
- nsXULTemplateQueryProcessorRDF::GetSortValue(nsIXULTemplateResult* aResult,
- nsIRDFResource* aPredicate,
- nsIRDFResource* aSortPredicate,
- nsISupports** aResultNode)
- {
- nsCOMPtr<nsIRDFResource> source;
- nsresult rv = aResult->GetResource(getter_AddRefs(source));
- if (NS_FAILED(rv))
- return rv;
-
- nsCOMPtr<nsIRDFNode> value;
- if (source && mDB) {
- // first check predicate?sort=true so that datasources may use a
- // custom value for sorting
- rv = mDB->GetTarget(source, aSortPredicate, true,
- getter_AddRefs(value));
- if (NS_FAILED(rv))
- return rv;
- if (!value) {
- rv = mDB->GetTarget(source, aPredicate, true,
- getter_AddRefs(value));
- if (NS_FAILED(rv))
- return rv;
- }
- }
- *aResultNode = value;
- NS_IF_ADDREF(*aResultNode);
- return NS_OK;
- }
|