123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574 |
- /* -*- 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/. */
- /*
- Builds content from a datasource using the XUL <template> tag.
- TO DO
- . Fix ContentTagTest's location in the network construction
- To turn on logging for this module, set:
- MOZ_LOG=nsXULTemplateBuilder:5
- */
- #include "nsAutoPtr.h"
- #include "nsCOMPtr.h"
- #include "nsCRT.h"
- #include "nsIContent.h"
- #include "nsIDOMElement.h"
- #include "nsIDOMNode.h"
- #include "nsIDOMDocument.h"
- #include "nsIDOMXULElement.h"
- #include "nsIDocument.h"
- #include "nsBindingManager.h"
- #include "nsIDOMNodeList.h"
- #include "nsIObserverService.h"
- #include "nsIRDFCompositeDataSource.h"
- #include "nsIRDFInferDataSource.h"
- #include "nsIRDFContainerUtils.h"
- #include "nsIXULDocument.h"
- #include "nsIXULTemplateBuilder.h"
- #include "nsIXULBuilderListener.h"
- #include "nsIRDFRemoteDataSource.h"
- #include "nsIRDFService.h"
- #include "nsIScriptContext.h"
- #include "nsIScriptGlobalObject.h"
- #include "nsIServiceManager.h"
- #include "nsISimpleEnumerator.h"
- #include "nsIMutableArray.h"
- #include "nsIURL.h"
- #include "nsIXPConnect.h"
- #include "nsContentCID.h"
- #include "nsNameSpaceManager.h"
- #include "nsRDFCID.h"
- #include "nsXULContentUtils.h"
- #include "nsString.h"
- #include "nsTArray.h"
- #include "nsXPIDLString.h"
- #include "nsWhitespaceTokenizer.h"
- #include "nsGkAtoms.h"
- #include "nsXULElement.h"
- #include "jsapi.h"
- #include "mozilla/Logging.h"
- #include "rdf.h"
- #include "PLDHashTable.h"
- #include "plhash.h"
- #include "nsDOMClassInfoID.h"
- #include "nsPIDOMWindow.h"
- #include "nsIConsoleService.h"
- #include "nsNetUtil.h"
- #include "nsXULTemplateBuilder.h"
- #include "nsXULTemplateQueryProcessorRDF.h"
- #include "nsXULTemplateQueryProcessorXML.h"
- #include "nsXULTemplateQueryProcessorStorage.h"
- #include "nsContentUtils.h"
- #include "ChildIterator.h"
- #include "mozilla/dom/ScriptSettings.h"
- #include "nsGlobalWindow.h"
- using namespace mozilla::dom;
- using namespace mozilla;
- //----------------------------------------------------------------------
- //
- // nsXULTemplateBuilder
- //
- nsrefcnt nsXULTemplateBuilder::gRefCnt = 0;
- nsIRDFService* nsXULTemplateBuilder::gRDFService;
- nsIRDFContainerUtils* nsXULTemplateBuilder::gRDFContainerUtils;
- nsIScriptSecurityManager* nsXULTemplateBuilder::gScriptSecurityManager;
- nsIPrincipal* nsXULTemplateBuilder::gSystemPrincipal;
- nsIObserverService* nsXULTemplateBuilder::gObserverService;
- LazyLogModule gXULTemplateLog("nsXULTemplateBuilder");
- #define NS_QUERY_PROCESSOR_CONTRACTID_PREFIX "@mozilla.org/xul/xul-query-processor;1?name="
- //----------------------------------------------------------------------
- //
- // nsXULTemplateBuilder methods
- //
- nsXULTemplateBuilder::nsXULTemplateBuilder(void)
- : mQueriesCompiled(false),
- mFlags(0),
- mTop(nullptr),
- mObservedDocument(nullptr)
- {
- MOZ_COUNT_CTOR(nsXULTemplateBuilder);
- }
- void
- nsXULTemplateBuilder::DestroyMatchMap()
- {
- for (auto iter = mMatchMap.Iter(); !iter.Done(); iter.Next()) {
- nsTemplateMatch*& match = iter.Data();
- // delete all the matches in the list
- while (match) {
- nsTemplateMatch* next = match->mNext;
- nsTemplateMatch::Destroy(match, true);
- match = next;
- }
- iter.Remove();
- }
- }
- nsXULTemplateBuilder::~nsXULTemplateBuilder(void)
- {
- Uninit(true);
- if (--gRefCnt == 0) {
- NS_IF_RELEASE(gRDFService);
- NS_IF_RELEASE(gRDFContainerUtils);
- NS_IF_RELEASE(gSystemPrincipal);
- NS_IF_RELEASE(gScriptSecurityManager);
- NS_IF_RELEASE(gObserverService);
- }
- MOZ_COUNT_DTOR(nsXULTemplateBuilder);
- }
- nsresult
- nsXULTemplateBuilder::InitGlobals()
- {
- nsresult rv;
- if (gRefCnt++ == 0) {
- // Initialize the global shared reference to the service
- // manager and get some shared resource objects.
- NS_DEFINE_CID(kRDFServiceCID, NS_RDFSERVICE_CID);
- rv = CallGetService(kRDFServiceCID, &gRDFService);
- if (NS_FAILED(rv))
- return rv;
- NS_DEFINE_CID(kRDFContainerUtilsCID, NS_RDFCONTAINERUTILS_CID);
- rv = CallGetService(kRDFContainerUtilsCID, &gRDFContainerUtils);
- if (NS_FAILED(rv))
- return rv;
- rv = CallGetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID,
- &gScriptSecurityManager);
- if (NS_FAILED(rv))
- return rv;
- rv = gScriptSecurityManager->GetSystemPrincipal(&gSystemPrincipal);
- if (NS_FAILED(rv))
- return rv;
- rv = CallGetService(NS_OBSERVERSERVICE_CONTRACTID, &gObserverService);
- if (NS_FAILED(rv))
- return rv;
- }
- return NS_OK;
- }
- void
- nsXULTemplateBuilder::StartObserving(nsIDocument* aDocument)
- {
- aDocument->AddObserver(this);
- mObservedDocument = aDocument;
- gObserverService->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC, false);
- }
- void
- nsXULTemplateBuilder::StopObserving()
- {
- MOZ_ASSERT(mObservedDocument);
- mObservedDocument->RemoveObserver(this);
- mObservedDocument = nullptr;
- gObserverService->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
- }
- void
- nsXULTemplateBuilder::CleanUp(bool aIsFinal)
- {
- for (int32_t q = mQuerySets.Length() - 1; q >= 0; q--) {
- nsTemplateQuerySet* qs = mQuerySets[q];
- delete qs;
- }
- mQuerySets.Clear();
- DestroyMatchMap();
- // Setting mQueryProcessor to null will close connections. This would be
- // handled by the cycle collector, but we want to close them earlier.
- if (aIsFinal)
- mQueryProcessor = nullptr;
- }
- void
- nsXULTemplateBuilder::Uninit(bool aIsFinal)
- {
- if (mObservedDocument && aIsFinal) {
- StopObserving();
- }
- if (mQueryProcessor)
- mQueryProcessor->Done();
- CleanUp(aIsFinal);
- mRootResult = nullptr;
- mRefVariable = nullptr;
- mMemberVariable = nullptr;
- mQueriesCompiled = false;
- }
- NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULTemplateBuilder)
- NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULTemplateBuilder)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mDataSource)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mDB)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mCompDB)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mRoot)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootResult)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mListeners)
- NS_IMPL_CYCLE_COLLECTION_UNLINK(mQueryProcessor)
- tmp->DestroyMatchMap();
- for (uint32_t i = 0; i < tmp->mQuerySets.Length(); ++i) {
- nsTemplateQuerySet* qs = tmp->mQuerySets[i];
- delete qs;
- }
- tmp->mQuerySets.Clear();
- NS_IMPL_CYCLE_COLLECTION_UNLINK_END
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULTemplateBuilder)
- if (tmp->mObservedDocument && !cb.WantAllTraces()) {
- // The global observer service holds us alive.
- return NS_SUCCESS_INTERRUPTED_TRAVERSE;
- }
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDataSource)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDB)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCompDB)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootResult)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListeners)
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mQueryProcessor)
- for (auto iter = tmp->mMatchMap.Iter(); !iter.Done(); iter.Next()) {
- cb.NoteXPCOMChild(iter.Key());
- nsTemplateMatch* match = iter.UserData();
- while (match) {
- cb.NoteXPCOMChild(match->GetContainer());
- cb.NoteXPCOMChild(match->mResult);
- match = match->mNext;
- }
- }
- {
- uint32_t i, count = tmp->mQuerySets.Length();
- for (i = 0; i < count; ++i) {
- nsTemplateQuerySet *set = tmp->mQuerySets[i];
- cb.NoteXPCOMChild(set->mQueryNode);
- cb.NoteXPCOMChild(set->mCompiledQuery);
- uint16_t j, rulesCount = set->RuleCount();
- for (j = 0; j < rulesCount; ++j) {
- set->GetRuleAt(j)->Traverse(cb);
- }
- }
- }
- tmp->Traverse(cb);
- NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
- NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULTemplateBuilder)
- NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULTemplateBuilder)
- NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXULTemplateBuilder)
- NS_INTERFACE_MAP_ENTRY(nsIXULTemplateBuilder)
- NS_INTERFACE_MAP_ENTRY(nsIDocumentObserver)
- NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
- NS_INTERFACE_MAP_ENTRY(nsIObserver)
- NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIXULTemplateBuilder)
- NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XULTemplateBuilder)
- NS_INTERFACE_MAP_END
- //----------------------------------------------------------------------
- //
- // nsIXULTemplateBuilder methods
- //
- NS_IMETHODIMP
- nsXULTemplateBuilder::GetRoot(nsIDOMElement** aResult)
- {
- if (mRoot) {
- return CallQueryInterface(mRoot, aResult);
- }
- *aResult = nullptr;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsXULTemplateBuilder::GetDatasource(nsISupports** aResult)
- {
- if (mCompDB)
- NS_ADDREF(*aResult = mCompDB);
- else
- NS_IF_ADDREF(*aResult = mDataSource);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsXULTemplateBuilder::SetDatasource(nsISupports* aResult)
- {
- mDataSource = aResult;
- mCompDB = do_QueryInterface(mDataSource);
- return Rebuild();
- }
- NS_IMETHODIMP
- nsXULTemplateBuilder::GetDatabase(nsIRDFCompositeDataSource** aResult)
- {
- NS_IF_ADDREF(*aResult = mCompDB);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsXULTemplateBuilder::GetQueryProcessor(nsIXULTemplateQueryProcessor** aResult)
- {
- NS_IF_ADDREF(*aResult = mQueryProcessor.get());
- return NS_OK;
- }
- NS_IMETHODIMP
- nsXULTemplateBuilder::AddRuleFilter(nsIDOMNode* aRule, nsIXULTemplateRuleFilter* aFilter)
- {
- if (!aRule || !aFilter)
- return NS_ERROR_NULL_POINTER;
- // a custom rule filter may be added, one for each rule. If a new one is
- // added, it replaces the old one. Look for the right rule and set its
- // filter
- int32_t count = mQuerySets.Length();
- for (int32_t q = 0; q < count; q++) {
- nsTemplateQuerySet* queryset = mQuerySets[q];
- int16_t rulecount = queryset->RuleCount();
- for (int16_t r = 0; r < rulecount; r++) {
- nsTemplateRule* rule = queryset->GetRuleAt(r);
- nsCOMPtr<nsIDOMNode> rulenode;
- rule->GetRuleNode(getter_AddRefs(rulenode));
- if (aRule == rulenode) {
- rule->SetRuleFilter(aFilter);
- return NS_OK;
- }
- }
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- nsXULTemplateBuilder::Rebuild()
- {
- int32_t i;
- for (i = mListeners.Count() - 1; i >= 0; --i) {
- mListeners[i]->WillRebuild(this);
- }
- nsresult rv = RebuildAll();
- for (i = mListeners.Count() - 1; i >= 0; --i) {
- mListeners[i]->DidRebuild(this);
- }
- return rv;
- }
- NS_IMETHODIMP
- nsXULTemplateBuilder::Refresh()
- {
- nsresult rv;
- if (!mCompDB)
- return NS_ERROR_FAILURE;
- nsCOMPtr<nsISimpleEnumerator> dslist;
- rv = mCompDB->GetDataSources(getter_AddRefs(dslist));
- NS_ENSURE_SUCCESS(rv, rv);
- bool hasMore;
- nsCOMPtr<nsISupports> next;
- nsCOMPtr<nsIRDFRemoteDataSource> rds;
- while(NS_SUCCEEDED(dslist->HasMoreElements(&hasMore)) && hasMore) {
- dslist->GetNext(getter_AddRefs(next));
- if (next && (rds = do_QueryInterface(next))) {
- rds->Refresh(false);
- }
- }
- // XXXbsmedberg: it would be kinda nice to install an async nsIRDFXMLSink
- // observer and call rebuild() once the load is complete. See bug 254600.
- return NS_OK;
- }
- NS_IMETHODIMP
- nsXULTemplateBuilder::Init(nsIContent* aElement)
- {
- NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
- mRoot = aElement;
- nsCOMPtr<nsIDocument> doc = mRoot->GetComposedDoc();
- NS_ASSERTION(doc, "element has no document");
- if (! doc)
- return NS_ERROR_UNEXPECTED;
- bool shouldDelay;
- nsresult rv = LoadDataSources(doc, &shouldDelay);
- if (NS_SUCCEEDED(rv)) {
- StartObserving(doc);
- }
- return rv;
- }
- NS_IMETHODIMP
- nsXULTemplateBuilder::CreateContents(nsIContent* aElement, bool aForceCreation)
- {
- return NS_OK;
- }
- NS_IMETHODIMP
- nsXULTemplateBuilder::HasGeneratedContent(nsIRDFResource* aResource,
- nsIAtom* aTag,
- bool* aGenerated)
- {
- *aGenerated = false;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsXULTemplateBuilder::AddResult(nsIXULTemplateResult* aResult,
- nsIDOMNode* aQueryNode)
- {
- NS_ENSURE_ARG_POINTER(aResult);
- NS_ENSURE_ARG_POINTER(aQueryNode);
- return UpdateResult(nullptr, aResult, aQueryNode);
- }
- NS_IMETHODIMP
- nsXULTemplateBuilder::RemoveResult(nsIXULTemplateResult* aResult)
- {
- NS_ENSURE_ARG_POINTER(aResult);
- return UpdateResult(aResult, nullptr, nullptr);
- }
- NS_IMETHODIMP
- nsXULTemplateBuilder::ReplaceResult(nsIXULTemplateResult* aOldResult,
- nsIXULTemplateResult* aNewResult,
- nsIDOMNode* aQueryNode)
- {
- NS_ENSURE_ARG_POINTER(aOldResult);
- NS_ENSURE_ARG_POINTER(aNewResult);
- NS_ENSURE_ARG_POINTER(aQueryNode);
- // just remove the old result and then add a new result separately
- nsresult rv = UpdateResult(aOldResult, nullptr, nullptr);
- if (NS_FAILED(rv))
- return rv;
- return UpdateResult(nullptr, aNewResult, aQueryNode);
- }
- nsresult
- nsXULTemplateBuilder::UpdateResult(nsIXULTemplateResult* aOldResult,
- nsIXULTemplateResult* aNewResult,
- nsIDOMNode* aQueryNode)
- {
- MOZ_LOG(gXULTemplateLog, LogLevel::Info,
- ("nsXULTemplateBuilder::UpdateResult %p %p %p",
- aOldResult, aNewResult, aQueryNode));
- if (!mRoot || !mQueriesCompiled)
- return NS_OK;
- // get the containers where content may be inserted. If
- // GetInsertionLocations returns false, no container has generated
- // any content yet so new content should not be generated either. This
- // will be false if the result applies to content that is in a closed menu
- // or treeitem for example.
- nsAutoPtr<nsCOMArray<nsIContent> > insertionPoints;
- bool mayReplace = GetInsertionLocations(aOldResult ? aOldResult : aNewResult,
- getter_Transfers(insertionPoints));
- if (! mayReplace)
- return NS_OK;
- nsresult rv = NS_OK;
- nsCOMPtr<nsIRDFResource> oldId, newId;
- nsTemplateQuerySet* queryset = nullptr;
- if (aOldResult) {
- rv = GetResultResource(aOldResult, getter_AddRefs(oldId));
- if (NS_FAILED(rv))
- return rv;
- // Ignore re-entrant builds for content that is currently in our
- // activation stack.
- if (IsActivated(oldId))
- return NS_OK;
- }
- if (aNewResult) {
- rv = GetResultResource(aNewResult, getter_AddRefs(newId));
- if (NS_FAILED(rv))
- return rv;
- // skip results that don't have ids
- if (! newId)
- return NS_OK;
- // Ignore re-entrant builds for content that is currently in our
- // activation stack.
- if (IsActivated(newId))
- return NS_OK;
- // look for the queryset associated with the supplied query node
- nsCOMPtr<nsIContent> querycontent = do_QueryInterface(aQueryNode);
- int32_t count = mQuerySets.Length();
- for (int32_t q = 0; q < count; q++) {
- nsTemplateQuerySet* qs = mQuerySets[q];
- if (qs->mQueryNode == querycontent) {
- queryset = qs;
- break;
- }
- }
- if (! queryset)
- return NS_OK;
- }
- if (insertionPoints) {
- // iterate over each insertion point and add or remove the result from
- // that container
- uint32_t count = insertionPoints->Count();
- for (uint32_t t = 0; t < count; t++) {
- nsCOMPtr<nsIContent> insertionPoint = insertionPoints->SafeObjectAt(t);
- if (insertionPoint) {
- rv = UpdateResultInContainer(aOldResult, aNewResult, queryset,
- oldId, newId, insertionPoint);
- if (NS_FAILED(rv))
- return rv;
- }
- }
- }
- else {
- // The tree builder doesn't use insertion points, so no insertion
- // points will be set. In this case, just update the one result.
- rv = UpdateResultInContainer(aOldResult, aNewResult, queryset,
- oldId, newId, nullptr);
- }
- return NS_OK;
- }
- nsresult
- nsXULTemplateBuilder::UpdateResultInContainer(nsIXULTemplateResult* aOldResult,
- nsIXULTemplateResult* aNewResult,
- nsTemplateQuerySet* aQuerySet,
- nsIRDFResource* aOldId,
- nsIRDFResource* aNewId,
- nsIContent* aInsertionPoint)
- {
- // This method takes a result that no longer applies (aOldResult) and
- // replaces it with a new result (aNewResult). Either may be null
- // indicating to just remove a result or add a new one without replacing.
- //
- // Matches are stored in the hashtable mMatchMap, keyed by result id. If
- // there is more than one query, or the same id is found in different
- // containers, the values in the hashtable will be a linked list of all
- // the matches for that id. The matches are sorted according to the
- // queries they are associated with. Matches for earlier queries in the
- // template take priority over matches from later queries. The priority
- // for a match is determined from the match's QuerySetPriority method.
- // The first query has a priority 0, and higher numbers are for later
- // queries with successively higher priorities. Thus, a match takes
- // precedence if it has a lower priority than another. If there is only
- // one query or container, then the match doesn't have any linked items.
- //
- // Matches are nsTemplateMatch objects. They are wrappers around
- // nsIXULTemplateResult result objects and are created with
- // nsTemplateMatch::Create below. The aQuerySet argument specifies which
- // query the match is associated with.
- //
- // When a result id exists in multiple containers, the match's mContainer
- // field is set to the container it corresponds to. The aInsertionPoint
- // argument specifies which container is being updated. Even though they
- // are stored in the same linked list as other matches of the same id, the
- // matches for different containers are treated separately. They are only
- // stored in the same hashtable to avoid a more complex data structure, as
- // the use of the same id in multiple containers isn't a common occurance.
- //
- // Only one match with a given id per container is active at a time. When
- // a match is active, content is generated for it. When a match is
- // inactive, content is not generated for it. A match becomes active if
- // another match with the same id and container with a lower priority
- // isn't already active, and the match has a rule or conditions clause
- // which evaluates to true. The former is checked by comparing the value
- // of the QuerySetPriority method of the match with earlier matches. The
- // latter is checked with the DetermineMatchedRule method.
- //
- // Naturally, if a match with a lower priority is active, it overrides
- // the new match, so the new match is hooked up into the match linked
- // list as inactive, and no content is generated for it. If a match with a
- // higher priority is active, and the new match's conditions evaluate
- // to true, then this existing match with the higher priority needs to have
- // its generated content removed and replaced with the new match's
- // generated content.
- //
- // Similar situations apply when removing an existing match. If the match
- // is active, the existing generated content will need to be removed, and
- // a match of higher priority that is revealed may become active and need
- // to have content generated.
- //
- // Content removal and generation is done by the ReplaceMatch method which
- // is overridden for the content builder and tree builder to update the
- // generated output for each type.
- //
- // The code below handles all of the various cases and ensures that the
- // match lists are maintained properly.
- nsresult rv = NS_OK;
- int16_t ruleindex;
- nsTemplateRule* matchedrule = nullptr;
- // Indicates that the old match was active and must have its content
- // removed
- bool oldMatchWasActive = false;
- // acceptedmatch will be set to a new match that has to have new content
- // generated for it. If a new match doesn't need to have content
- // generated, (because for example, a match with a lower priority
- // already applies), then acceptedmatch will be null, but the match will
- // be still hooked up into the chain, since it may become active later
- // as other results are updated.
- nsTemplateMatch* acceptedmatch = nullptr;
- // When aOldResult is specified, removematch will be set to the
- // corresponding match. This match needs to be deleted as it no longer
- // applies. However, removedmatch will be null when aOldResult is null, or
- // when no match was found corresponding to aOldResult.
- nsTemplateMatch* removedmatch = nullptr;
- // These will be set when aNewResult is specified indicating to add a
- // result, but will end up replacing an existing match. The former
- // indicates a match being replaced that was active and had content
- // generated for it, while the latter indicates a match that wasn't active
- // and just needs to be deleted. Both may point to different matches. For
- // example, if the new match becomes active, replacing an inactive match,
- // the inactive match will need to be deleted. However, if another match
- // with a higher priority is active, the new match will override it, so
- // content will need to be generated for the new match and removed for
- // this existing active match.
- nsTemplateMatch* replacedmatch = nullptr;
- nsTemplateMatch* replacedmatchtodelete = nullptr;
- if (aOldResult) {
- nsTemplateMatch* firstmatch;
- if (mMatchMap.Get(aOldId, &firstmatch)) {
- nsTemplateMatch* oldmatch = firstmatch;
- nsTemplateMatch* prevmatch = nullptr;
- // look for the right match if there was more than one
- while (oldmatch && (oldmatch->mResult != aOldResult)) {
- prevmatch = oldmatch;
- oldmatch = oldmatch->mNext;
- }
- if (oldmatch) {
- nsTemplateMatch* findmatch = oldmatch->mNext;
- // Keep a reference so that linked list can be hooked up at
- // the end in case an error occurs.
- nsTemplateMatch* nextmatch = findmatch;
- if (oldmatch->IsActive()) {
- // Indicate that the old match was active so its content
- // will be removed later.
- oldMatchWasActive = true;
- // The match being removed is the active match, so scan
- // through the later matches to determine if one should
- // now become the active match.
- while (findmatch) {
- // only other matches with the same container should
- // now match, leave other containers alone
- if (findmatch->GetContainer() == aInsertionPoint) {
- nsTemplateQuerySet* qs =
- mQuerySets[findmatch->QuerySetPriority()];
-
- DetermineMatchedRule(aInsertionPoint, findmatch->mResult,
- qs, &matchedrule, &ruleindex);
- if (matchedrule) {
- rv = findmatch->RuleMatched(qs,
- matchedrule, ruleindex,
- findmatch->mResult);
- if (NS_FAILED(rv))
- return rv;
- acceptedmatch = findmatch;
- break;
- }
- }
- findmatch = findmatch->mNext;
- }
- }
- if (oldmatch == firstmatch) {
- // the match to remove is at the beginning
- if (oldmatch->mNext) {
- mMatchMap.Put(aOldId, oldmatch->mNext);
- }
- else {
- mMatchMap.Remove(aOldId);
- }
- }
- if (prevmatch)
- prevmatch->mNext = nextmatch;
- removedmatch = oldmatch;
- if (mFlags & eLoggingEnabled)
- OutputMatchToLog(aOldId, removedmatch, false);
- }
- }
- }
- nsTemplateMatch *newmatch = nullptr;
- if (aNewResult) {
- // only allow a result to be inserted into containers with a matching tag
- nsIAtom* tag = aQuerySet->GetTag();
- if (aInsertionPoint && tag &&
- tag != aInsertionPoint->NodeInfo()->NameAtom())
- return NS_OK;
- int32_t findpriority = aQuerySet->Priority();
- newmatch = nsTemplateMatch::Create(findpriority,
- aNewResult, aInsertionPoint);
- if (!newmatch)
- return NS_ERROR_OUT_OF_MEMORY;
- nsTemplateMatch* firstmatch;
- if (mMatchMap.Get(aNewId, &firstmatch)) {
- bool hasEarlierActiveMatch = false;
- // Scan through the existing matches to find where the new one
- // should be inserted. oldmatch will be set to the old match for
- // the same query and prevmatch will be set to the match before it.
- nsTemplateMatch* prevmatch = nullptr;
- nsTemplateMatch* oldmatch = firstmatch;
- while (oldmatch) {
- // Break out once we've reached a query in the list with a
- // lower priority. The new match will be inserted at this
- // location so that the match list is sorted by priority.
- int32_t priority = oldmatch->QuerySetPriority();
- if (priority > findpriority) {
- oldmatch = nullptr;
- break;
- }
- // look for matches that belong in the same container
- if (oldmatch->GetContainer() == aInsertionPoint) {
- if (priority == findpriority)
- break;
- // If a match with a lower priority is active, the new
- // match can't replace it.
- if (oldmatch->IsActive())
- hasEarlierActiveMatch = true;
- }
- prevmatch = oldmatch;
- oldmatch = oldmatch->mNext;
- }
- // At this point, oldmatch will either be null, or set to a match
- // with the same container and priority. If set, oldmatch will
- // need to be replaced by newmatch.
- if (oldmatch)
- newmatch->mNext = oldmatch->mNext;
- else if (prevmatch)
- newmatch->mNext = prevmatch->mNext;
- else
- newmatch->mNext = firstmatch;
- // hasEarlierActiveMatch will be set to true if a match with a
- // lower priority was found. The new match won't replace it in
- // this case. If hasEarlierActiveMatch is false, then the new match
- // may be become active if it matches one of the rules, and will
- // generate output. It's also possible however, that a match with
- // the same priority already exists, which means that the new match
- // will replace the old one. In this case, oldmatch will be set to
- // the old match. The content for the old match must be removed and
- // content for the new match generated in its place.
- if (! hasEarlierActiveMatch) {
- // If the old match was the active match, set replacedmatch to
- // indicate that it needs its content removed.
- if (oldmatch) {
- if (oldmatch->IsActive())
- replacedmatch = oldmatch;
- replacedmatchtodelete = oldmatch;
- }
- // check if the new result matches the rules
- rv = DetermineMatchedRule(aInsertionPoint, newmatch->mResult,
- aQuerySet, &matchedrule, &ruleindex);
- if (NS_FAILED(rv)) {
- nsTemplateMatch::Destroy(newmatch, false);
- return rv;
- }
- if (matchedrule) {
- rv = newmatch->RuleMatched(aQuerySet,
- matchedrule, ruleindex,
- newmatch->mResult);
- if (NS_FAILED(rv)) {
- nsTemplateMatch::Destroy(newmatch, false);
- return rv;
- }
- // acceptedmatch may have been set in the block handling
- // aOldResult earlier. If so, we would only get here when
- // that match has a higher priority than this new match.
- // As only one match can have content generated for it, it
- // is OK to set acceptedmatch here to the new match,
- // ignoring the other one.
- acceptedmatch = newmatch;
- // Clear the matched state of the later results for the
- // same container.
- nsTemplateMatch* clearmatch = newmatch->mNext;
- while (clearmatch) {
- if (clearmatch->GetContainer() == aInsertionPoint &&
- clearmatch->IsActive()) {
- clearmatch->SetInactive();
- // Replacedmatch should be null here. If not, it
- // means that two matches were active which isn't
- // a valid state
- NS_ASSERTION(!replacedmatch,
- "replaced match already set");
- replacedmatch = clearmatch;
- break;
- }
- clearmatch = clearmatch->mNext;
- }
- }
- else if (oldmatch && oldmatch->IsActive()) {
- // The result didn't match the rules, so look for a later
- // one. However, only do this if the old match was the
- // active match.
- newmatch = newmatch->mNext;
- while (newmatch) {
- if (newmatch->GetContainer() == aInsertionPoint) {
- rv = DetermineMatchedRule(aInsertionPoint, newmatch->mResult,
- aQuerySet, &matchedrule, &ruleindex);
- if (NS_FAILED(rv)) {
- nsTemplateMatch::Destroy(newmatch, false);
- return rv;
- }
- if (matchedrule) {
- rv = newmatch->RuleMatched(aQuerySet,
- matchedrule, ruleindex,
- newmatch->mResult);
- if (NS_FAILED(rv)) {
- nsTemplateMatch::Destroy(newmatch, false);
- return rv;
- }
- acceptedmatch = newmatch;
- break;
- }
- }
- newmatch = newmatch->mNext;
- }
- }
- // put the match in the map if there isn't a previous match
- if (! prevmatch) {
- mMatchMap.Put(aNewId, newmatch);
- }
- }
- // hook up the match last in case an error occurs
- if (prevmatch)
- prevmatch->mNext = newmatch;
- }
- else {
- // The id is not used in the hashtable yet so create a new match
- // and add it to the hashtable.
- rv = DetermineMatchedRule(aInsertionPoint, aNewResult,
- aQuerySet, &matchedrule, &ruleindex);
- if (NS_FAILED(rv)) {
- nsTemplateMatch::Destroy(newmatch, false);
- return rv;
- }
- if (matchedrule) {
- rv = newmatch->RuleMatched(aQuerySet, matchedrule,
- ruleindex, aNewResult);
- if (NS_FAILED(rv)) {
- nsTemplateMatch::Destroy(newmatch, false);
- return rv;
- }
- acceptedmatch = newmatch;
- }
- mMatchMap.Put(aNewId, newmatch);
- }
- }
- // The ReplaceMatch method is builder specific and removes the generated
- // content for a match.
- // Remove the content for a match that was active and needs to be replaced.
- if (replacedmatch) {
- rv = ReplaceMatch(replacedmatch->mResult, nullptr, nullptr,
- aInsertionPoint);
- if (mFlags & eLoggingEnabled)
- OutputMatchToLog(aNewId, replacedmatch, false);
- }
- // remove a match that needs to be deleted.
- if (replacedmatchtodelete)
- nsTemplateMatch::Destroy(replacedmatchtodelete, true);
- // If the old match was active, the content for it needs to be removed.
- // If the old match was not active, it shouldn't have had any content,
- // so just pass null to ReplaceMatch. If acceptedmatch was set, then
- // content needs to be generated for a new match.
- if (oldMatchWasActive || acceptedmatch)
- rv = ReplaceMatch(oldMatchWasActive ? aOldResult : nullptr,
- acceptedmatch, matchedrule, aInsertionPoint);
- // delete the old match that was replaced
- if (removedmatch)
- nsTemplateMatch::Destroy(removedmatch, true);
- if (mFlags & eLoggingEnabled && newmatch)
- OutputMatchToLog(aNewId, newmatch, true);
- return rv;
- }
- NS_IMETHODIMP
- nsXULTemplateBuilder::ResultBindingChanged(nsIXULTemplateResult* aResult)
- {
- // A binding update is used when only the values of the bindings have
- // changed, so the same rule still applies. Just synchronize the content.
- // The new result will have the new values.
- NS_ENSURE_ARG_POINTER(aResult);
- if (!mRoot || !mQueriesCompiled)
- return NS_OK;
- return SynchronizeResult(aResult);
- }
- NS_IMETHODIMP
- nsXULTemplateBuilder::GetRootResult(nsIXULTemplateResult** aResult)
- {
- *aResult = mRootResult;
- NS_IF_ADDREF(*aResult);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsXULTemplateBuilder::GetResultForId(const nsAString& aId,
- nsIXULTemplateResult** aResult)
- {
- if (aId.IsEmpty())
- return NS_ERROR_INVALID_ARG;
- nsCOMPtr<nsIRDFResource> resource;
- gRDFService->GetUnicodeResource(aId, getter_AddRefs(resource));
- *aResult = nullptr;
- nsTemplateMatch* match;
- if (mMatchMap.Get(resource, &match)) {
- // find the active match
- while (match) {
- if (match->IsActive()) {
- *aResult = match->mResult;
- NS_IF_ADDREF(*aResult);
- break;
- }
- match = match->mNext;
- }
- }
- return NS_OK;
- }
- NS_IMETHODIMP
- nsXULTemplateBuilder::GetResultForContent(nsIDOMElement* aContent,
- nsIXULTemplateResult** aResult)
- {
- *aResult = nullptr;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsXULTemplateBuilder::AddListener(nsIXULBuilderListener* aListener)
- {
- NS_ENSURE_ARG(aListener);
- if (!mListeners.AppendObject(aListener))
- return NS_ERROR_OUT_OF_MEMORY;
- return NS_OK;
- }
- NS_IMETHODIMP
- nsXULTemplateBuilder::RemoveListener(nsIXULBuilderListener* aListener)
- {
- NS_ENSURE_ARG(aListener);
- mListeners.RemoveObject(aListener);
- return NS_OK;
- }
- NS_IMETHODIMP
- nsXULTemplateBuilder::Observe(nsISupports* aSubject,
- const char* aTopic,
- const char16_t* aData)
- {
- // Uuuuber hack to clean up circular references that the cycle collector
- // doesn't know about. See bug 394514.
- if (!strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC)) {
- if (nsCOMPtr<mozIDOMWindow> window = do_QueryInterface(aSubject)) {
- nsCOMPtr<nsIDocument> doc =
- nsPIDOMWindowInner::From(window)->GetExtantDoc();
- if (doc && doc == mObservedDocument)
- NodeWillBeDestroyed(doc);
- }
- }
- return NS_OK;
- }
- //----------------------------------------------------------------------
- //
- // nsIDocumentOberver interface
- //
- void
- nsXULTemplateBuilder::AttributeChanged(nsIDocument* aDocument,
- Element* aElement,
- int32_t aNameSpaceID,
- nsIAtom* aAttribute,
- int32_t aModType,
- const nsAttrValue* aOldValue)
- {
- if (aElement == mRoot && aNameSpaceID == kNameSpaceID_None) {
- // Check for a change to the 'ref' attribute on an atom, in which
- // case we may need to nuke and rebuild the entire content model
- // beneath the element.
- if (aAttribute == nsGkAtoms::ref)
- nsContentUtils::AddScriptRunner(
- NewRunnableMethod(this, &nsXULTemplateBuilder::RunnableRebuild));
- // Check for a change to the 'datasources' attribute. If so, setup
- // mDB by parsing the new value and rebuild.
- else if (aAttribute == nsGkAtoms::datasources) {
- nsContentUtils::AddScriptRunner(
- NewRunnableMethod(this, &nsXULTemplateBuilder::RunnableLoadAndRebuild));
- }
- }
- }
- void
- nsXULTemplateBuilder::ContentRemoved(nsIDocument* aDocument,
- nsIContent* aContainer,
- nsIContent* aChild,
- int32_t aIndexInContainer,
- nsIContent* aPreviousSibling)
- {
- if (mRoot && nsContentUtils::ContentIsDescendantOf(mRoot, aChild)) {
- RefPtr<nsXULTemplateBuilder> kungFuDeathGrip(this);
- if (mQueryProcessor)
- mQueryProcessor->Done();
- // Pass false to Uninit since content is going away anyway
- nsContentUtils::AddScriptRunner(
- NewRunnableMethod(this, &nsXULTemplateBuilder::UninitFalse));
- MOZ_ASSERT(aDocument == mObservedDocument);
- StopObserving();
- nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(aDocument);
- if (xuldoc)
- xuldoc->SetTemplateBuilderFor(mRoot, nullptr);
- // clear the template state when removing content so that template
- // content will be regenerated again if the content is reinserted
- nsXULElement *xulcontent = nsXULElement::FromContent(mRoot);
- if (xulcontent)
- xulcontent->ClearTemplateGenerated();
- CleanUp(true);
- mDB = nullptr;
- mCompDB = nullptr;
- mDataSource = nullptr;
- }
- }
- void
- nsXULTemplateBuilder::NodeWillBeDestroyed(const nsINode* aNode)
- {
- // The call to RemoveObserver could release the last reference to
- // |this|, so hold another reference.
- RefPtr<nsXULTemplateBuilder> kungFuDeathGrip(this);
- // Break circular references
- if (mQueryProcessor)
- mQueryProcessor->Done();
- mDataSource = nullptr;
- mDB = nullptr;
- mCompDB = nullptr;
- nsContentUtils::AddScriptRunner(
- NewRunnableMethod(this, &nsXULTemplateBuilder::UninitTrue));
- }
- //----------------------------------------------------------------------
- //
- // Implementation methods
- //
- nsresult
- nsXULTemplateBuilder::LoadDataSources(nsIDocument* aDocument,
- bool* aShouldDelayBuilding)
- {
- NS_PRECONDITION(mRoot != nullptr, "not initialized");
- nsresult rv;
- bool isRDFQuery = false;
-
- // we'll set these again later, after we create a new composite ds
- mDB = nullptr;
- mCompDB = nullptr;
- mDataSource = nullptr;
- *aShouldDelayBuilding = false;
- nsAutoString datasources;
- mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::datasources, datasources);
- nsAutoString querytype;
- mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::querytype, querytype);
- // create the query processor. The querytype attribute on the root element
- // may be used to create one of a specific type.
-
- // XXX should non-chrome be restricted to specific names?
- if (querytype.IsEmpty())
- querytype.AssignLiteral("rdf");
- if (querytype.EqualsLiteral("rdf")) {
- isRDFQuery = true;
- mQueryProcessor = new nsXULTemplateQueryProcessorRDF();
- }
- else if (querytype.EqualsLiteral("xml")) {
- mQueryProcessor = new nsXULTemplateQueryProcessorXML();
- }
- else if (querytype.EqualsLiteral("storage")) {
- mQueryProcessor = new nsXULTemplateQueryProcessorStorage();
- }
- else {
- nsAutoCString cid(NS_QUERY_PROCESSOR_CONTRACTID_PREFIX);
- AppendUTF16toUTF8(querytype, cid);
- mQueryProcessor = do_CreateInstance(cid.get(), &rv);
- if (!mQueryProcessor) {
- nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_INVALID_QUERYPROCESSOR);
- return rv;
- }
- }
- rv = LoadDataSourceUrls(aDocument, datasources,
- isRDFQuery, aShouldDelayBuilding);
- NS_ENSURE_SUCCESS(rv, rv);
- // Now set the database on the element, so that script writers can
- // access it.
- nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(aDocument);
- if (xuldoc)
- xuldoc->SetTemplateBuilderFor(mRoot, this);
- if (!mRoot->IsXULElement()) {
- // Hmm. This must be an HTML element. Try to set it as a
- // JS property "by hand".
- InitHTMLTemplateRoot();
- }
-
- return NS_OK;
- }
-
- nsresult
- nsXULTemplateBuilder::LoadDataSourceUrls(nsIDocument* aDocument,
- const nsAString& aDataSources,
- bool aIsRDFQuery,
- bool* aShouldDelayBuilding)
- {
- // Grab the doc's principal...
- nsIPrincipal *docPrincipal = aDocument->NodePrincipal();
- NS_ASSERTION(docPrincipal == mRoot->NodePrincipal(),
- "Principal mismatch? Which one to use?");
- bool isTrusted = false;
- nsresult rv = IsSystemPrincipal(docPrincipal, &isTrusted);
- NS_ENSURE_SUCCESS(rv, rv);
- // Parse datasources: they are assumed to be a whitespace
- // separated list of URIs; e.g.,
- //
- // rdf:bookmarks rdf:history http://foo.bar.com/blah.cgi?baz=9
- //
- nsIURI *docurl = aDocument->GetDocumentURI();
- nsCOMPtr<nsIMutableArray> uriList = do_CreateInstance(NS_ARRAY_CONTRACTID);
- if (!uriList)
- return NS_ERROR_FAILURE;
- nsAutoString datasources(aDataSources);
- uint32_t first = 0;
- while (1) {
- while (first < datasources.Length() && nsCRT::IsAsciiSpace(datasources.CharAt(first)))
- ++first;
- if (first >= datasources.Length())
- break;
- uint32_t last = first;
- while (last < datasources.Length() && !nsCRT::IsAsciiSpace(datasources.CharAt(last)))
- ++last;
- nsAutoString uriStr;
- datasources.Mid(uriStr, first, last - first);
- first = last + 1;
- // A special 'dummy' datasource
- if (uriStr.EqualsLiteral("rdf:null"))
- continue;
- if (uriStr.CharAt(0) == '#') {
- // ok, the datasource is certainly a node of the current document
- nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(aDocument);
- nsCOMPtr<nsIDOMElement> dsnode;
- domdoc->GetElementById(Substring(uriStr, 1),
- getter_AddRefs(dsnode));
- if (dsnode)
- uriList->AppendElement(dsnode, false);
- continue;
- }
- // N.B. that `failure' (e.g., because it's an unknown
- // protocol) leaves uriStr unaltered.
- NS_MakeAbsoluteURI(uriStr, uriStr, docurl);
- nsCOMPtr<nsIURI> uri;
- rv = NS_NewURI(getter_AddRefs(uri), uriStr);
- if (NS_FAILED(rv) || !uri)
- continue; // Necko will barf if our URI is weird
- // don't add the uri to the list if the document is not allowed to
- // load it
- if (!isTrusted && NS_FAILED(docPrincipal->CheckMayLoad(uri, true, false)))
- continue;
- uriList->AppendElement(uri, false);
- }
- nsCOMPtr<nsIDOMNode> rootNode = do_QueryInterface(mRoot);
- rv = mQueryProcessor->GetDatasource(uriList,
- rootNode,
- isTrusted,
- this,
- aShouldDelayBuilding,
- getter_AddRefs(mDataSource));
- NS_ENSURE_SUCCESS(rv, rv);
- if (aIsRDFQuery && mDataSource) {
- // check if we were given an inference engine type
- nsCOMPtr<nsIRDFInferDataSource> inferDB = do_QueryInterface(mDataSource);
- if (inferDB) {
- nsCOMPtr<nsIRDFDataSource> ds;
- inferDB->GetBaseDataSource(getter_AddRefs(ds));
- if (ds)
- mCompDB = do_QueryInterface(ds);
- }
- if (!mCompDB)
- mCompDB = do_QueryInterface(mDataSource);
- mDB = do_QueryInterface(mDataSource);
- }
- if (!mDB && isTrusted) {
- gRDFService->GetDataSource("rdf:local-store", getter_AddRefs(mDB));
- }
- return NS_OK;
- }
- nsresult
- nsXULTemplateBuilder::InitHTMLTemplateRoot()
- {
- // Use XPConnect and the JS APIs to whack mDB and this as the
- // 'database' and 'builder' properties onto aElement.
- nsresult rv;
- nsCOMPtr<nsIDocument> doc = mRoot->GetComposedDoc();
- NS_ASSERTION(doc, "no document");
- if (! doc)
- return NS_ERROR_UNEXPECTED;
- nsCOMPtr<nsIScriptGlobalObject> global =
- do_QueryInterface(doc->GetWindow());
- if (! global)
- return NS_ERROR_UNEXPECTED;
- nsCOMPtr<nsIGlobalObject> innerWin =
- do_QueryInterface(doc->GetInnerWindow());
- // We are going to run script via JS_SetProperty, so we need a script entry
- // point, but as this is XUL related it does not appear in the HTML spec.
- AutoEntryScript aes(innerWin, "nsXULTemplateBuilder creation", true);
- JSContext* jscontext = aes.cx();
- JS::Rooted<JS::Value> v(jscontext);
- rv = nsContentUtils::WrapNative(jscontext, mRoot, mRoot, &v);
- NS_ENSURE_SUCCESS(rv, rv);
- JS::Rooted<JSObject*> jselement(jscontext, v.toObjectOrNull());
- if (mDB) {
- // database
- JS::Rooted<JS::Value> jsdatabase(jscontext);
- rv = nsContentUtils::WrapNative(jscontext, mDB,
- &NS_GET_IID(nsIRDFCompositeDataSource),
- &jsdatabase);
- NS_ENSURE_SUCCESS(rv, rv);
- bool ok = JS_SetProperty(jscontext, jselement, "database", jsdatabase);
- NS_ASSERTION(ok, "unable to set database property");
- if (! ok)
- return NS_ERROR_FAILURE;
- }
- {
- // builder
- JS::Rooted<JS::Value> jsbuilder(jscontext);
- rv = nsContentUtils::WrapNative(jscontext,
- static_cast<nsIXULTemplateBuilder*>(this),
- &NS_GET_IID(nsIXULTemplateBuilder),
- &jsbuilder);
- NS_ENSURE_SUCCESS(rv, rv);
- bool ok = JS_SetProperty(jscontext, jselement, "builder", jsbuilder);
- if (! ok)
- return NS_ERROR_FAILURE;
- }
- return NS_OK;
- }
- nsresult
- nsXULTemplateBuilder::DetermineMatchedRule(nsIContent *aContainer,
- nsIXULTemplateResult* aResult,
- nsTemplateQuerySet* aQuerySet,
- nsTemplateRule** aMatchedRule,
- int16_t *aRuleIndex)
- {
- // iterate through the rules and look for one that the result matches
- int16_t count = aQuerySet->RuleCount();
- for (int16_t r = 0; r < count; r++) {
- nsTemplateRule* rule = aQuerySet->GetRuleAt(r);
- // If a tag was specified, it must match the tag of the container
- // where content is being inserted.
- nsIAtom* tag = rule->GetTag();
- if ((!aContainer || !tag ||
- tag == aContainer->NodeInfo()->NameAtom()) &&
- rule->CheckMatch(aResult)) {
- *aMatchedRule = rule;
- *aRuleIndex = r;
- return NS_OK;
- }
- }
- *aRuleIndex = -1;
- *aMatchedRule = nullptr;
- return NS_OK;
- }
- void
- nsXULTemplateBuilder::ParseAttribute(const nsAString& aAttributeValue,
- void (*aVariableCallback)(nsXULTemplateBuilder*, const nsAString&, void*),
- void (*aTextCallback)(nsXULTemplateBuilder*, const nsAString&, void*),
- void* aClosure)
- {
- nsAString::const_iterator done_parsing;
- aAttributeValue.EndReading(done_parsing);
- nsAString::const_iterator iter;
- aAttributeValue.BeginReading(iter);
- nsAString::const_iterator mark(iter), backup(iter);
- for (; iter != done_parsing; backup = ++iter) {
- // A variable is either prefixed with '?' (in the extended
- // syntax) or "rdf:" (in the simple syntax).
- bool isvar;
- if (*iter == char16_t('?') && (++iter != done_parsing)) {
- isvar = true;
- }
- else if ((*iter == char16_t('r') && (++iter != done_parsing)) &&
- (*iter == char16_t('d') && (++iter != done_parsing)) &&
- (*iter == char16_t('f') && (++iter != done_parsing)) &&
- (*iter == char16_t(':') && (++iter != done_parsing))) {
- isvar = true;
- }
- else {
- isvar = false;
- }
- if (! isvar) {
- // It's not a variable, or we ran off the end of the
- // string after the initial variable prefix. Since we may
- // have slurped down some characters before realizing that
- // fact, back up to the point where we started.
- iter = backup;
- continue;
- }
- else if (backup != mark && aTextCallback) {
- // Okay, we've found a variable, and there's some vanilla
- // text that's been buffered up. Flush it.
- (*aTextCallback)(this, Substring(mark, backup), aClosure);
- }
- if (*iter == char16_t('?')) {
- // Well, it was not really a variable, but "??". We use one
- // question mark (the second one, actually) literally.
- mark = iter;
- continue;
- }
- // Construct a substring that is the symbol we need to look up
- // in the rule's symbol table. The symbol is terminated by a
- // space character, a caret, or the end of the string,
- // whichever comes first.
- nsAString::const_iterator first(backup);
- char16_t c = 0;
- while (iter != done_parsing) {
- c = *iter;
- if ((c == char16_t(' ')) || (c == char16_t('^')))
- break;
- ++iter;
- }
- nsAString::const_iterator last(iter);
- // Back up so we don't consume the terminating character
- // *unless* the terminating character was a caret: the caret
- // means "concatenate with no space in between".
- if (c != char16_t('^'))
- --iter;
- (*aVariableCallback)(this, Substring(first, last), aClosure);
- mark = iter;
- ++mark;
- }
- if (backup != mark && aTextCallback) {
- // If there's any text left over, then fire the text callback
- (*aTextCallback)(this, Substring(mark, backup), aClosure);
- }
- }
- struct MOZ_STACK_CLASS SubstituteTextClosure {
- SubstituteTextClosure(nsIXULTemplateResult* aResult, nsAString& aString)
- : result(aResult), str(aString) {}
- // some datasources are lazily initialized or modified while values are
- // being retrieved, causing results to be removed. Due to this, hold a
- // strong reference to the result.
- nsCOMPtr<nsIXULTemplateResult> result;
- nsAString& str;
- };
- nsresult
- nsXULTemplateBuilder::SubstituteText(nsIXULTemplateResult* aResult,
- const nsAString& aAttributeValue,
- nsAString& aString)
- {
- // See if it's the special value "..."
- if (aAttributeValue.EqualsLiteral("...")) {
- aResult->GetId(aString);
- return NS_OK;
- }
- // Reasonable guess at how big it should be
- aString.SetCapacity(aAttributeValue.Length());
- SubstituteTextClosure closure(aResult, aString);
- ParseAttribute(aAttributeValue,
- SubstituteTextReplaceVariable,
- SubstituteTextAppendText,
- &closure);
- return NS_OK;
- }
- void
- nsXULTemplateBuilder::SubstituteTextAppendText(nsXULTemplateBuilder* aThis,
- const nsAString& aText,
- void* aClosure)
- {
- // Append aString to the closure's result
- SubstituteTextClosure* c = static_cast<SubstituteTextClosure*>(aClosure);
- c->str.Append(aText);
- }
- void
- nsXULTemplateBuilder::SubstituteTextReplaceVariable(nsXULTemplateBuilder* aThis,
- const nsAString& aVariable,
- void* aClosure)
- {
- // Substitute the value for the variable and append to the
- // closure's result.
- SubstituteTextClosure* c = static_cast<SubstituteTextClosure*>(aClosure);
- nsAutoString replacementText;
- // The symbol "rdf:*" is special, and means "this guy's URI"
- if (aVariable.EqualsLiteral("rdf:*")){
- c->result->GetId(replacementText);
- }
- else {
- // Got a variable; get the value it's assigned to
- nsCOMPtr<nsIAtom> var = NS_Atomize(aVariable);
- c->result->GetBindingFor(var, replacementText);
- }
- c->str += replacementText;
- }
- bool
- nsXULTemplateBuilder::IsTemplateElement(nsIContent* aContent)
- {
- return aContent->NodeInfo()->Equals(nsGkAtoms::_template,
- kNameSpaceID_XUL);
- }
- nsresult
- nsXULTemplateBuilder::GetTemplateRoot(nsIContent** aResult)
- {
- NS_PRECONDITION(mRoot != nullptr, "not initialized");
- if (! mRoot)
- return NS_ERROR_NOT_INITIALIZED;
- // First, check and see if the root has a template attribute. This
- // allows a template to be specified "out of line"; e.g.,
- //
- // <window>
- // <foo template="MyTemplate">...</foo>
- // <template id="MyTemplate">...</template>
- // </window>
- //
- nsAutoString templateID;
- mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::_template, templateID);
- if (! templateID.IsEmpty()) {
- nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(mRoot->GetComposedDoc());
- if (! domDoc)
- return NS_OK;
- nsCOMPtr<nsIDOMElement> domElement;
- domDoc->GetElementById(templateID, getter_AddRefs(domElement));
- if (domElement) {
- nsCOMPtr<nsIContent> content = do_QueryInterface(domElement);
- NS_ENSURE_STATE(content &&
- !nsContentUtils::ContentIsDescendantOf(mRoot,
- content));
- content.forget(aResult);
- return NS_OK;
- }
- }
- // If root node has no template attribute, then look for a child
- // node which is a template tag.
- for (nsIContent* child = mRoot->GetFirstChild();
- child;
- child = child->GetNextSibling()) {
- if (IsTemplateElement(child)) {
- NS_ADDREF(*aResult = child);
- return NS_OK;
- }
- }
- // Look through the anonymous children as well. Although FlattenedChildIterator
- // will find a template element that has been placed in an insertion point, many
- // bindings do not have a specific insertion point for the template element, which
- // would cause it to not be part of the flattened content tree. The check above to
- // check the explicit children as well handles this case.
- FlattenedChildIterator iter(mRoot);
- for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
- if (IsTemplateElement(child)) {
- NS_ADDREF(*aResult = child);
- return NS_OK;
- }
- }
- *aResult = nullptr;
- return NS_OK;
- }
- nsresult
- nsXULTemplateBuilder::CompileQueries()
- {
- nsCOMPtr<nsIContent> tmpl;
- GetTemplateRoot(getter_AddRefs(tmpl));
- if (! tmpl)
- return NS_OK;
- if (! mRoot)
- return NS_ERROR_NOT_INITIALIZED;
- // Determine if there are any special settings we need to observe
- mFlags = 0;
- nsAutoString flags;
- mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::flags, flags);
- // if the dont-test-empty flag is set, containers should not be checked to
- // see if they are empty. If dont-recurse is set, then don't process the
- // template recursively and only show one level of results. The logging
- // flag logs errors and results to the console, which is useful when
- // debugging templates.
- nsWhitespaceTokenizer tokenizer(flags);
- while (tokenizer.hasMoreTokens()) {
- const nsDependentSubstring& token(tokenizer.nextToken());
- if (token.EqualsLiteral("dont-test-empty"))
- mFlags |= eDontTestEmpty;
- else if (token.EqualsLiteral("dont-recurse"))
- mFlags |= eDontRecurse;
- else if (token.EqualsLiteral("logging"))
- mFlags |= eLoggingEnabled;
- }
- // always enable logging if the debug setting is used
- if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug))
- mFlags |= eLoggingEnabled;
- nsCOMPtr<nsIDOMNode> rootnode = do_QueryInterface(mRoot);
- nsresult rv =
- mQueryProcessor->InitializeForBuilding(mDataSource, this, rootnode);
- if (NS_FAILED(rv))
- return rv;
- // Set the "container" and "member" variables, if the user has specified
- // them. The container variable may be specified with the container
- // attribute on the <template> and the member variable may be specified
- // using the member attribute or the value of the uri attribute inside the
- // first action body in the template. If not specified, the container
- // variable defaults to '?uri' and the member variable defaults to '?' or
- // 'rdf:*' for simple queries.
- // For RDF queries, the container variable may also be set via the
- // <content> tag.
- nsAutoString containervar;
- tmpl->GetAttr(kNameSpaceID_None, nsGkAtoms::container, containervar);
- if (containervar.IsEmpty())
- mRefVariable = NS_Atomize("?uri");
- else
- mRefVariable = NS_Atomize(containervar);
- nsAutoString membervar;
- tmpl->GetAttr(kNameSpaceID_None, nsGkAtoms::member, membervar);
- if (membervar.IsEmpty())
- mMemberVariable = nullptr;
- else
- mMemberVariable = NS_Atomize(membervar);
- nsTemplateQuerySet* queryset = new nsTemplateQuerySet(0);
- if (!mQuerySets.AppendElement(queryset)) {
- delete queryset;
- return NS_ERROR_OUT_OF_MEMORY;
- }
- bool canUseTemplate = false;
- int32_t priority = 0;
- rv = CompileTemplate(tmpl, queryset, false, &priority, &canUseTemplate);
- if (NS_FAILED(rv) || !canUseTemplate) {
- for (int32_t q = mQuerySets.Length() - 1; q >= 0; q--) {
- nsTemplateQuerySet* qs = mQuerySets[q];
- delete qs;
- }
- mQuerySets.Clear();
- }
- mQueriesCompiled = true;
- return NS_OK;
- }
- nsresult
- nsXULTemplateBuilder::CompileTemplate(nsIContent* aTemplate,
- nsTemplateQuerySet* aQuerySet,
- bool aIsQuerySet,
- int32_t* aPriority,
- bool* aCanUseTemplate)
- {
- NS_ASSERTION(aQuerySet, "No queryset supplied");
- nsresult rv = NS_OK;
- bool isQuerySetMode = false;
- bool hasQuerySet = false, hasRule = false, hasQuery = false;
- for (nsIContent* rulenode = aTemplate->GetFirstChild();
- rulenode;
- rulenode = rulenode->GetNextSibling()) {
- mozilla::dom::NodeInfo *ni = rulenode->NodeInfo();
- // don't allow more queries than can be supported
- if (*aPriority == INT16_MAX)
- return NS_ERROR_FAILURE;
- // XXXndeakin queryset isn't a good name for this tag since it only
- // ever contains one query
- if (!aIsQuerySet && ni->Equals(nsGkAtoms::queryset, kNameSpaceID_XUL)) {
- if (hasRule || hasQuery) {
- nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_INVALID_QUERYSET);
- continue;
- }
- isQuerySetMode = true;
- // only create a queryset for those after the first since the
- // first one is always created by CompileQueries
- if (hasQuerySet) {
- aQuerySet = new nsTemplateQuerySet(++*aPriority);
- // once the queryset is appended to the mQuerySets list, it
- // will be removed by CompileQueries if an error occurs
- if (!mQuerySets.AppendElement(aQuerySet)) {
- delete aQuerySet;
- return NS_ERROR_OUT_OF_MEMORY;
- }
- }
- hasQuerySet = true;
- rv = CompileTemplate(rulenode, aQuerySet, true, aPriority, aCanUseTemplate);
- if (NS_FAILED(rv))
- return rv;
- }
- // once a queryset is used, everything must be a queryset
- if (isQuerySetMode)
- continue;
- if (ni->Equals(nsGkAtoms::rule, kNameSpaceID_XUL)) {
- nsCOMPtr<nsIContent> action;
- nsXULContentUtils::FindChildByTag(rulenode,
- kNameSpaceID_XUL,
- nsGkAtoms::action,
- getter_AddRefs(action));
- if (action){
- nsCOMPtr<nsIAtom> memberVariable = mMemberVariable;
- if (!memberVariable) {
- memberVariable = DetermineMemberVariable(action);
- if (!memberVariable) {
- nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_NO_MEMBERVAR);
- continue;
- }
- }
- if (hasQuery) {
- nsCOMPtr<nsIAtom> tag;
- DetermineRDFQueryRef(aQuerySet->mQueryNode,
- getter_AddRefs(tag));
- if (tag)
- aQuerySet->SetTag(tag);
- if (! aQuerySet->mCompiledQuery) {
- nsCOMPtr<nsIDOMNode> query(do_QueryInterface(aQuerySet->mQueryNode));
- rv = mQueryProcessor->CompileQuery(this, query,
- mRefVariable, memberVariable,
- getter_AddRefs(aQuerySet->mCompiledQuery));
- if (NS_FAILED(rv))
- return rv;
- }
- if (aQuerySet->mCompiledQuery) {
- rv = CompileExtendedQuery(rulenode, action, memberVariable,
- aQuerySet);
- if (NS_FAILED(rv))
- return rv;
- *aCanUseTemplate = true;
- }
- }
- else {
- // backwards-compatible RDF template syntax where there is
- // an <action> node but no <query> node. In this case,
- // use the conditions as if it was the query.
- nsCOMPtr<nsIContent> conditions;
- nsXULContentUtils::FindChildByTag(rulenode,
- kNameSpaceID_XUL,
- nsGkAtoms::conditions,
- getter_AddRefs(conditions));
- if (conditions) {
- // create a new queryset if one hasn't been created already
- if (hasQuerySet) {
- aQuerySet = new nsTemplateQuerySet(++*aPriority);
- if (!mQuerySets.AppendElement(aQuerySet)) {
- delete aQuerySet;
- return NS_ERROR_OUT_OF_MEMORY;
- }
- }
- nsCOMPtr<nsIAtom> tag;
- DetermineRDFQueryRef(conditions, getter_AddRefs(tag));
- if (tag)
- aQuerySet->SetTag(tag);
- hasQuerySet = true;
- nsCOMPtr<nsIDOMNode> conditionsnode(do_QueryInterface(conditions));
- aQuerySet->mQueryNode = conditions;
- rv = mQueryProcessor->CompileQuery(this, conditionsnode,
- mRefVariable,
- memberVariable,
- getter_AddRefs(aQuerySet->mCompiledQuery));
- if (NS_FAILED(rv))
- return rv;
- if (aQuerySet->mCompiledQuery) {
- rv = CompileExtendedQuery(rulenode, action, memberVariable,
- aQuerySet);
- if (NS_FAILED(rv))
- return rv;
- *aCanUseTemplate = true;
- }
- }
- }
- }
- else {
- if (hasQuery)
- continue;
- // a new queryset must always be created in this case
- if (hasQuerySet) {
- aQuerySet = new nsTemplateQuerySet(++*aPriority);
- if (!mQuerySets.AppendElement(aQuerySet)) {
- delete aQuerySet;
- return NS_ERROR_OUT_OF_MEMORY;
- }
- }
- hasQuerySet = true;
- rv = CompileSimpleQuery(rulenode, aQuerySet, aCanUseTemplate);
- if (NS_FAILED(rv))
- return rv;
- }
- hasRule = true;
- }
- else if (ni->Equals(nsGkAtoms::query, kNameSpaceID_XUL)) {
- if (hasQuery)
- continue;
- aQuerySet->mQueryNode = rulenode;
- hasQuery = true;
- }
- else if (ni->Equals(nsGkAtoms::action, kNameSpaceID_XUL)) {
- // the query must appear before the action
- if (! hasQuery)
- continue;
- nsCOMPtr<nsIAtom> tag;
- DetermineRDFQueryRef(aQuerySet->mQueryNode, getter_AddRefs(tag));
- if (tag)
- aQuerySet->SetTag(tag);
- nsCOMPtr<nsIAtom> memberVariable = mMemberVariable;
- if (!memberVariable) {
- memberVariable = DetermineMemberVariable(rulenode);
- if (!memberVariable) {
- nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_NO_MEMBERVAR);
- continue;
- }
- }
- nsCOMPtr<nsIDOMNode> query(do_QueryInterface(aQuerySet->mQueryNode));
- rv = mQueryProcessor->CompileQuery(this, query,
- mRefVariable, memberVariable,
- getter_AddRefs(aQuerySet->mCompiledQuery));
- if (aQuerySet->mCompiledQuery) {
- nsTemplateRule* rule = aQuerySet->NewRule(aTemplate, rulenode, aQuerySet);
- if (! rule)
- return NS_ERROR_OUT_OF_MEMORY;
- rule->SetVars(mRefVariable, memberVariable);
- *aCanUseTemplate = true;
- return NS_OK;
- }
- }
- }
- if (! hasRule && ! hasQuery && ! hasQuerySet) {
- // if no rules are specified in the template, then the contents of the
- // <template> tag are the one-and-only template.
- rv = CompileSimpleQuery(aTemplate, aQuerySet, aCanUseTemplate);
- }
- return rv;
- }
- nsresult
- nsXULTemplateBuilder::CompileExtendedQuery(nsIContent* aRuleElement,
- nsIContent* aActionElement,
- nsIAtom* aMemberVariable,
- nsTemplateQuerySet* aQuerySet)
- {
- // Compile an "extended" <template> rule. An extended rule may have
- // a <conditions> child, an <action> child, and a <bindings> child.
- nsresult rv;
- nsTemplateRule* rule = aQuerySet->NewRule(aRuleElement, aActionElement, aQuerySet);
- if (! rule)
- return NS_ERROR_OUT_OF_MEMORY;
- nsCOMPtr<nsIContent> conditions;
- nsXULContentUtils::FindChildByTag(aRuleElement,
- kNameSpaceID_XUL,
- nsGkAtoms::conditions,
- getter_AddRefs(conditions));
- // allow the conditions to be placed directly inside the rule
- if (!conditions)
- conditions = aRuleElement;
-
- rv = CompileConditions(rule, conditions);
- // If the rule compilation failed, then we have to bail.
- if (NS_FAILED(rv)) {
- aQuerySet->RemoveRule(rule);
- return rv;
- }
- rule->SetVars(mRefVariable, aMemberVariable);
- // If we've got bindings, add 'em.
- nsCOMPtr<nsIContent> bindings;
- nsXULContentUtils::FindChildByTag(aRuleElement,
- kNameSpaceID_XUL,
- nsGkAtoms::bindings,
- getter_AddRefs(bindings));
- // allow bindings to be placed directly inside rule
- if (!bindings)
- bindings = aRuleElement;
- rv = CompileBindings(rule, bindings);
- NS_ENSURE_SUCCESS(rv, rv);
- return NS_OK;
- }
- already_AddRefed<nsIAtom>
- nsXULTemplateBuilder::DetermineMemberVariable(nsIContent* aElement)
- {
- // recursively iterate over the children looking for an element
- // with uri="?..."
- for (nsIContent* child = aElement->GetFirstChild();
- child;
- child = child->GetNextSibling()) {
- nsAutoString uri;
- child->GetAttr(kNameSpaceID_None, nsGkAtoms::uri, uri);
- if (!uri.IsEmpty() && uri[0] == char16_t('?')) {
- return NS_Atomize(uri);
- }
- nsCOMPtr<nsIAtom> result = DetermineMemberVariable(child);
- if (result) {
- return result.forget();
- }
- }
- return nullptr;
- }
- void
- nsXULTemplateBuilder::DetermineRDFQueryRef(nsIContent* aQueryElement, nsIAtom** aTag)
- {
- // check for a tag
- nsCOMPtr<nsIContent> content;
- nsXULContentUtils::FindChildByTag(aQueryElement,
- kNameSpaceID_XUL,
- nsGkAtoms::content,
- getter_AddRefs(content));
- if (! content) {
- // look for older treeitem syntax as well
- nsXULContentUtils::FindChildByTag(aQueryElement,
- kNameSpaceID_XUL,
- nsGkAtoms::treeitem,
- getter_AddRefs(content));
- }
- if (content) {
- nsAutoString uri;
- content->GetAttr(kNameSpaceID_None, nsGkAtoms::uri, uri);
- if (!uri.IsEmpty())
- mRefVariable = NS_Atomize(uri);
- nsAutoString tag;
- content->GetAttr(kNameSpaceID_None, nsGkAtoms::tag, tag);
- if (!tag.IsEmpty())
- *aTag = NS_Atomize(tag).take();
- }
- }
- nsresult
- nsXULTemplateBuilder::CompileSimpleQuery(nsIContent* aRuleElement,
- nsTemplateQuerySet* aQuerySet,
- bool* aCanUseTemplate)
- {
- // compile a simple query, which is a query with no <query> or
- // <conditions>. This means that a default query is used.
- nsCOMPtr<nsIDOMNode> query(do_QueryInterface(aRuleElement));
- nsCOMPtr<nsIAtom> memberVariable;
- if (mMemberVariable)
- memberVariable = mMemberVariable;
- else
- memberVariable = NS_Atomize("rdf:*");
- // since there is no <query> node for a simple query, the query node will
- // be either the <rule> node if multiple rules are used, or the <template> node.
- aQuerySet->mQueryNode = aRuleElement;
- nsresult rv = mQueryProcessor->CompileQuery(this, query,
- mRefVariable, memberVariable,
- getter_AddRefs(aQuerySet->mCompiledQuery));
- if (NS_FAILED(rv))
- return rv;
- if (! aQuerySet->mCompiledQuery) {
- *aCanUseTemplate = false;
- return NS_OK;
- }
- nsTemplateRule* rule = aQuerySet->NewRule(aRuleElement, aRuleElement, aQuerySet);
- if (! rule)
- return NS_ERROR_OUT_OF_MEMORY;
- rule->SetVars(mRefVariable, memberVariable);
- nsAutoString tag;
- aRuleElement->GetAttr(kNameSpaceID_None, nsGkAtoms::parent, tag);
- if (!tag.IsEmpty()) {
- nsCOMPtr<nsIAtom> tagatom = NS_Atomize(tag);
- aQuerySet->SetTag(tagatom);
- }
- *aCanUseTemplate = true;
- return AddSimpleRuleBindings(rule, aRuleElement);
- }
- nsresult
- nsXULTemplateBuilder::CompileConditions(nsTemplateRule* aRule,
- nsIContent* aCondition)
- {
- nsAutoString tag;
- aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::parent, tag);
- if (!tag.IsEmpty()) {
- nsCOMPtr<nsIAtom> tagatom = NS_Atomize(tag);
- aRule->SetTag(tagatom);
- }
- nsTemplateCondition* currentCondition = nullptr;
- for (nsIContent* node = aCondition->GetFirstChild();
- node;
- node = node->GetNextSibling()) {
- if (node->NodeInfo()->Equals(nsGkAtoms::where, kNameSpaceID_XUL)) {
- nsresult rv = CompileWhereCondition(aRule, node, ¤tCondition);
- if (NS_FAILED(rv))
- return rv;
- }
- }
- return NS_OK;
- }
- nsresult
- nsXULTemplateBuilder::CompileWhereCondition(nsTemplateRule* aRule,
- nsIContent* aCondition,
- nsTemplateCondition** aCurrentCondition)
- {
- // Compile a <where> condition, which must be of the form:
- //
- // <where subject="?var1|string" rel="relation" value="?var2|string" />
- //
- // The value of rel may be:
- // equal - subject must be equal to object
- // notequal - subject must not be equal to object
- // less - subject must be less than object
- // greater - subject must be greater than object
- // startswith - subject must start with object
- // endswith - subject must end with object
- // contains - subject must contain object
- // Comparisons are done as strings unless the subject is an integer.
- // subject
- nsAutoString subject;
- aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::subject, subject);
- if (subject.IsEmpty()) {
- nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_WHERE_NO_SUBJECT);
- return NS_OK;
- }
- nsCOMPtr<nsIAtom> svar;
- if (subject[0] == char16_t('?'))
- svar = NS_Atomize(subject);
- nsAutoString relstring;
- aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::rel, relstring);
- if (relstring.IsEmpty()) {
- nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_WHERE_NO_RELATION);
- return NS_OK;
- }
- // object
- nsAutoString value;
- aCondition->GetAttr(kNameSpaceID_None, nsGkAtoms::value, value);
- if (value.IsEmpty()) {
- nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_WHERE_NO_VALUE);
- return NS_OK;
- }
- // multiple
- bool shouldMultiple =
- aCondition->AttrValueIs(kNameSpaceID_None, nsGkAtoms::multiple,
- nsGkAtoms::_true, eCaseMatters);
- nsCOMPtr<nsIAtom> vvar;
- if (!shouldMultiple && (value[0] == char16_t('?'))) {
- vvar = NS_Atomize(value);
- }
- // ignorecase
- bool shouldIgnoreCase =
- aCondition->AttrValueIs(kNameSpaceID_None, nsGkAtoms::ignorecase,
- nsGkAtoms::_true, eCaseMatters);
- // negate
- bool shouldNegate =
- aCondition->AttrValueIs(kNameSpaceID_None, nsGkAtoms::negate,
- nsGkAtoms::_true, eCaseMatters);
- nsTemplateCondition* condition;
- if (svar && vvar) {
- condition = new nsTemplateCondition(svar, relstring, vvar,
- shouldIgnoreCase, shouldNegate);
- }
- else if (svar && !value.IsEmpty()) {
- condition = new nsTemplateCondition(svar, relstring, value,
- shouldIgnoreCase, shouldNegate, shouldMultiple);
- }
- else if (vvar) {
- condition = new nsTemplateCondition(subject, relstring, vvar,
- shouldIgnoreCase, shouldNegate);
- }
- else {
- nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_WHERE_NO_VAR);
- return NS_OK;
- }
- if (*aCurrentCondition) {
- (*aCurrentCondition)->SetNext(condition);
- }
- else {
- aRule->SetCondition(condition);
- }
- *aCurrentCondition = condition;
- return NS_OK;
- }
- nsresult
- nsXULTemplateBuilder::CompileBindings(nsTemplateRule* aRule, nsIContent* aBindings)
- {
- // Add an extended rule's bindings.
- nsresult rv;
- for (nsIContent* binding = aBindings->GetFirstChild();
- binding;
- binding = binding->GetNextSibling()) {
- if (binding->NodeInfo()->Equals(nsGkAtoms::binding,
- kNameSpaceID_XUL)) {
- rv = CompileBinding(aRule, binding);
- if (NS_FAILED(rv))
- return rv;
- }
- }
- aRule->AddBindingsToQueryProcessor(mQueryProcessor);
- return NS_OK;
- }
- nsresult
- nsXULTemplateBuilder::CompileBinding(nsTemplateRule* aRule,
- nsIContent* aBinding)
- {
- // Compile a <binding> "condition", which must be of the form:
- //
- // <binding subject="?var1"
- // predicate="resource"
- // object="?var2" />
- //
- // XXXwaterson Some day it would be cool to allow the 'predicate'
- // to be bound to a variable.
- // subject
- nsAutoString subject;
- aBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::subject, subject);
- if (subject.IsEmpty()) {
- nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_SUBJECT);
- return NS_OK;
- }
- nsCOMPtr<nsIAtom> svar;
- if (subject[0] == char16_t('?')) {
- svar = NS_Atomize(subject);
- }
- else {
- nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_SUBJECT);
- return NS_OK;
- }
- // predicate
- nsAutoString predicate;
- aBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::predicate, predicate);
- if (predicate.IsEmpty()) {
- nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_PREDICATE);
- return NS_OK;
- }
- // object
- nsAutoString object;
- aBinding->GetAttr(kNameSpaceID_None, nsGkAtoms::object, object);
- if (object.IsEmpty()) {
- nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_OBJECT);
- return NS_OK;
- }
- nsCOMPtr<nsIAtom> ovar;
- if (object[0] == char16_t('?')) {
- ovar = NS_Atomize(object);
- }
- else {
- nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_BINDING_BAD_OBJECT);
- return NS_OK;
- }
- return aRule->AddBinding(svar, predicate, ovar);
- }
- nsresult
- nsXULTemplateBuilder::AddSimpleRuleBindings(nsTemplateRule* aRule,
- nsIContent* aElement)
- {
- // Crawl the content tree of a "simple" rule, adding a variable
- // assignment for any attribute whose value is "rdf:".
- AutoTArray<nsIContent*, 8> elements;
- if (elements.AppendElement(aElement) == nullptr)
- return NS_ERROR_OUT_OF_MEMORY;
- while (elements.Length()) {
- // Pop the next element off the stack
- uint32_t i = elements.Length() - 1;
- nsIContent* element = elements[i];
- elements.RemoveElementAt(i);
- // Iterate through its attributes, looking for substitutions
- // that we need to add as bindings.
- uint32_t count = element->GetAttrCount();
- for (i = 0; i < count; ++i) {
- const nsAttrName* name = element->GetAttrNameAt(i);
- if (!name->Equals(nsGkAtoms::id, kNameSpaceID_None) &&
- !name->Equals(nsGkAtoms::uri, kNameSpaceID_None)) {
- nsAutoString value;
- element->GetAttr(name->NamespaceID(), name->LocalName(), value);
- // Scan the attribute for variables, adding a binding for
- // each one.
- ParseAttribute(value, AddBindingsFor, nullptr, aRule);
- }
- }
- // Push kids onto the stack, and search them next.
- for (nsIContent* child = element->GetLastChild();
- child;
- child = child->GetPreviousSibling()) {
- if (!elements.AppendElement(child))
- return NS_ERROR_OUT_OF_MEMORY;
- }
- }
- aRule->AddBindingsToQueryProcessor(mQueryProcessor);
- return NS_OK;
- }
- void
- nsXULTemplateBuilder::AddBindingsFor(nsXULTemplateBuilder* aThis,
- const nsAString& aVariable,
- void* aClosure)
- {
- // We should *only* be recieving "rdf:"-style variables. Make
- // sure...
- if (!StringBeginsWith(aVariable, NS_LITERAL_STRING("rdf:")))
- return;
- nsTemplateRule* rule = static_cast<nsTemplateRule*>(aClosure);
- nsCOMPtr<nsIAtom> var = NS_Atomize(aVariable);
- // Strip it down to the raw RDF property by clobbering the "rdf:"
- // prefix
- nsAutoString property;
- property.Assign(Substring(aVariable, uint32_t(4), aVariable.Length() - 4));
- if (! rule->HasBinding(rule->GetMemberVariable(), property, var))
- // In the simple syntax, the binding is always from the
- // member variable, through the property, to the target.
- rule->AddBinding(rule->GetMemberVariable(), property, var);
- }
- nsresult
- nsXULTemplateBuilder::IsSystemPrincipal(nsIPrincipal *principal, bool *result)
- {
- if (!gSystemPrincipal)
- return NS_ERROR_UNEXPECTED;
- *result = (principal == gSystemPrincipal);
- return NS_OK;
- }
- bool
- nsXULTemplateBuilder::IsActivated(nsIRDFResource *aResource)
- {
- for (ActivationEntry *entry = mTop;
- entry != nullptr;
- entry = entry->mPrevious) {
- if (entry->mResource == aResource)
- return true;
- }
- return false;
- }
- nsresult
- nsXULTemplateBuilder::GetResultResource(nsIXULTemplateResult* aResult,
- nsIRDFResource** aResource)
- {
- // get the resource for a result by checking its resource property. If it
- // is not set, check the id. This allows non-chrome implementations to
- // avoid having to use RDF.
- nsresult rv = aResult->GetResource(aResource);
- if (NS_FAILED(rv))
- return rv;
- if (! *aResource) {
- nsAutoString id;
- rv = aResult->GetId(id);
- if (NS_FAILED(rv))
- return rv;
- return gRDFService->GetUnicodeResource(id, aResource);
- }
- return rv;
- }
- void
- nsXULTemplateBuilder::OutputMatchToLog(nsIRDFResource* aId,
- nsTemplateMatch* aMatch,
- bool aIsNew)
- {
- int32_t priority = aMatch->QuerySetPriority() + 1;
- int32_t activePriority = -1;
- nsAutoString msg;
- nsAutoString templateid;
- mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::id, templateid);
- msg.AppendLiteral("In template");
- if (!templateid.IsEmpty()) {
- msg.AppendLiteral(" with id ");
- msg.Append(templateid);
- }
- nsAutoString refstring;
- aMatch->mResult->GetBindingFor(mRefVariable, refstring);
- if (!refstring.IsEmpty()) {
- msg.AppendLiteral(" using ref ");
- msg.Append(refstring);
- }
- msg.AppendLiteral("\n ");
- nsTemplateMatch* match = nullptr;
- if (mMatchMap.Get(aId, &match)){
- while (match) {
- if (match == aMatch)
- break;
- if (match->IsActive() &&
- match->GetContainer() == aMatch->GetContainer()) {
- activePriority = match->QuerySetPriority() + 1;
- break;
- }
- match = match->mNext;
- }
- }
- if (aMatch->IsActive()) {
- if (aIsNew) {
- msg.AppendLiteral("New active result for query ");
- msg.AppendInt(priority);
- msg.AppendLiteral(" matching rule ");
- msg.AppendInt(aMatch->RuleIndex() + 1);
- }
- else {
- msg.AppendLiteral("Removed active result for query ");
- msg.AppendInt(priority);
- if (activePriority > 0) {
- msg.AppendLiteral(" (new active query is ");
- msg.AppendInt(activePriority);
- msg.Append(')');
- }
- else {
- msg.AppendLiteral(" (no new active query)");
- }
- }
- }
- else {
- if (aIsNew) {
- msg.AppendLiteral("New inactive result for query ");
- msg.AppendInt(priority);
- if (activePriority > 0) {
- msg.AppendLiteral(" (overridden by query ");
- msg.AppendInt(activePriority);
- msg.Append(')');
- }
- else {
- msg.AppendLiteral(" (didn't match a rule)");
- }
- }
- else {
- msg.AppendLiteral("Removed inactive result for query ");
- msg.AppendInt(priority);
- if (activePriority > 0) {
- msg.AppendLiteral(" (active query is ");
- msg.AppendInt(activePriority);
- msg.Append(')');
- }
- else {
- msg.AppendLiteral(" (no active query)");
- }
- }
- }
- nsAutoString idstring;
- nsXULContentUtils::GetTextForNode(aId, idstring);
- msg.AppendLiteral(": ");
- msg.Append(idstring);
- nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
- if (cs)
- cs->LogStringMessage(msg.get());
- }
|