LCOV - code coverage report
Current view: directory - content/xul/templates/src - nsXULContentBuilder.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 741 0 0.0 %
Date: 2012-06-02 Functions: 33 0 0.0 %

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

Generated by: LCOV version 1.7