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