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