nsXULContentBuilder.cpp 69 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977
  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. #include "mozilla/ArrayUtils.h"
  6. #include "nsContentCID.h"
  7. #include "nsIDocument.h"
  8. #include "nsIDOMNodeList.h"
  9. #include "nsIDOMXULDocument.h"
  10. #include "mozilla/dom/NodeInfo.h"
  11. #include "nsIServiceManager.h"
  12. #include "nsIXULDocument.h"
  13. #include "nsContentSupportMap.h"
  14. #include "nsRDFConMemberTestNode.h"
  15. #include "nsRDFPropertyTestNode.h"
  16. #include "nsXULSortService.h"
  17. #include "nsTemplateRule.h"
  18. #include "nsTemplateMap.h"
  19. #include "nsTArray.h"
  20. #include "nsXPIDLString.h"
  21. #include "nsGkAtoms.h"
  22. #include "nsXULContentUtils.h"
  23. #include "nsXULElement.h"
  24. #include "nsXULTemplateBuilder.h"
  25. #include "nsNodeInfoManager.h"
  26. #include "nsContentCreatorFunctions.h"
  27. #include "nsContentUtils.h"
  28. #include "nsAttrName.h"
  29. #include "nsNodeUtils.h"
  30. #include "mozAutoDocUpdate.h"
  31. #include "nsTextNode.h"
  32. #include "mozilla/dom/Element.h"
  33. #include "PLDHashTable.h"
  34. #include "rdf.h"
  35. using namespace mozilla;
  36. using namespace mozilla::dom;
  37. //----------------------------------------------------------------------
  38. //
  39. // Return values for EnsureElementHasGenericChild()
  40. //
  41. #define NS_ELEMENT_GOT_CREATED NS_RDF_NO_VALUE
  42. #define NS_ELEMENT_WAS_THERE NS_OK
  43. //----------------------------------------------------------------------
  44. //
  45. // nsXULContentBuilder
  46. //
  47. /**
  48. * The content builder generates DOM nodes from a template. The actual content
  49. * generation is done entirely inside BuildContentFromTemplate.
  50. *
  51. * Content generation is centered around the generation node (the node with
  52. * uri="?member" on it). Nodes above the generation node are unique and
  53. * generated only once. BuildContentFromTemplate will be passed the unique
  54. * flag as an argument for content at this point and will recurse until it
  55. * finds the generation node.
  56. *
  57. * Once the generation node has been found, the results for that content node
  58. * are added to the content map, stored in mContentSupportMap.
  59. *
  60. * If recursion is allowed, generation continues, where the generation node
  61. * becomes the container to insert into.
  62. */
  63. class nsXULContentBuilder : public nsXULTemplateBuilder
  64. {
  65. public:
  66. // nsIXULTemplateBuilder interface
  67. NS_IMETHOD CreateContents(nsIContent* aElement, bool aForceCreation) override;
  68. NS_IMETHOD HasGeneratedContent(nsIRDFResource* aResource,
  69. nsIAtom* aTag,
  70. bool* aGenerated) override;
  71. NS_IMETHOD GetResultForContent(nsIDOMElement* aContent,
  72. nsIXULTemplateResult** aResult) override;
  73. // nsIMutationObserver interface
  74. NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
  75. NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
  76. protected:
  77. friend nsresult
  78. NS_NewXULContentBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult);
  79. nsXULContentBuilder();
  80. void Traverse(nsCycleCollectionTraversalCallback& aCb) const override
  81. {
  82. mSortState.Traverse(aCb);
  83. }
  84. virtual void Uninit(bool aIsFinal) override;
  85. // Implementation methods
  86. nsresult
  87. OpenContainer(nsIContent* aElement);
  88. nsresult
  89. CloseContainer(nsIContent* aElement);
  90. /**
  91. * Build content from a template for a given result. This will be called
  92. * recursively or on demand and will be called for every node in the
  93. * generated content tree.
  94. */
  95. nsresult
  96. BuildContentFromTemplate(nsIContent *aTemplateNode,
  97. nsIContent *aResourceNode,
  98. nsIContent *aRealNode,
  99. bool aIsUnique,
  100. bool aIsSelfReference,
  101. nsIXULTemplateResult* aChild,
  102. bool aNotify,
  103. nsTemplateMatch* aMatch,
  104. nsIContent** aContainer,
  105. int32_t* aNewIndexInContainer);
  106. /**
  107. * Copy the attributes from the template node to the node generated
  108. * from it, performing any substitutions.
  109. *
  110. * @param aTemplateNode node within template
  111. * @param aRealNode generated node to set attibutes upon
  112. * @param aResult result to look up variable->value bindings in
  113. * @param aNotify true to notify of DOM changes
  114. */
  115. nsresult
  116. CopyAttributesToElement(nsIContent* aTemplateNode,
  117. nsIContent* aRealNode,
  118. nsIXULTemplateResult* aResult,
  119. bool aNotify);
  120. /**
  121. * Add any necessary persistent attributes (persist="...") from the
  122. * local store to a generated node.
  123. *
  124. * @param aTemplateNode node within template
  125. * @param aRealNode generated node to set persisted attibutes upon
  126. * @param aResult result to look up variable->value bindings in
  127. */
  128. nsresult
  129. AddPersistentAttributes(Element* aTemplateNode,
  130. nsIXULTemplateResult* aResult,
  131. nsIContent* aRealNode);
  132. /**
  133. * Recalculate any attributes that have variable references. This will
  134. * be called when a binding has been changed to update the attributes.
  135. * The attributes are copied from the node aTemplateNode in the template
  136. * to the generated node aRealNode, using the values from the result
  137. * aResult. This method will operate recursively.
  138. *
  139. * @param aTemplateNode node within template
  140. * @param aRealNode generated node to set attibutes upon
  141. * @param aResult result to look up variable->value bindings in
  142. */
  143. nsresult
  144. SynchronizeUsingTemplate(nsIContent *aTemplateNode,
  145. nsIContent* aRealNode,
  146. nsIXULTemplateResult* aResult);
  147. /**
  148. * Remove the generated node aContent from the DOM and the hashtables
  149. * used by the content builder.
  150. */
  151. nsresult
  152. RemoveMember(nsIContent* aContent);
  153. /**
  154. * Create the appropriate generated content for aElement, by calling
  155. * CreateContainerContents.
  156. *
  157. * @param aElement element to generate content inside
  158. * @param aForceCreation true to force creation for closed items such as menus
  159. */
  160. nsresult
  161. CreateTemplateAndContainerContents(nsIContent* aElement,
  162. bool aForceCreation);
  163. /**
  164. * Generate the results for a template, by calling
  165. * CreateContainerContentsForQuerySet for each queryset.
  166. *
  167. * @param aElement element to generate content inside
  168. * @param aResult reference point for query
  169. * @param aForceCreation true to force creation for closed items such as menus
  170. * @param aNotify true to notify of DOM changes as each element is inserted
  171. * @param aNotifyAtEnd notify at the end of all DOM changes
  172. */
  173. nsresult
  174. CreateContainerContents(nsIContent* aElement,
  175. nsIXULTemplateResult* aResult,
  176. bool aForceCreation,
  177. bool aNotify,
  178. bool aNotifyAtEnd);
  179. /**
  180. * Generate the results for a query.
  181. *
  182. * @param aElement element to generate content inside
  183. * @param aResult reference point for query
  184. * @param aNotify true to notify of DOM changes
  185. * @param aContainer container content was added inside
  186. * @param aNewIndexInContainer index with container in which content was added
  187. */
  188. nsresult
  189. CreateContainerContentsForQuerySet(nsIContent* aElement,
  190. nsIXULTemplateResult* aResult,
  191. bool aNotify,
  192. nsTemplateQuerySet* aQuerySet,
  193. nsIContent** aContainer,
  194. int32_t* aNewIndexInContainer);
  195. /**
  196. * Check if an element with a particular tag exists with a container.
  197. * If it is not present, append a new element with that tag into the
  198. * container.
  199. *
  200. * @param aParent parent container
  201. * @param aNameSpaceID namespace of tag to locate or create
  202. * @param aTag tag to locate or create
  203. * @param aNotify true to notify of DOM changes
  204. * @param aResult set to the found or created node.
  205. */
  206. nsresult
  207. EnsureElementHasGenericChild(nsIContent* aParent,
  208. int32_t aNameSpaceID,
  209. nsIAtom* aTag,
  210. bool aNotify,
  211. nsIContent** aResult);
  212. bool
  213. IsOpen(nsIContent* aElement);
  214. nsresult
  215. RemoveGeneratedContent(nsIContent* aElement);
  216. nsresult
  217. GetElementsForResult(nsIXULTemplateResult* aResult,
  218. nsCOMArray<nsIContent>& aElements);
  219. nsresult
  220. CreateElement(int32_t aNameSpaceID,
  221. nsIAtom* aTag,
  222. Element** aResult);
  223. /**
  224. * Set the container and empty attributes on a node. If
  225. * aIgnoreNonContainers is true, then the element is not changed
  226. * for non-containers. Otherwise, the container attribute will be set to
  227. * false.
  228. *
  229. * @param aElement element to set attributes on
  230. * @param aResult result to use to determine state of attributes
  231. * @param aIgnoreNonContainers true to not change for non-containers
  232. * @param aNotify true to notify of DOM changes
  233. */
  234. nsresult
  235. SetContainerAttrs(nsIContent *aElement,
  236. nsIXULTemplateResult* aResult,
  237. bool aIgnoreNonContainers,
  238. bool aNotify);
  239. virtual nsresult
  240. RebuildAll() override;
  241. // GetInsertionLocations, ReplaceMatch and SynchronizeResult are inherited
  242. // from nsXULTemplateBuilder
  243. /**
  244. * Return true if the result can be inserted into the template as
  245. * generated content. For the content builder, aLocations will be set
  246. * to the list of containers where the content should be inserted.
  247. */
  248. virtual bool
  249. GetInsertionLocations(nsIXULTemplateResult* aOldResult,
  250. nsCOMArray<nsIContent>** aLocations) override;
  251. /**
  252. * Remove the content associated with aOldResult which no longer matches,
  253. * and/or generate content for a new match.
  254. */
  255. virtual nsresult
  256. ReplaceMatch(nsIXULTemplateResult* aOldResult,
  257. nsTemplateMatch* aNewMatch,
  258. nsTemplateRule* aNewMatchRule,
  259. void *aContext) override;
  260. /**
  261. * Synchronize a result bindings with the generated content for that
  262. * result. This will be called as a result of the template builder's
  263. * ResultBindingChanged method.
  264. */
  265. virtual nsresult
  266. SynchronizeResult(nsIXULTemplateResult* aResult) override;
  267. /**
  268. * Compare a result to a content node. If the generated content for the
  269. * result should come before aContent, set aSortOrder to -1. If it should
  270. * come after, set sortOrder to 1. If both are equal, set to 0.
  271. */
  272. nsresult
  273. CompareResultToNode(nsIXULTemplateResult* aResult, nsIContent* aContent,
  274. int32_t* aSortOrder);
  275. /**
  276. * Insert a generated node into the container where it should go according
  277. * to the current sort. aNode is the generated content node and aResult is
  278. * the result for the generated node.
  279. */
  280. nsresult
  281. InsertSortedNode(nsIContent* aContainer,
  282. nsIContent* aNode,
  283. nsIXULTemplateResult* aResult,
  284. bool aNotify);
  285. /**
  286. * Maintains a mapping between elements in the DOM and the matches
  287. * that they support.
  288. */
  289. nsContentSupportMap mContentSupportMap;
  290. /**
  291. * Maintains a mapping from an element in the DOM to the template
  292. * element that it was created from.
  293. */
  294. nsTemplateMap mTemplateMap;
  295. /**
  296. * Information about the currently active sort
  297. */
  298. nsSortState mSortState;
  299. };
  300. nsresult
  301. NS_NewXULContentBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult)
  302. {
  303. NS_PRECONDITION(aOuter == nullptr, "no aggregation");
  304. if (aOuter)
  305. return NS_ERROR_NO_AGGREGATION;
  306. nsresult rv;
  307. nsXULContentBuilder* result = new nsXULContentBuilder();
  308. NS_ADDREF(result); // stabilize
  309. rv = result->InitGlobals();
  310. if (NS_SUCCEEDED(rv))
  311. rv = result->QueryInterface(aIID, aResult);
  312. NS_RELEASE(result);
  313. return rv;
  314. }
  315. nsXULContentBuilder::nsXULContentBuilder()
  316. {
  317. mSortState.initialized = false;
  318. }
  319. void
  320. nsXULContentBuilder::Uninit(bool aIsFinal)
  321. {
  322. if (! aIsFinal && mRoot) {
  323. nsresult rv = RemoveGeneratedContent(mRoot);
  324. if (NS_FAILED(rv))
  325. return;
  326. }
  327. // Nuke the content support map completely.
  328. mContentSupportMap.Clear();
  329. mTemplateMap.Clear();
  330. mSortState.initialized = false;
  331. nsXULTemplateBuilder::Uninit(aIsFinal);
  332. }
  333. nsresult
  334. nsXULContentBuilder::BuildContentFromTemplate(nsIContent *aTemplateNode,
  335. nsIContent *aResourceNode,
  336. nsIContent *aRealNode,
  337. bool aIsUnique,
  338. bool aIsSelfReference,
  339. nsIXULTemplateResult* aChild,
  340. bool aNotify,
  341. nsTemplateMatch* aMatch,
  342. nsIContent** aContainer,
  343. int32_t* aNewIndexInContainer)
  344. {
  345. // This is the mother lode. Here is where we grovel through an
  346. // element in the template, copying children from the template
  347. // into the "real" content tree, performing substitution as we go
  348. // by looking stuff up using the results.
  349. //
  350. // |aTemplateNode| is the element in the "template tree", whose
  351. // children we will duplicate and move into the "real" content
  352. // tree.
  353. //
  354. // |aResourceNode| is the element in the "real" content tree that
  355. // has the "id" attribute set to an result's id. This is
  356. // not directly used here, but rather passed down to the XUL
  357. // sort service to perform container-level sort.
  358. //
  359. // |aRealNode| is the element in the "real" content tree to which
  360. // the new elements will be copied.
  361. //
  362. // |aIsUnique| is set to "true" so long as content has been
  363. // "unique" (or "above" the resource element) so far in the
  364. // template.
  365. //
  366. // |aIsSelfReference| should be set to "true" for cases where
  367. // the reference and member variables are the same, indicating
  368. // that the generated node is the same as the reference point,
  369. // so generation should not recurse, or else an infinite loop
  370. // would occur.
  371. //
  372. // |aChild| is the result for which we are building content.
  373. //
  374. // |aNotify| is set to "true" if content should be constructed
  375. // "noisily"; that is, whether the document observers should be
  376. // notified when new content is added to the content model.
  377. //
  378. // |aContainer| is an out parameter that will be set to the first
  379. // container element in the "real" content tree to which content
  380. // was appended.
  381. //
  382. // |aNewIndexInContainer| is an out parameter that will be set to
  383. // the index in aContainer at which new content is first
  384. // constructed.
  385. //
  386. // If |aNotify| is "false", then |aContainer| and
  387. // |aNewIndexInContainer| are used to determine where in the
  388. // content model new content is constructed. This allows a single
  389. // notification to be propagated to document observers.
  390. //
  391. nsresult rv;
  392. if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) {
  393. MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
  394. ("nsXULContentBuilder::BuildContentFromTemplate (is unique: %d)",
  395. aIsUnique));
  396. nsAutoString id;
  397. aChild->GetId(id);
  398. MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
  399. ("Tags: [Template: %s Resource: %s Real: %s] for id %s",
  400. nsAtomCString(aTemplateNode->NodeInfo()->NameAtom()).get(),
  401. nsAtomCString(aResourceNode->NodeInfo()->NameAtom()).get(),
  402. nsAtomCString(aRealNode->NodeInfo()->NameAtom()).get(), NS_ConvertUTF16toUTF8(id).get()));
  403. }
  404. // Iterate through all of the template children, constructing
  405. // "real" content model nodes for each "template" child.
  406. for (nsIContent* tmplKid = aTemplateNode->GetFirstChild();
  407. tmplKid;
  408. tmplKid = tmplKid->GetNextSibling()) {
  409. int32_t nameSpaceID = tmplKid->GetNameSpaceID();
  410. // Check whether this element is the generation element. The generation
  411. // element is the element that is cookie-cutter copied once for each
  412. // different result specified by |aChild|.
  413. //
  414. // Nodes that appear -above- the generation element
  415. // (that is, are ancestors of the generation element in the
  416. // content model) are unique across all values of |aChild|,
  417. // and are created only once.
  418. //
  419. // Nodes that appear -below- the generation element (that is,
  420. // are descendants of the generation element in the content
  421. // model), are cookie-cutter copied for each distinct value of
  422. // |aChild|.
  423. //
  424. // For example, in a <tree> template:
  425. //
  426. // <tree>
  427. // <template>
  428. // <treechildren> [1]
  429. // <treeitem uri="rdf:*"> [2]
  430. // <treerow> [3]
  431. // <treecell value="rdf:urn:foo" /> [4]
  432. // <treecell value="rdf:urn:bar" /> [5]
  433. // </treerow>
  434. // </treeitem>
  435. // </treechildren>
  436. // </template>
  437. // </tree>
  438. //
  439. // The <treeitem> element [2] is the generation element. This
  440. // element, and all of its descendants ([3], [4], and [5])
  441. // will be duplicated for each different |aChild|.
  442. // It's ancestor <treechildren> [1] is unique, and
  443. // will only be created -once-, no matter how many <treeitem>s
  444. // are created below it.
  445. //
  446. // isUnique will be true for nodes above the generation element,
  447. // isGenerationElement will be true for the generation element,
  448. // and both will be false for descendants
  449. bool isGenerationElement = false;
  450. bool isUnique = aIsUnique;
  451. // We identify the resource element by presence of a
  452. // "uri='rdf:*'" attribute. (We also support the older
  453. // "uri='...'" syntax.)
  454. if (tmplKid->HasAttr(kNameSpaceID_None, nsGkAtoms::uri) && aMatch->IsActive()) {
  455. isGenerationElement = true;
  456. isUnique = false;
  457. }
  458. MOZ_ASSERT_IF(isGenerationElement, tmplKid->IsElement());
  459. nsIAtom *tag = tmplKid->NodeInfo()->NameAtom();
  460. if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) {
  461. MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
  462. ("xultemplate[%p] building %s %s %s",
  463. this, nsAtomCString(tag).get(),
  464. (isGenerationElement ? "[resource]" : ""),
  465. (isUnique ? "[unique]" : "")));
  466. }
  467. // Set to true if the child we're trying to create now
  468. // already existed in the content model.
  469. bool realKidAlreadyExisted = false;
  470. nsCOMPtr<nsIContent> realKid;
  471. if (isUnique) {
  472. // The content is "unique"; that is, we haven't descended
  473. // far enough into the template to hit the generation
  474. // element yet. |EnsureElementHasGenericChild()| will
  475. // conditionally create the element iff it isn't there
  476. // already.
  477. rv = EnsureElementHasGenericChild(aRealNode, nameSpaceID, tag, aNotify, getter_AddRefs(realKid));
  478. if (NS_FAILED(rv))
  479. return rv;
  480. if (rv == NS_ELEMENT_WAS_THERE) {
  481. realKidAlreadyExisted = true;
  482. }
  483. else {
  484. // Potentially remember the index of this element as the first
  485. // element that we've generated. Note that we remember
  486. // this -before- we recurse!
  487. if (aContainer && !*aContainer) {
  488. *aContainer = aRealNode;
  489. NS_ADDREF(*aContainer);
  490. uint32_t indx = aRealNode->GetChildCount();
  491. // Since EnsureElementHasGenericChild() added us, make
  492. // sure to subtract one for our real index.
  493. *aNewIndexInContainer = indx - 1;
  494. }
  495. }
  496. // Recurse until we get to the resource element. Since
  497. // -we're- unique, assume that our child will be
  498. // unique. The check for the "resource" element at the top
  499. // of the function will trip this to |false| as soon as we
  500. // encounter it.
  501. rv = BuildContentFromTemplate(tmplKid, aResourceNode, realKid, true,
  502. aIsSelfReference, aChild, aNotify, aMatch,
  503. aContainer, aNewIndexInContainer);
  504. if (NS_FAILED(rv))
  505. return rv;
  506. }
  507. else if (isGenerationElement) {
  508. // It's the "resource" element. Create a new element using
  509. // the namespace ID and tag from the template element.
  510. nsCOMPtr<Element> element;
  511. rv = CreateElement(nameSpaceID, tag, getter_AddRefs(element));
  512. if (NS_FAILED(rv))
  513. return rv;
  514. realKid = element.forget();
  515. // Add the resource element to the content support map so
  516. // we can remove the match based on the content node later.
  517. mContentSupportMap.Put(realKid, aMatch);
  518. // Assign the element an 'id' attribute using result's id
  519. nsAutoString id;
  520. rv = aChild->GetId(id);
  521. if (NS_FAILED(rv))
  522. return rv;
  523. rv = realKid->SetAttr(kNameSpaceID_None, nsGkAtoms::id, id, false);
  524. if (NS_FAILED(rv))
  525. return rv;
  526. // Set up the element's 'container' and 'empty' attributes.
  527. SetContainerAttrs(realKid, aChild, true, false);
  528. }
  529. else if (tag == nsGkAtoms::textnode &&
  530. nameSpaceID == kNameSpaceID_XUL) {
  531. // <xul:text value="..."> is replaced by text of the
  532. // actual value of the 'rdf:resource' attribute for the
  533. // given node.
  534. // SynchronizeUsingTemplate contains code used to update textnodes,
  535. // so make sure to modify both when changing this
  536. char16_t attrbuf[128];
  537. nsFixedString attrValue(attrbuf, ArrayLength(attrbuf), 0);
  538. tmplKid->GetAttr(kNameSpaceID_None, nsGkAtoms::value, attrValue);
  539. if (!attrValue.IsEmpty()) {
  540. nsAutoString value;
  541. rv = SubstituteText(aChild, attrValue, value);
  542. if (NS_FAILED(rv)) return rv;
  543. RefPtr<nsTextNode> content =
  544. new nsTextNode(mRoot->NodeInfo()->NodeInfoManager());
  545. content->SetText(value, false);
  546. rv = aRealNode->AppendChildTo(content, aNotify);
  547. if (NS_FAILED(rv)) return rv;
  548. // XXX Don't bother remembering text nodes as the
  549. // first element we've generated?
  550. }
  551. }
  552. else if (tmplKid->IsNodeOfType(nsINode::eTEXT)) {
  553. nsCOMPtr<nsIDOMNode> tmplTextNode = do_QueryInterface(tmplKid);
  554. if (!tmplTextNode) {
  555. NS_ERROR("textnode not implementing nsIDOMNode??");
  556. return NS_ERROR_FAILURE;
  557. }
  558. nsCOMPtr<nsIDOMNode> clonedNode;
  559. tmplTextNode->CloneNode(false, 1, getter_AddRefs(clonedNode));
  560. nsCOMPtr<nsIContent> clonedContent = do_QueryInterface(clonedNode);
  561. if (!clonedContent) {
  562. NS_ERROR("failed to clone textnode");
  563. return NS_ERROR_FAILURE;
  564. }
  565. rv = aRealNode->AppendChildTo(clonedContent, aNotify);
  566. if (NS_FAILED(rv)) return rv;
  567. }
  568. else {
  569. // It's just a generic element. Create it!
  570. nsCOMPtr<Element> element;
  571. rv = CreateElement(nameSpaceID, tag, getter_AddRefs(element));
  572. if (NS_FAILED(rv)) return rv;
  573. realKid = element.forget();
  574. }
  575. if (realKid && !realKidAlreadyExisted) {
  576. // Potentially remember the index of this element as the
  577. // first element that we've generated.
  578. if (aContainer && !*aContainer) {
  579. *aContainer = aRealNode;
  580. NS_ADDREF(*aContainer);
  581. uint32_t indx = aRealNode->GetChildCount();
  582. // Since we haven't inserted any content yet, our new
  583. // index in the container will be the current count of
  584. // elements in the container.
  585. *aNewIndexInContainer = indx;
  586. }
  587. // Remember the template kid from which we created the
  588. // real kid. This allows us to sync back up with the
  589. // template to incrementally build content.
  590. mTemplateMap.Put(realKid, tmplKid);
  591. rv = CopyAttributesToElement(tmplKid, realKid, aChild, false);
  592. if (NS_FAILED(rv)) return rv;
  593. // Add any persistent attributes
  594. if (isGenerationElement) {
  595. rv = AddPersistentAttributes(tmplKid->AsElement(), aChild,
  596. realKid);
  597. if (NS_FAILED(rv)) return rv;
  598. }
  599. // the unique content recurses up above. Also, don't recurse if
  600. // this is a self reference (a reference to the same resource)
  601. // or we'll end up regenerating the same content.
  602. if (!aIsSelfReference && !isUnique) {
  603. // this call creates the content inside the generation node,
  604. // for example the label below:
  605. // <vbox uri="?">
  606. // <label value="?title"/>
  607. // </vbox>
  608. rv = BuildContentFromTemplate(tmplKid, aResourceNode, realKid, false,
  609. false, aChild, false, aMatch,
  610. nullptr /* don't care */,
  611. nullptr /* don't care */);
  612. if (NS_FAILED(rv)) return rv;
  613. if (isGenerationElement) {
  614. // build the next level of children
  615. rv = CreateContainerContents(realKid, aChild, false,
  616. false, false);
  617. if (NS_FAILED(rv)) return rv;
  618. }
  619. }
  620. // We'll _already_ have added the unique elements; but if
  621. // it's -not- unique, then use the XUL sort service now to
  622. // append the element to the content model.
  623. if (! isUnique) {
  624. rv = NS_ERROR_UNEXPECTED;
  625. if (isGenerationElement)
  626. rv = InsertSortedNode(aRealNode, realKid, aChild, aNotify);
  627. if (NS_FAILED(rv)) {
  628. rv = aRealNode->AppendChildTo(realKid, aNotify);
  629. NS_ASSERTION(NS_SUCCEEDED(rv), "unable to insert element");
  630. }
  631. }
  632. }
  633. }
  634. return NS_OK;
  635. }
  636. nsresult
  637. nsXULContentBuilder::CopyAttributesToElement(nsIContent* aTemplateNode,
  638. nsIContent* aRealNode,
  639. nsIXULTemplateResult* aResult,
  640. bool aNotify)
  641. {
  642. nsresult rv;
  643. // Copy all attributes from the template to the new element
  644. uint32_t numAttribs = aTemplateNode->GetAttrCount();
  645. for (uint32_t attr = 0; attr < numAttribs; attr++) {
  646. const nsAttrName* name = aTemplateNode->GetAttrNameAt(attr);
  647. int32_t attribNameSpaceID = name->NamespaceID();
  648. // Hold a strong reference here so that the atom doesn't go away
  649. // during UnsetAttr.
  650. nsCOMPtr<nsIAtom> attribName = name->LocalName();
  651. // XXXndeakin ignore namespaces until bug 321182 is fixed
  652. if (attribName != nsGkAtoms::id && attribName != nsGkAtoms::uri) {
  653. // Create a buffer here, because there's a chance that an
  654. // attribute in the template is going to be an RDF URI, which is
  655. // usually longish.
  656. char16_t attrbuf[128];
  657. nsFixedString attribValue(attrbuf, ArrayLength(attrbuf), 0);
  658. aTemplateNode->GetAttr(attribNameSpaceID, attribName, attribValue);
  659. if (!attribValue.IsEmpty()) {
  660. nsAutoString value;
  661. rv = SubstituteText(aResult, attribValue, value);
  662. if (NS_FAILED(rv))
  663. return rv;
  664. // if the string is empty after substitutions, remove the
  665. // attribute
  666. if (!value.IsEmpty()) {
  667. rv = aRealNode->SetAttr(attribNameSpaceID,
  668. attribName,
  669. name->GetPrefix(),
  670. value,
  671. aNotify);
  672. }
  673. else {
  674. rv = aRealNode->UnsetAttr(attribNameSpaceID,
  675. attribName,
  676. aNotify);
  677. }
  678. if (NS_FAILED(rv))
  679. return rv;
  680. }
  681. }
  682. }
  683. return NS_OK;
  684. }
  685. nsresult
  686. nsXULContentBuilder::AddPersistentAttributes(Element* aTemplateNode,
  687. nsIXULTemplateResult* aResult,
  688. nsIContent* aRealNode)
  689. {
  690. if (!mRoot)
  691. return NS_OK;
  692. nsCOMPtr<nsIRDFResource> resource;
  693. nsresult rv = GetResultResource(aResult, getter_AddRefs(resource));
  694. NS_ENSURE_SUCCESS(rv, rv);
  695. nsAutoString attribute, persist;
  696. aTemplateNode->GetAttr(kNameSpaceID_None, nsGkAtoms::persist, persist);
  697. while (!persist.IsEmpty()) {
  698. attribute.Truncate();
  699. int32_t offset = persist.FindCharInSet(" ,");
  700. if (offset > 0) {
  701. persist.Left(attribute, offset);
  702. persist.Cut(0, offset + 1);
  703. }
  704. else {
  705. attribute = persist;
  706. persist.Truncate();
  707. }
  708. attribute.Trim(" ");
  709. if (attribute.IsEmpty())
  710. break;
  711. nsCOMPtr<nsIAtom> tag;
  712. int32_t nameSpaceID;
  713. RefPtr<mozilla::dom::NodeInfo> ni =
  714. aTemplateNode->GetExistingAttrNameFromQName(attribute);
  715. if (ni) {
  716. tag = ni->NameAtom();
  717. nameSpaceID = ni->NamespaceID();
  718. }
  719. else {
  720. tag = NS_Atomize(attribute);
  721. NS_ENSURE_TRUE(tag, NS_ERROR_OUT_OF_MEMORY);
  722. nameSpaceID = kNameSpaceID_None;
  723. }
  724. nsCOMPtr<nsIRDFResource> property;
  725. rv = nsXULContentUtils::GetResource(nameSpaceID, tag, getter_AddRefs(property));
  726. NS_ENSURE_SUCCESS(rv, rv);
  727. nsCOMPtr<nsIRDFNode> target;
  728. rv = mDB->GetTarget(resource, property, true, getter_AddRefs(target));
  729. NS_ENSURE_SUCCESS(rv, rv);
  730. if (! target)
  731. continue;
  732. nsCOMPtr<nsIRDFLiteral> value = do_QueryInterface(target);
  733. NS_ASSERTION(value != nullptr, "unable to stomach that sort of node");
  734. if (! value)
  735. continue;
  736. const char16_t* valueStr;
  737. rv = value->GetValueConst(&valueStr);
  738. NS_ENSURE_SUCCESS(rv, rv);
  739. rv = aRealNode->SetAttr(nameSpaceID, tag, nsDependentString(valueStr),
  740. false);
  741. NS_ENSURE_SUCCESS(rv, rv);
  742. }
  743. return NS_OK;
  744. }
  745. nsresult
  746. nsXULContentBuilder::SynchronizeUsingTemplate(nsIContent* aTemplateNode,
  747. nsIContent* aRealElement,
  748. nsIXULTemplateResult* aResult)
  749. {
  750. // check all attributes on the template node; if they reference a resource,
  751. // update the equivalent attribute on the content node
  752. nsresult rv;
  753. rv = CopyAttributesToElement(aTemplateNode, aRealElement, aResult, true);
  754. if (NS_FAILED(rv))
  755. return rv;
  756. uint32_t count = aTemplateNode->GetChildCount();
  757. for (uint32_t loop = 0; loop < count; ++loop) {
  758. nsIContent *tmplKid = aTemplateNode->GetChildAt(loop);
  759. if (! tmplKid)
  760. break;
  761. nsIContent *realKid = aRealElement->GetChildAt(loop);
  762. if (! realKid)
  763. break;
  764. // check for text nodes and update them accordingly.
  765. // This code is similar to that in BuildContentFromTemplate
  766. if (tmplKid->NodeInfo()->Equals(nsGkAtoms::textnode,
  767. kNameSpaceID_XUL)) {
  768. char16_t attrbuf[128];
  769. nsFixedString attrValue(attrbuf, ArrayLength(attrbuf), 0);
  770. tmplKid->GetAttr(kNameSpaceID_None, nsGkAtoms::value, attrValue);
  771. if (!attrValue.IsEmpty()) {
  772. nsAutoString value;
  773. rv = SubstituteText(aResult, attrValue, value);
  774. if (NS_FAILED(rv)) return rv;
  775. realKid->SetText(value, true);
  776. }
  777. }
  778. rv = SynchronizeUsingTemplate(tmplKid, realKid, aResult);
  779. if (NS_FAILED(rv)) return rv;
  780. }
  781. return NS_OK;
  782. }
  783. nsresult
  784. nsXULContentBuilder::RemoveMember(nsIContent* aContent)
  785. {
  786. nsCOMPtr<nsIContent> parent = aContent->GetParent();
  787. if (parent) {
  788. int32_t pos = parent->IndexOf(aContent);
  789. NS_ASSERTION(pos >= 0, "parent doesn't think this child has an index");
  790. if (pos < 0) return NS_OK;
  791. // Note: RemoveChildAt sets |child|'s document to null so that
  792. // it'll get knocked out of the XUL doc's resource-to-element
  793. // map.
  794. parent->RemoveChildAt(pos, true);
  795. }
  796. // Remove from the content support map.
  797. mContentSupportMap.Remove(aContent);
  798. // Remove from the template map
  799. mTemplateMap.Remove(aContent);
  800. return NS_OK;
  801. }
  802. nsresult
  803. nsXULContentBuilder::CreateTemplateAndContainerContents(nsIContent* aElement,
  804. bool aForceCreation)
  805. {
  806. // Generate both 1) the template content for the current element,
  807. // and 2) recursive subcontent (if the current element refers to a
  808. // container result).
  809. MOZ_LOG(gXULTemplateLog, LogLevel::Info,
  810. ("nsXULContentBuilder::CreateTemplateAndContainerContents start - flags: %d",
  811. mFlags));
  812. if (! mQueryProcessor)
  813. return NS_OK;
  814. // for the root element, get the ref attribute and generate content
  815. if (aElement == mRoot) {
  816. if (! mRootResult) {
  817. nsAutoString ref;
  818. mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::ref, ref);
  819. if (! ref.IsEmpty()) {
  820. nsresult rv = mQueryProcessor->TranslateRef(mDataSource, ref,
  821. getter_AddRefs(mRootResult));
  822. if (NS_FAILED(rv))
  823. return rv;
  824. }
  825. }
  826. if (mRootResult) {
  827. CreateContainerContents(aElement, mRootResult, aForceCreation,
  828. false, true);
  829. }
  830. }
  831. else if (!(mFlags & eDontRecurse)) {
  832. // The content map will contain the generation elements (the ones that
  833. // are given ids) and only those elements, so get the reference point
  834. // from the corresponding match.
  835. nsTemplateMatch *match = nullptr;
  836. if (mContentSupportMap.Get(aElement, &match))
  837. CreateContainerContents(aElement, match->mResult, aForceCreation,
  838. false, true);
  839. }
  840. MOZ_LOG(gXULTemplateLog, LogLevel::Info,
  841. ("nsXULContentBuilder::CreateTemplateAndContainerContents end"));
  842. return NS_OK;
  843. }
  844. nsresult
  845. nsXULContentBuilder::CreateContainerContents(nsIContent* aElement,
  846. nsIXULTemplateResult* aResult,
  847. bool aForceCreation,
  848. bool aNotify,
  849. bool aNotifyAtEnd)
  850. {
  851. if (!aForceCreation && !IsOpen(aElement))
  852. return NS_OK;
  853. // don't generate children if recursion or child processing isn't allowed
  854. if (aResult != mRootResult) {
  855. if (mFlags & eDontRecurse)
  856. return NS_OK;
  857. bool mayProcessChildren;
  858. nsresult rv = aResult->GetMayProcessChildren(&mayProcessChildren);
  859. if (NS_FAILED(rv) || !mayProcessChildren)
  860. return rv;
  861. }
  862. nsCOMPtr<nsIRDFResource> refResource;
  863. GetResultResource(aResult, getter_AddRefs(refResource));
  864. if (! refResource)
  865. return NS_ERROR_FAILURE;
  866. // Avoid re-entrant builds for the same resource.
  867. if (IsActivated(refResource))
  868. return NS_OK;
  869. ActivationEntry entry(refResource, &mTop);
  870. // Compile the rules now, if they haven't been already.
  871. if (! mQueriesCompiled) {
  872. nsresult rv = CompileQueries();
  873. if (NS_FAILED(rv))
  874. return rv;
  875. }
  876. if (mQuerySets.Length() == 0)
  877. return NS_OK;
  878. // See if the element's templates contents have been generated:
  879. // this prevents a re-entrant call from triggering another
  880. // generation.
  881. nsXULElement *xulcontent = nsXULElement::FromContent(aElement);
  882. if (xulcontent) {
  883. if (xulcontent->GetTemplateGenerated())
  884. return NS_OK;
  885. // Now mark the element's contents as being generated so that
  886. // any re-entrant calls don't trigger an infinite recursion.
  887. xulcontent->SetTemplateGenerated();
  888. }
  889. int32_t newIndexInContainer = -1;
  890. nsIContent* container = nullptr;
  891. int32_t querySetCount = mQuerySets.Length();
  892. for (int32_t r = 0; r < querySetCount; r++) {
  893. nsTemplateQuerySet* queryset = mQuerySets[r];
  894. nsIAtom* tag = queryset->GetTag();
  895. if (tag && tag != aElement->NodeInfo()->NameAtom())
  896. continue;
  897. CreateContainerContentsForQuerySet(aElement, aResult, aNotify, queryset,
  898. &container, &newIndexInContainer);
  899. }
  900. if (aNotifyAtEnd && container) {
  901. MOZ_AUTO_DOC_UPDATE(container->GetUncomposedDoc(), UPDATE_CONTENT_MODEL,
  902. true);
  903. nsNodeUtils::ContentAppended(container,
  904. container->GetChildAt(newIndexInContainer),
  905. newIndexInContainer);
  906. }
  907. NS_IF_RELEASE(container);
  908. return NS_OK;
  909. }
  910. nsresult
  911. nsXULContentBuilder::CreateContainerContentsForQuerySet(nsIContent* aElement,
  912. nsIXULTemplateResult* aResult,
  913. bool aNotify,
  914. nsTemplateQuerySet* aQuerySet,
  915. nsIContent** aContainer,
  916. int32_t* aNewIndexInContainer)
  917. {
  918. if (MOZ_LOG_TEST(gXULTemplateLog, LogLevel::Debug)) {
  919. nsAutoString id;
  920. aResult->GetId(id);
  921. MOZ_LOG(gXULTemplateLog, LogLevel::Debug,
  922. ("nsXULContentBuilder::CreateContainerContentsForQuerySet start for ref %s\n",
  923. NS_ConvertUTF16toUTF8(id).get()));
  924. }
  925. if (! mQueryProcessor)
  926. return NS_OK;
  927. nsCOMPtr<nsISimpleEnumerator> results;
  928. nsresult rv = mQueryProcessor->GenerateResults(mDataSource, aResult,
  929. aQuerySet->mCompiledQuery,
  930. getter_AddRefs(results));
  931. if (NS_FAILED(rv) || !results)
  932. return rv;
  933. bool hasMoreResults;
  934. rv = results->HasMoreElements(&hasMoreResults);
  935. for (; NS_SUCCEEDED(rv) && hasMoreResults;
  936. rv = results->HasMoreElements(&hasMoreResults)) {
  937. nsCOMPtr<nsISupports> nr;
  938. rv = results->GetNext(getter_AddRefs(nr));
  939. if (NS_FAILED(rv))
  940. return rv;
  941. nsCOMPtr<nsIXULTemplateResult> nextresult = do_QueryInterface(nr);
  942. if (!nextresult)
  943. return NS_ERROR_UNEXPECTED;
  944. nsCOMPtr<nsIRDFResource> resultid;
  945. rv = GetResultResource(nextresult, getter_AddRefs(resultid));
  946. if (NS_FAILED(rv))
  947. return rv;
  948. if (!resultid)
  949. continue;
  950. nsTemplateMatch *newmatch =
  951. nsTemplateMatch::Create(aQuerySet->Priority(),
  952. nextresult, aElement);
  953. if (!newmatch)
  954. return NS_ERROR_OUT_OF_MEMORY;
  955. // check if there is already an existing match. If so, a previous
  956. // query already generated content so the match is just added to the
  957. // end of the set of matches.
  958. bool generateContent = true;
  959. nsTemplateMatch* prevmatch = nullptr;
  960. nsTemplateMatch* existingmatch = nullptr;
  961. nsTemplateMatch* removematch = nullptr;
  962. if (mMatchMap.Get(resultid, &existingmatch)){
  963. // check if there is an existing match that matched a rule
  964. while (existingmatch) {
  965. // break out once we've reached a query in the list with a
  966. // higher priority, as the new match list is sorted by
  967. // priority, and the new match should be inserted here
  968. int32_t priority = existingmatch->QuerySetPriority();
  969. if (priority > aQuerySet->Priority())
  970. break;
  971. // skip over non-matching containers
  972. if (existingmatch->GetContainer() == aElement) {
  973. // if the same priority is already found, replace it. This can happen
  974. // when a container is removed and readded
  975. if (priority == aQuerySet->Priority()) {
  976. removematch = existingmatch;
  977. break;
  978. }
  979. if (existingmatch->IsActive())
  980. generateContent = false;
  981. }
  982. prevmatch = existingmatch;
  983. existingmatch = existingmatch->mNext;
  984. }
  985. }
  986. if (removematch) {
  987. // remove the generated content for the existing match
  988. rv = ReplaceMatch(removematch->mResult, nullptr, nullptr, aElement);
  989. if (NS_FAILED(rv))
  990. return rv;
  991. if (mFlags & eLoggingEnabled)
  992. OutputMatchToLog(resultid, removematch, false);
  993. }
  994. if (generateContent) {
  995. // find the rule that matches. If none match, the content does not
  996. // need to be generated
  997. int16_t ruleindex;
  998. nsTemplateRule* matchedrule = nullptr;
  999. rv = DetermineMatchedRule(aElement, nextresult, aQuerySet,
  1000. &matchedrule, &ruleindex);
  1001. if (NS_FAILED(rv)) {
  1002. nsTemplateMatch::Destroy(newmatch, false);
  1003. return rv;
  1004. }
  1005. if (matchedrule) {
  1006. rv = newmatch->RuleMatched(aQuerySet, matchedrule,
  1007. ruleindex, nextresult);
  1008. if (NS_FAILED(rv)) {
  1009. nsTemplateMatch::Destroy(newmatch, false);
  1010. return rv;
  1011. }
  1012. // Grab the template node
  1013. nsCOMPtr<nsIContent> action = matchedrule->GetAction();
  1014. BuildContentFromTemplate(action, aElement, aElement, true,
  1015. mRefVariable == matchedrule->GetMemberVariable(),
  1016. nextresult, aNotify, newmatch,
  1017. aContainer, aNewIndexInContainer);
  1018. }
  1019. }
  1020. if (mFlags & eLoggingEnabled)
  1021. OutputMatchToLog(resultid, newmatch, true);
  1022. if (prevmatch) {
  1023. prevmatch->mNext = newmatch;
  1024. }
  1025. else {
  1026. mMatchMap.Put(resultid, newmatch);
  1027. }
  1028. if (removematch) {
  1029. newmatch->mNext = removematch->mNext;
  1030. nsTemplateMatch::Destroy(removematch, true);
  1031. }
  1032. else {
  1033. newmatch->mNext = existingmatch;
  1034. }
  1035. }
  1036. return rv;
  1037. }
  1038. nsresult
  1039. nsXULContentBuilder::EnsureElementHasGenericChild(nsIContent* parent,
  1040. int32_t nameSpaceID,
  1041. nsIAtom* tag,
  1042. bool aNotify,
  1043. nsIContent** result)
  1044. {
  1045. nsresult rv;
  1046. rv = nsXULContentUtils::FindChildByTag(parent, nameSpaceID, tag, result);
  1047. if (NS_FAILED(rv))
  1048. return rv;
  1049. if (rv == NS_RDF_NO_VALUE) {
  1050. // we need to construct a new child element.
  1051. nsCOMPtr<Element> element;
  1052. rv = CreateElement(nameSpaceID, tag, getter_AddRefs(element));
  1053. if (NS_FAILED(rv))
  1054. return rv;
  1055. // XXX Note that the notification ensures we won't batch insertions! This could be bad! - Dave
  1056. rv = parent->AppendChildTo(element, aNotify);
  1057. if (NS_FAILED(rv))
  1058. return rv;
  1059. element.forget(result);
  1060. return NS_ELEMENT_GOT_CREATED;
  1061. }
  1062. else {
  1063. return NS_ELEMENT_WAS_THERE;
  1064. }
  1065. }
  1066. bool
  1067. nsXULContentBuilder::IsOpen(nsIContent* aElement)
  1068. {
  1069. // Determine if this is a <treeitem> or <menu> element
  1070. // XXXhyatt Use the XBL service to obtain a base tag.
  1071. if (aElement->IsAnyOfXULElements(nsGkAtoms::menu,
  1072. nsGkAtoms::menubutton,
  1073. nsGkAtoms::toolbarbutton,
  1074. nsGkAtoms::button,
  1075. nsGkAtoms::treeitem))
  1076. return aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::open,
  1077. nsGkAtoms::_true, eCaseMatters);
  1078. return true;
  1079. }
  1080. nsresult
  1081. nsXULContentBuilder::RemoveGeneratedContent(nsIContent* aElement)
  1082. {
  1083. // Keep a queue of "ungenerated" elements that we have to probe
  1084. // for generated content.
  1085. AutoTArray<nsIContent*, 8> ungenerated;
  1086. if (ungenerated.AppendElement(aElement) == nullptr)
  1087. return NS_ERROR_OUT_OF_MEMORY;
  1088. uint32_t count;
  1089. while (0 != (count = ungenerated.Length())) {
  1090. // Pull the next "ungenerated" element off the queue.
  1091. uint32_t last = count - 1;
  1092. nsCOMPtr<nsIContent> element = ungenerated[last];
  1093. ungenerated.RemoveElementAt(last);
  1094. uint32_t i = element->GetChildCount();
  1095. while (i-- > 0) {
  1096. nsCOMPtr<nsIContent> child = element->GetChildAt(i);
  1097. // Optimize for the <template> element, because we *know*
  1098. // it won't have any generated content: there's no reason
  1099. // to even check this subtree.
  1100. // XXX should this check |child| rather than |element|? Otherwise
  1101. // it should be moved outside the inner loop. Bug 297290.
  1102. if (element->NodeInfo()->Equals(nsGkAtoms::_template,
  1103. kNameSpaceID_XUL) ||
  1104. !element->IsElement())
  1105. continue;
  1106. // If the element is in the template map, then we
  1107. // assume it's been generated and nuke it.
  1108. nsCOMPtr<nsIContent> tmpl;
  1109. mTemplateMap.GetTemplateFor(child, getter_AddRefs(tmpl));
  1110. if (! tmpl) {
  1111. // No 'template' attribute, so this must not have been
  1112. // generated. We'll need to examine its kids.
  1113. if (ungenerated.AppendElement(child) == nullptr)
  1114. return NS_ERROR_OUT_OF_MEMORY;
  1115. continue;
  1116. }
  1117. // If we get here, it's "generated". Bye bye!
  1118. element->RemoveChildAt(i, true);
  1119. // Remove this and any children from the content support map.
  1120. mContentSupportMap.Remove(child);
  1121. // Remove from the template map
  1122. mTemplateMap.Remove(child);
  1123. }
  1124. }
  1125. return NS_OK;
  1126. }
  1127. nsresult
  1128. nsXULContentBuilder::GetElementsForResult(nsIXULTemplateResult* aResult,
  1129. nsCOMArray<nsIContent>& aElements)
  1130. {
  1131. // if the root has been removed from the document, just return
  1132. // since there won't be any generated content any more
  1133. nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(mRoot->GetComposedDoc());
  1134. if (! xuldoc)
  1135. return NS_OK;
  1136. nsAutoString id;
  1137. aResult->GetId(id);
  1138. xuldoc->GetElementsForID(id, aElements);
  1139. return NS_OK;
  1140. }
  1141. nsresult
  1142. nsXULContentBuilder::CreateElement(int32_t aNameSpaceID,
  1143. nsIAtom* aTag,
  1144. Element** aResult)
  1145. {
  1146. nsCOMPtr<nsIDocument> doc = mRoot->GetComposedDoc();
  1147. NS_ASSERTION(doc != nullptr, "not initialized");
  1148. if (! doc)
  1149. return NS_ERROR_NOT_INITIALIZED;
  1150. RefPtr<mozilla::dom::NodeInfo> nodeInfo =
  1151. doc->NodeInfoManager()->GetNodeInfo(aTag, nullptr, aNameSpaceID,
  1152. nsIDOMNode::ELEMENT_NODE);
  1153. return NS_NewElement(aResult, nodeInfo.forget(), NOT_FROM_PARSER);
  1154. }
  1155. nsresult
  1156. nsXULContentBuilder::SetContainerAttrs(nsIContent *aElement,
  1157. nsIXULTemplateResult* aResult,
  1158. bool aIgnoreNonContainers,
  1159. bool aNotify)
  1160. {
  1161. NS_PRECONDITION(aResult != nullptr, "null ptr");
  1162. if (! aResult)
  1163. return NS_ERROR_NULL_POINTER;
  1164. bool iscontainer;
  1165. aResult->GetIsContainer(&iscontainer);
  1166. if (aIgnoreNonContainers && !iscontainer)
  1167. return NS_OK;
  1168. NS_NAMED_LITERAL_STRING(true_, "true");
  1169. NS_NAMED_LITERAL_STRING(false_, "false");
  1170. const nsAString& newcontainer =
  1171. iscontainer ? true_ : false_;
  1172. aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::container,
  1173. newcontainer, aNotify);
  1174. if (iscontainer && !(mFlags & eDontTestEmpty)) {
  1175. bool isempty;
  1176. aResult->GetIsEmpty(&isempty);
  1177. const nsAString& newempty =
  1178. (iscontainer && isempty) ? true_ : false_;
  1179. aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::empty,
  1180. newempty, aNotify);
  1181. }
  1182. return NS_OK;
  1183. }
  1184. //----------------------------------------------------------------------
  1185. //
  1186. // nsIXULTemplateBuilder methods
  1187. //
  1188. NS_IMETHODIMP
  1189. nsXULContentBuilder::CreateContents(nsIContent* aElement, bool aForceCreation)
  1190. {
  1191. NS_PRECONDITION(aElement != nullptr, "null ptr");
  1192. if (! aElement)
  1193. return NS_ERROR_NULL_POINTER;
  1194. // don't build contents for closed elements. aForceCreation will be true
  1195. // when a menu is about to be opened, so the content should be built anyway.
  1196. if (!aForceCreation && !IsOpen(aElement))
  1197. return NS_OK;
  1198. return CreateTemplateAndContainerContents(aElement, aForceCreation);
  1199. }
  1200. NS_IMETHODIMP
  1201. nsXULContentBuilder::HasGeneratedContent(nsIRDFResource* aResource,
  1202. nsIAtom* aTag,
  1203. bool* aGenerated)
  1204. {
  1205. *aGenerated = false;
  1206. NS_ENSURE_TRUE(mRoot, NS_ERROR_NOT_INITIALIZED);
  1207. NS_ENSURE_STATE(mRootResult);
  1208. nsCOMPtr<nsIRDFResource> rootresource;
  1209. nsresult rv = mRootResult->GetResource(getter_AddRefs(rootresource));
  1210. if (NS_FAILED(rv))
  1211. return rv;
  1212. // the root resource is always acceptable
  1213. if (aResource == rootresource) {
  1214. if (!aTag || mRoot->NodeInfo()->NameAtom() == aTag)
  1215. *aGenerated = true;
  1216. }
  1217. else {
  1218. const char* uri;
  1219. aResource->GetValueConst(&uri);
  1220. NS_ConvertUTF8toUTF16 refID(uri);
  1221. // just return if the node is no longer in a document
  1222. nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(mRoot->GetComposedDoc());
  1223. if (! xuldoc)
  1224. return NS_OK;
  1225. nsCOMArray<nsIContent> elements;
  1226. xuldoc->GetElementsForID(refID, elements);
  1227. uint32_t cnt = elements.Count();
  1228. for (int32_t i = int32_t(cnt) - 1; i >= 0; --i) {
  1229. nsCOMPtr<nsIContent> content = elements.SafeObjectAt(i);
  1230. do {
  1231. nsTemplateMatch* match;
  1232. if (content == mRoot || mContentSupportMap.Get(content, &match)) {
  1233. // If we've got a tag, check it to ensure we're consistent.
  1234. if (!aTag || content->NodeInfo()->NameAtom() == aTag) {
  1235. *aGenerated = true;
  1236. return NS_OK;
  1237. }
  1238. }
  1239. content = content->GetParent();
  1240. } while (content);
  1241. }
  1242. }
  1243. return NS_OK;
  1244. }
  1245. NS_IMETHODIMP
  1246. nsXULContentBuilder::GetResultForContent(nsIDOMElement* aElement,
  1247. nsIXULTemplateResult** aResult)
  1248. {
  1249. NS_ENSURE_ARG_POINTER(aElement);
  1250. NS_ENSURE_ARG_POINTER(aResult);
  1251. nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
  1252. if (content == mRoot) {
  1253. *aResult = mRootResult;
  1254. }
  1255. else {
  1256. nsTemplateMatch *match = nullptr;
  1257. if (mContentSupportMap.Get(content, &match))
  1258. *aResult = match->mResult;
  1259. else
  1260. *aResult = nullptr;
  1261. }
  1262. NS_IF_ADDREF(*aResult);
  1263. return NS_OK;
  1264. }
  1265. //----------------------------------------------------------------------
  1266. //
  1267. // nsIDocumentObserver methods
  1268. //
  1269. void
  1270. nsXULContentBuilder::AttributeChanged(nsIDocument* aDocument,
  1271. Element* aElement,
  1272. int32_t aNameSpaceID,
  1273. nsIAtom* aAttribute,
  1274. int32_t aModType,
  1275. const nsAttrValue* aOldValue)
  1276. {
  1277. nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
  1278. // Handle "open" and "close" cases. We do this handling before
  1279. // we've notified the observer, so that content is already created
  1280. // for the frame system to walk.
  1281. if (aElement->GetNameSpaceID() == kNameSpaceID_XUL &&
  1282. aAttribute == nsGkAtoms::open) {
  1283. // We're on a XUL tag, and an ``open'' attribute changed.
  1284. if (aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::open,
  1285. nsGkAtoms::_true, eCaseMatters))
  1286. OpenContainer(aElement);
  1287. else
  1288. CloseContainer(aElement);
  1289. }
  1290. if ((aNameSpaceID == kNameSpaceID_XUL) &&
  1291. ((aAttribute == nsGkAtoms::sort) ||
  1292. (aAttribute == nsGkAtoms::sortDirection) ||
  1293. (aAttribute == nsGkAtoms::sortResource) ||
  1294. (aAttribute == nsGkAtoms::sortResource2)))
  1295. mSortState.initialized = false;
  1296. // Pass along to the generic template builder.
  1297. nsXULTemplateBuilder::AttributeChanged(aDocument, aElement, aNameSpaceID,
  1298. aAttribute, aModType, aOldValue);
  1299. }
  1300. void
  1301. nsXULContentBuilder::NodeWillBeDestroyed(const nsINode* aNode)
  1302. {
  1303. nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
  1304. // Break circular references
  1305. mContentSupportMap.Clear();
  1306. nsXULTemplateBuilder::NodeWillBeDestroyed(aNode);
  1307. }
  1308. //----------------------------------------------------------------------
  1309. //
  1310. // nsXULTemplateBuilder methods
  1311. //
  1312. bool
  1313. nsXULContentBuilder::GetInsertionLocations(nsIXULTemplateResult* aResult,
  1314. nsCOMArray<nsIContent>** aLocations)
  1315. {
  1316. *aLocations = nullptr;
  1317. nsAutoString ref;
  1318. nsresult rv = aResult->GetBindingFor(mRefVariable, ref);
  1319. if (NS_FAILED(rv))
  1320. return false;
  1321. nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(mRoot->GetComposedDoc());
  1322. if (! xuldoc)
  1323. return false;
  1324. *aLocations = new nsCOMArray<nsIContent>;
  1325. NS_ENSURE_TRUE(*aLocations, false);
  1326. xuldoc->GetElementsForID(ref, **aLocations);
  1327. uint32_t count = (*aLocations)->Count();
  1328. bool found = false;
  1329. for (uint32_t t = 0; t < count; t++) {
  1330. nsCOMPtr<nsIContent> content = (*aLocations)->SafeObjectAt(t);
  1331. nsTemplateMatch* refmatch;
  1332. if (content == mRoot || mContentSupportMap.Get(content, &refmatch)) {
  1333. // See if we've built the container contents for "content"
  1334. // yet. If not, we don't need to build any content. This
  1335. // happens, for example, if we receive an assertion on a
  1336. // closed folder in a tree widget or on a menu that hasn't
  1337. // yet been opened.
  1338. nsXULElement *xulcontent = nsXULElement::FromContent(content);
  1339. if (!xulcontent || xulcontent->GetTemplateGenerated()) {
  1340. found = true;
  1341. continue;
  1342. }
  1343. }
  1344. // clear the item in the list since we don't want to insert there
  1345. (*aLocations)->ReplaceObjectAt(nullptr, t);
  1346. }
  1347. return found;
  1348. }
  1349. nsresult
  1350. nsXULContentBuilder::ReplaceMatch(nsIXULTemplateResult* aOldResult,
  1351. nsTemplateMatch* aNewMatch,
  1352. nsTemplateRule* aNewMatchRule,
  1353. void *aContext)
  1354. {
  1355. nsresult rv;
  1356. nsIContent* content = static_cast<nsIContent*>(aContext);
  1357. // update the container attributes for the match
  1358. if (content) {
  1359. nsAutoString ref;
  1360. if (aNewMatch)
  1361. rv = aNewMatch->mResult->GetBindingFor(mRefVariable, ref);
  1362. else
  1363. rv = aOldResult->GetBindingFor(mRefVariable, ref);
  1364. if (NS_FAILED(rv))
  1365. return rv;
  1366. if (!ref.IsEmpty()) {
  1367. nsCOMPtr<nsIXULTemplateResult> refResult;
  1368. rv = GetResultForId(ref, getter_AddRefs(refResult));
  1369. if (NS_FAILED(rv))
  1370. return rv;
  1371. if (refResult)
  1372. SetContainerAttrs(content, refResult, false, true);
  1373. }
  1374. }
  1375. if (aOldResult) {
  1376. nsCOMArray<nsIContent> elements;
  1377. rv = GetElementsForResult(aOldResult, elements);
  1378. if (NS_FAILED(rv))
  1379. return rv;
  1380. uint32_t count = elements.Count();
  1381. for (int32_t e = int32_t(count) - 1; e >= 0; --e) {
  1382. nsCOMPtr<nsIContent> child = elements.SafeObjectAt(e);
  1383. nsTemplateMatch* match;
  1384. if (mContentSupportMap.Get(child, &match)) {
  1385. if (content == match->GetContainer())
  1386. RemoveMember(child);
  1387. }
  1388. }
  1389. }
  1390. if (aNewMatch) {
  1391. nsCOMPtr<nsIContent> action = aNewMatchRule->GetAction();
  1392. return BuildContentFromTemplate(action, content, content, true,
  1393. mRefVariable == aNewMatchRule->GetMemberVariable(),
  1394. aNewMatch->mResult, true, aNewMatch,
  1395. nullptr, nullptr);
  1396. }
  1397. return NS_OK;
  1398. }
  1399. nsresult
  1400. nsXULContentBuilder::SynchronizeResult(nsIXULTemplateResult* aResult)
  1401. {
  1402. nsCOMArray<nsIContent> elements;
  1403. GetElementsForResult(aResult, elements);
  1404. uint32_t cnt = elements.Count();
  1405. for (int32_t i = int32_t(cnt) - 1; i >= 0; --i) {
  1406. nsCOMPtr<nsIContent> element = elements.SafeObjectAt(i);
  1407. nsTemplateMatch* match;
  1408. if (! mContentSupportMap.Get(element, &match))
  1409. continue;
  1410. nsCOMPtr<nsIContent> templateNode;
  1411. mTemplateMap.GetTemplateFor(element, getter_AddRefs(templateNode));
  1412. NS_ASSERTION(templateNode, "couldn't find template node for element");
  1413. if (! templateNode)
  1414. continue;
  1415. // this node was created by a XUL template, so update it accordingly
  1416. SynchronizeUsingTemplate(templateNode, element, aResult);
  1417. }
  1418. return NS_OK;
  1419. }
  1420. //----------------------------------------------------------------------
  1421. //
  1422. // Implementation methods
  1423. //
  1424. nsresult
  1425. nsXULContentBuilder::OpenContainer(nsIContent* aElement)
  1426. {
  1427. if (aElement != mRoot) {
  1428. if (mFlags & eDontRecurse)
  1429. return NS_OK;
  1430. bool rightBuilder = false;
  1431. nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(aElement->GetComposedDoc());
  1432. if (! xuldoc)
  1433. return NS_OK;
  1434. // See if we're responsible for this element
  1435. nsIContent* content = aElement;
  1436. do {
  1437. nsCOMPtr<nsIXULTemplateBuilder> builder;
  1438. xuldoc->GetTemplateBuilderFor(content, getter_AddRefs(builder));
  1439. if (builder) {
  1440. if (builder == this)
  1441. rightBuilder = true;
  1442. break;
  1443. }
  1444. content = content->GetParent();
  1445. } while (content);
  1446. if (! rightBuilder)
  1447. return NS_OK;
  1448. }
  1449. CreateTemplateAndContainerContents(aElement, false);
  1450. return NS_OK;
  1451. }
  1452. nsresult
  1453. nsXULContentBuilder::CloseContainer(nsIContent* aElement)
  1454. {
  1455. return NS_OK;
  1456. }
  1457. nsresult
  1458. nsXULContentBuilder::RebuildAll()
  1459. {
  1460. NS_ENSURE_TRUE(mRoot, NS_ERROR_NOT_INITIALIZED);
  1461. // Bail out early if we are being torn down.
  1462. nsCOMPtr<nsIDocument> doc = mRoot->GetComposedDoc();
  1463. if (!doc)
  1464. return NS_OK;
  1465. if (mQueriesCompiled)
  1466. Uninit(false);
  1467. nsresult rv = CompileQueries();
  1468. if (NS_FAILED(rv))
  1469. return rv;
  1470. if (mQuerySets.Length() == 0)
  1471. return NS_OK;
  1472. nsXULElement *xulcontent = nsXULElement::FromContent(mRoot);
  1473. if (xulcontent)
  1474. xulcontent->ClearTemplateGenerated();
  1475. // Now, regenerate both the template- and container-generated
  1476. // contents for the current element...
  1477. CreateTemplateAndContainerContents(mRoot, false);
  1478. return NS_OK;
  1479. }
  1480. /**** Sorting Methods ****/
  1481. nsresult
  1482. nsXULContentBuilder::CompareResultToNode(nsIXULTemplateResult* aResult,
  1483. nsIContent* aContent,
  1484. int32_t* aSortOrder)
  1485. {
  1486. NS_ASSERTION(aSortOrder, "CompareResultToNode: null out param aSortOrder");
  1487. *aSortOrder = 0;
  1488. nsTemplateMatch *match = nullptr;
  1489. if (!mContentSupportMap.Get(aContent, &match)) {
  1490. *aSortOrder = mSortState.sortStaticsLast ? -1 : 1;
  1491. return NS_OK;
  1492. }
  1493. if (!mQueryProcessor)
  1494. return NS_OK;
  1495. if (mSortState.direction == nsSortState_natural) {
  1496. // sort in natural order
  1497. nsresult rv = mQueryProcessor->CompareResults(aResult, match->mResult,
  1498. nullptr, mSortState.sortHints,
  1499. aSortOrder);
  1500. NS_ENSURE_SUCCESS(rv, rv);
  1501. }
  1502. else {
  1503. // iterate over each sort key and compare. If the nodes are equal,
  1504. // continue to compare using the next sort key. If not equal, stop.
  1505. int32_t length = mSortState.sortKeys.Count();
  1506. for (int32_t t = 0; t < length; t++) {
  1507. nsresult rv = mQueryProcessor->CompareResults(aResult, match->mResult,
  1508. mSortState.sortKeys[t],
  1509. mSortState.sortHints, aSortOrder);
  1510. NS_ENSURE_SUCCESS(rv, rv);
  1511. if (*aSortOrder)
  1512. break;
  1513. }
  1514. }
  1515. // flip the sort order if performing a descending sorting
  1516. if (mSortState.direction == nsSortState_descending)
  1517. *aSortOrder = -*aSortOrder;
  1518. return NS_OK;
  1519. }
  1520. nsresult
  1521. nsXULContentBuilder::InsertSortedNode(nsIContent* aContainer,
  1522. nsIContent* aNode,
  1523. nsIXULTemplateResult* aResult,
  1524. bool aNotify)
  1525. {
  1526. nsresult rv;
  1527. if (!mSortState.initialized) {
  1528. nsAutoString sort, sortDirection, sortHints;
  1529. mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::sort, sort);
  1530. mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::sortDirection, sortDirection);
  1531. mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::sorthints, sortHints);
  1532. sortDirection += ' ';
  1533. sortDirection += sortHints;
  1534. rv = XULSortServiceImpl::InitializeSortState(mRoot, aContainer,
  1535. sort, sortDirection, &mSortState);
  1536. NS_ENSURE_SUCCESS(rv, rv);
  1537. }
  1538. // when doing a natural sort, items will typically be sorted according to
  1539. // the order they appear in the datasource. For RDF, cache whether the
  1540. // reference parent is an RDF Seq. That way, the items can be sorted in the
  1541. // order they are in the Seq.
  1542. mSortState.isContainerRDFSeq = false;
  1543. if (mSortState.direction == nsSortState_natural) {
  1544. nsCOMPtr<nsISupports> ref;
  1545. nsresult rv = aResult->GetBindingObjectFor(mRefVariable, getter_AddRefs(ref));
  1546. NS_ENSURE_SUCCESS(rv, rv);
  1547. nsCOMPtr<nsIRDFResource> container = do_QueryInterface(ref);
  1548. if (container) {
  1549. rv = gRDFContainerUtils->IsSeq(mDB, container, &mSortState.isContainerRDFSeq);
  1550. NS_ENSURE_SUCCESS(rv, rv);
  1551. }
  1552. }
  1553. bool childAdded = false;
  1554. uint32_t numChildren = aContainer->GetChildCount();
  1555. if (mSortState.direction != nsSortState_natural ||
  1556. (mSortState.direction == nsSortState_natural && mSortState.isContainerRDFSeq))
  1557. {
  1558. // because numChildren gets modified
  1559. int32_t realNumChildren = numChildren;
  1560. nsIContent *child = nullptr;
  1561. // rjc says: determine where static XUL ends and generated XUL/RDF begins
  1562. int32_t staticCount = 0;
  1563. nsAutoString staticValue;
  1564. aContainer->GetAttr(kNameSpaceID_None, nsGkAtoms::staticHint, staticValue);
  1565. if (!staticValue.IsEmpty())
  1566. {
  1567. // found "static" XUL element count hint
  1568. nsresult strErr = NS_OK;
  1569. staticCount = staticValue.ToInteger(&strErr);
  1570. if (NS_FAILED(strErr))
  1571. staticCount = 0;
  1572. } else {
  1573. // compute the "static" XUL element count
  1574. for (nsIContent* child = aContainer->GetFirstChild();
  1575. child;
  1576. child = child->GetNextSibling()) {
  1577. if (nsContentUtils::HasNonEmptyAttr(child, kNameSpaceID_None,
  1578. nsGkAtoms::_template))
  1579. break;
  1580. else
  1581. ++staticCount;
  1582. }
  1583. if (mSortState.sortStaticsLast) {
  1584. // indicate that static XUL comes after RDF-generated content by
  1585. // making negative
  1586. staticCount = -staticCount;
  1587. }
  1588. // save the "static" XUL element count hint
  1589. nsAutoString valueStr;
  1590. valueStr.AppendInt(staticCount);
  1591. aContainer->SetAttr(kNameSpaceID_None, nsGkAtoms::staticHint, valueStr, false);
  1592. }
  1593. if (staticCount <= 0) {
  1594. numChildren += staticCount;
  1595. staticCount = 0;
  1596. } else if (staticCount > (int32_t)numChildren) {
  1597. staticCount = numChildren;
  1598. numChildren -= staticCount;
  1599. }
  1600. // figure out where to insert the node when a sort order is being imposed
  1601. if (numChildren > 0) {
  1602. nsIContent *temp;
  1603. int32_t direction;
  1604. // rjc says: The following is an implementation of a fairly optimal
  1605. // binary search insertion sort... with interpolation at either end-point.
  1606. if (mSortState.lastWasFirst) {
  1607. child = aContainer->GetChildAt(staticCount);
  1608. temp = child;
  1609. rv = CompareResultToNode(aResult, temp, &direction);
  1610. if (direction < 0) {
  1611. aContainer->InsertChildAt(aNode, staticCount, aNotify);
  1612. childAdded = true;
  1613. } else
  1614. mSortState.lastWasFirst = false;
  1615. } else if (mSortState.lastWasLast) {
  1616. child = aContainer->GetChildAt(realNumChildren - 1);
  1617. temp = child;
  1618. rv = CompareResultToNode(aResult, temp, &direction);
  1619. if (direction > 0) {
  1620. aContainer->InsertChildAt(aNode, realNumChildren, aNotify);
  1621. childAdded = true;
  1622. } else
  1623. mSortState.lastWasLast = false;
  1624. }
  1625. int32_t left = staticCount + 1, right = realNumChildren, x;
  1626. while (!childAdded && right >= left) {
  1627. x = (left + right) / 2;
  1628. child = aContainer->GetChildAt(x - 1);
  1629. temp = child;
  1630. rv = CompareResultToNode(aResult, temp, &direction);
  1631. if ((x == left && direction < 0) ||
  1632. (x == right && direction >= 0) ||
  1633. left == right)
  1634. {
  1635. int32_t thePos = (direction > 0 ? x : x - 1);
  1636. aContainer->InsertChildAt(aNode, thePos, aNotify);
  1637. childAdded = true;
  1638. mSortState.lastWasFirst = (thePos == staticCount);
  1639. mSortState.lastWasLast = (thePos >= realNumChildren);
  1640. break;
  1641. }
  1642. if (direction < 0)
  1643. right = x - 1;
  1644. else
  1645. left = x + 1;
  1646. }
  1647. }
  1648. }
  1649. // if the child hasn't been inserted yet, just add it at the end. Note
  1650. // that an append isn't done as there may be static content afterwards.
  1651. if (!childAdded)
  1652. aContainer->InsertChildAt(aNode, numChildren, aNotify);
  1653. return NS_OK;
  1654. }