nsXULTemplateBuilder.cpp 86 KB

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