content/xul/templates/src/nsXULContentBuilder.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

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;
  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;
  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();
  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);
  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);
  1067     NS_IF_RELEASE(container);
  1069     return NS_OK;
  1072 nsresult
  1073 nsXULContentBuilder::CreateContainerContentsForQuerySet(nsIContent* aElement,
  1074                                                         nsIXULTemplateResult* aResult,
  1075                                                         bool aNotify,
  1076                                                         nsTemplateQuerySet* aQuerySet,
  1077                                                         nsIContent** aContainer,
  1078                                                         int32_t* aNewIndexInContainer)
  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()));
  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;
  1156                     if (existingmatch->IsActive())
  1157                         generateContent = false;
  1160                 prevmatch = existingmatch;
  1161                 existingmatch = existingmatch->mNext;
  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);
  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;
  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;
  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);
  1205         if (mFlags & eLoggingEnabled)
  1206             OutputMatchToLog(resultid, newmatch, true);
  1208         if (prevmatch) {
  1209             prevmatch->mNext = newmatch;
  1211         else {
  1212             mMatchMap.Put(resultid, newmatch);
  1215         if (removematch) {
  1216             newmatch->mNext = removematch->mNext;
  1217             nsTemplateMatch::Destroy(removematch, true);
  1219         else {
  1220             newmatch->mNext = existingmatch;
  1224     return rv;
  1227 nsresult
  1228 nsXULContentBuilder::EnsureElementHasGenericChild(nsIContent* parent,
  1229                                                   int32_t nameSpaceID,
  1230                                                   nsIAtom* tag,
  1231                                                   bool aNotify,
  1232                                                   nsIContent** result)
  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;
  1257     else {
  1258         return NS_ELEMENT_WAS_THERE;
  1262 bool
  1263 nsXULContentBuilder::IsOpen(nsIContent* aElement)
  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;
  1281 nsresult
  1282 nsXULContentBuilder::RemoveGeneratedContent(nsIContent* aElement)
  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;
  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);
  1336     return NS_OK;
  1339 nsresult
  1340 nsXULContentBuilder::GetElementsForResult(nsIXULTemplateResult* aResult,
  1341                                           nsCOMArray<nsIContent>& aElements)
  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;
  1357 nsresult
  1358 nsXULContentBuilder::CreateElement(int32_t aNameSpaceID,
  1359                                    nsIAtom* aTag,
  1360                                    Element** aResult)
  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);
  1374 nsresult
  1375 nsXULContentBuilder::SetContainerAttrs(nsIContent *aElement,
  1376                                        nsIXULTemplateResult* aResult,
  1377                                        bool aIgnoreNonContainers,
  1378                                        bool aNotify)
  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);
  1410     return NS_OK;
  1414 //----------------------------------------------------------------------
  1415 //
  1416 // nsIXULTemplateBuilder methods
  1417 //
  1419 NS_IMETHODIMP
  1420 nsXULContentBuilder::CreateContents(nsIContent* aElement, bool aForceCreation)
  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);
  1434 NS_IMETHODIMP
  1435 nsXULContentBuilder::HasGeneratedContent(nsIRDFResource* aResource,
  1436                                          nsIAtom* aTag,
  1437                                          bool* aGenerated)
  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;
  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;
  1482                 content = content->GetParent();
  1483             } while (content);
  1487     return NS_OK;
  1490 NS_IMETHODIMP
  1491 nsXULContentBuilder::GetResultForContent(nsIDOMElement* aElement,
  1492                                          nsIXULTemplateResult** aResult)
  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;
  1501     else {
  1502         nsTemplateMatch *match = nullptr;
  1503         if (mContentSupportMap.Get(content, &match))
  1504             *aResult = match->mResult;
  1505         else
  1506             *aResult = nullptr;
  1509     NS_IF_ADDREF(*aResult);
  1510     return NS_OK;
  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)
  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);
  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);
  1552 void
  1553 nsXULContentBuilder::NodeWillBeDestroyed(const nsINode* aNode)
  1555     nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
  1556     // Break circular references
  1557     mContentSupportMap.Clear();
  1559     nsXULTemplateBuilder::NodeWillBeDestroyed(aNode);
  1563 //----------------------------------------------------------------------
  1564 //
  1565 // nsXULTemplateBuilder methods
  1566 //
  1568 bool
  1569 nsXULContentBuilder::GetInsertionLocations(nsIXULTemplateResult* aResult,
  1570                                            nsCOMArray<nsIContent>** aLocations)
  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;
  1608         // clear the item in the list since we don't want to insert there
  1609         (*aLocations)->ReplaceObjectAt(nullptr, t);
  1612     return found;
  1615 nsresult
  1616 nsXULContentBuilder::ReplaceMatch(nsIXULTemplateResult* aOldResult,
  1617                                   nsTemplateMatch* aNewMatch,
  1618                                   nsTemplateRule* aNewMatchRule,
  1619                                   void *aContext)
  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);
  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);
  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);
  1673     return NS_OK;
  1677 nsresult
  1678 nsXULContentBuilder::SynchronizeResult(nsIXULTemplateResult* aResult)
  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);
  1703     return NS_OK;
  1706 //----------------------------------------------------------------------
  1707 //
  1708 // Implementation methods
  1709 //
  1711 nsresult
  1712 nsXULContentBuilder::OpenContainer(nsIContent* aElement)
  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;
  1735             content = content->GetParent();
  1736         } while (content);
  1738         if (! rightBuilder)
  1739             return NS_OK;
  1742     CreateTemplateAndContainerContents(aElement, false);
  1744     return NS_OK;
  1747 nsresult
  1748 nsXULContentBuilder::CloseContainer(nsIContent* aElement)
  1750     return NS_OK;
  1753 nsresult
  1754 nsXULContentBuilder::RebuildAll()
  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;
  1784 /**** Sorting Methods ****/
  1786 nsresult
  1787 nsXULContentBuilder::CompareResultToNode(nsIXULTemplateResult* aResult,
  1788                                          nsIContent* aContent,
  1789                                          int32_t* aSortOrder)
  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;
  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);
  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;
  1826     // flip the sort order if performing a descending sorting
  1827     if (mSortState.direction == nsSortState_descending)
  1828         *aSortOrder = -*aSortOrder;
  1830     return NS_OK;
  1833 nsresult
  1834 nsXULContentBuilder::InsertSortedNode(nsIContent* aContainer,
  1835                                       nsIContent* aNode,
  1836                                       nsIXULTemplateResult* aResult,
  1837                                       bool aNotify)
  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);
  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);
  1871     bool childAdded = false;
  1872     uint32_t numChildren = aContainer->GetChildCount();
  1874     if (mSortState.direction != nsSortState_natural ||
  1875         (mSortState.direction == nsSortState_natural && mSortState.isContainerRDFSeq))
  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())
  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;
  1906             if (mSortState.sortStaticsLast) {
  1907                 // indicate that static XUL comes after RDF-generated content by
  1908                 // making negative
  1909                 staticCount = -staticCount;
  1912             // save the "static" XUL element count hint
  1913             nsAutoString valueStr;
  1914             valueStr.AppendInt(staticCount);
  1915             aContainer->SetAttr(kNameSpaceID_None, nsGkAtoms::staticHint, valueStr, false);
  1918         if (staticCount <= 0) {
  1919             numChildren += staticCount;
  1920             staticCount = 0;
  1921         } else if (staticCount > (int32_t)numChildren) {
  1922             staticCount = numChildren;
  1923             numChildren -= staticCount;
  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;
  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)
  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;
  1974                 if (direction < 0)
  1975                     right = x - 1;
  1976                 else
  1977                     left = x + 1;
  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;

mercurial