Back to index

lightning-sunbird  0.9+nobinonly
nsXULContentBuilder.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Robert Churchill <rjc@netscape.com>
00024  *   David Hyatt <hyatt@netscape.com>
00025  *   Chris Waterson <waterson@netscape.com>
00026  *   Pierre Phaneuf <pp@ludusdesign.com>
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either of the GNU General Public License Version 2 or later (the "GPL"),
00030  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 #include "nsContentCID.h"
00043 #include "nsIDocument.h"
00044 #include "nsIDOMNodeList.h"
00045 #include "nsIDOMXULDocument.h"
00046 #include "nsINodeInfo.h"
00047 #include "nsIPrincipal.h"
00048 #include "nsIServiceManager.h"
00049 #include "nsITextContent.h"
00050 #include "nsIXULDocument.h"
00051 #include "nsIXULSortService.h"
00052 
00053 #include "nsClusterKeySet.h"
00054 #include "nsContentSupportMap.h"
00055 #include "nsContentTestNode.h"
00056 #include "nsContentTagTestNode.h"
00057 #include "nsInstantiationNode.h"
00058 #include "nsRDFConMemberTestNode.h"
00059 #include "nsRDFPropertyTestNode.h"
00060 #include "nsRDFSort.h"
00061 #include "nsTemplateRule.h"
00062 #include "nsTemplateMap.h"
00063 #include "nsVoidArray.h"
00064 #include "nsXPIDLString.h"
00065 #include "nsXULAtoms.h"
00066 #include "nsLayoutAtoms.h"
00067 #include "nsXULContentUtils.h"
00068 #include "nsXULElement.h"
00069 #include "nsXULTemplateBuilder.h"
00070 #include "nsSupportsArray.h"
00071 #include "nsNodeInfoManager.h"
00072 #include "nsContentCreatorFunctions.h"
00073 #include "nsContentUtils.h"
00074 
00075 #include "jsapi.h"
00076 #include "pldhash.h"
00077 #include "rdf.h"
00078 
00079 //----------------------------------------------------------------------0
00080 
00081 static NS_DEFINE_CID(kXULSortServiceCID,         NS_XULSORTSERVICE_CID);
00082 
00083 PRBool
00084 IsElementInBuilder(nsIContent *aContent, nsIXULTemplateBuilder *aBuilder)
00085 {
00086     // Make sure that the element is contained within the heirarchy
00087     // that we're supposed to be processing.
00088     nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(aContent->GetDocument());
00089     if (! xuldoc)
00090         return PR_FALSE;
00091 
00092     nsCOMPtr<nsIContent> content = aContent;
00093     do {
00094         nsCOMPtr<nsIXULTemplateBuilder> builder;
00095         xuldoc->GetTemplateBuilderFor(content, getter_AddRefs(builder));
00096         if (builder) {
00097             if (builder == aBuilder)
00098                 return PR_TRUE; // aBuilder is the builder for this element.
00099 
00100             // We found a builder, but it's not aBuilder.
00101             break;
00102         }
00103 
00104         content = content->GetParent();
00105     } while (content);
00106 
00107     return PR_FALSE;
00108 }
00109 
00110 //----------------------------------------------------------------------
00111 //
00112 // Return values for EnsureElementHasGenericChild()
00113 //
00114 #define NS_RDF_ELEMENT_GOT_CREATED NS_RDF_NO_VALUE
00115 #define NS_RDF_ELEMENT_WAS_THERE   NS_OK
00116 
00117 //----------------------------------------------------------------------
00118 //
00119 // nsXULContentBuilder
00120 //
00121 
00122 class nsXULContentBuilder : public nsXULTemplateBuilder
00123 {
00124 public:
00125     // nsIXULTemplateBuilder interface
00126     NS_IMETHOD CreateContents(nsIContent* aElement);
00127 
00128     // nsIDocumentObserver interface
00129     virtual void AttributeChanged(nsIDocument* aDocument,
00130                                   nsIContent*  aContent,
00131                                   PRInt32      aNameSpaceID,
00132                                   nsIAtom*     aAttribute,
00133                                   PRInt32      aModType);
00134 
00135     void DocumentWillBeDestroyed(nsIDocument* aDocument);
00136 
00137 protected:
00138     friend NS_IMETHODIMP
00139     NS_NewXULContentBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult);
00140 
00141     nsXULContentBuilder();
00142     virtual ~nsXULContentBuilder();
00143     nsresult Init();
00144 
00145     // Implementation methods
00146     nsresult
00147     OpenContainer(nsIContent* aElement);
00148 
00149     nsresult
00150     CloseContainer(nsIContent* aElement);
00151 
00152     PRBool
00153     IsIgnoreableAttribute(PRInt32 aNameSpaceID, nsIAtom* aAtom);
00154 
00155     nsresult
00156     BuildContentFromTemplate(nsIContent *aTemplateNode,
00157                              nsIContent *aResourceNode,
00158                              nsIContent *aRealNode,
00159                              PRBool aIsUnique,
00160                              nsIRDFResource* aChild,
00161                              PRBool aNotify,
00162                              nsTemplateMatch* aMatch,
00163                              nsIContent** aContainer,
00164                              PRInt32* aNewIndexInContainer);
00165 
00166     nsresult
00167     AddPersistentAttributes(nsIContent* aTemplateNode, nsIRDFResource* aResource, nsIContent* aRealNode);
00168 
00169     nsresult
00170     SynchronizeUsingTemplate(nsIContent *aTemplateNode,
00171                              nsIContent* aRealNode,
00172                              nsTemplateMatch& aMatch,
00173                              const VariableSet& aModifiedVars);
00174 
00175     PRBool
00176     IsDirectlyContainedBy(nsIContent* aChild, nsIContent* aParent);
00177 
00178     nsresult
00179     RemoveMember(nsIContent* aContainerElement,
00180                  nsIRDFResource* aMember,
00181                  PRBool aNotify);
00182 
00183     nsresult
00184     CreateTemplateAndContainerContents(nsIContent* aElement,
00185                                        nsIContent** aContainer,
00186                                        PRInt32* aNewIndexInContainer);
00187 
00188     nsresult
00189     CreateContainerContents(nsIContent* aElement,
00190                             nsIRDFResource* aResource,
00191                             PRBool aNotify,
00192                             nsIContent** aContainer,
00193                             PRInt32* aNewIndexInContainer);
00194 
00195     nsresult
00196     CreateTemplateContents(nsIContent* aElement,
00197                            nsIContent* aTemplateElement,
00198                            nsIContent** aContainer,
00199                            PRInt32* aNewIndexInContainer);
00200 
00201     nsresult
00202     EnsureElementHasGenericChild(nsIContent* aParent,
00203                                  PRInt32 aNameSpaceID,
00204                                  nsIAtom* aTag,
00205                                  PRBool aNotify,
00206                                  nsIContent** aResult);
00207 
00208     PRBool
00209     IsOpen(nsIContent* aElement);
00210 
00211     nsresult
00212     RemoveGeneratedContent(nsIContent* aElement);
00213 
00214     PRBool
00215     IsLazyWidgetItem(nsIContent* aElement);
00216 
00217     nsresult
00218     GetElementsForResource(nsIRDFResource* aResource, nsISupportsArray* aElements);
00219 
00220     nsresult
00221     CreateElement(PRInt32 aNameSpaceID,
00222                   nsIAtom* aTag,
00223                   nsIContent** aResult);
00224 
00225     nsresult
00226     SetContainerAttrs(nsIContent *aElement, const nsTemplateMatch* aMatch);
00227 
00228     virtual nsresult
00229     InitializeRuleNetwork();
00230 
00231     virtual nsresult
00232     InitializeRuleNetworkForSimpleRules(InnerNode** aChildNode);
00233 
00234     virtual nsresult
00235     RebuildAll();
00236 
00237     virtual nsresult
00238     CompileCondition(nsIAtom* aTag,
00239                      nsTemplateRule* aRule,
00240                      nsIContent* aCondition,
00241                      InnerNode* aParentNode,
00242                      TestNode** aResult);
00243 
00244     nsresult
00245     CompileContentCondition(nsTemplateRule* aRule,
00246                             nsIContent* aCondition,
00247                             InnerNode* aParentNode,
00248                             TestNode** aResult);
00249 
00253     virtual PRBool
00254     CompileSimpleAttributeCondition(PRInt32 aNameSpaceID,
00255                                     nsIAtom* aAttribute,
00256                                     const nsAString& aValue,
00257                                     InnerNode* aParentNode,
00258                                     TestNode** aResult);
00263     nsContentSupportMap mContentSupportMap;
00264 
00269     PRInt32 mContentVar;
00270 
00275     nsTemplateMap mTemplateMap;
00276 
00280     nsRDFSortState sortState;
00281 
00282     virtual nsresult
00283     ReplaceMatch(nsIRDFResource* aMember, const nsTemplateMatch* aOldMatch, nsTemplateMatch* aNewMatch);
00284 
00285     virtual nsresult
00286     SynchronizeMatch(nsTemplateMatch* aMatch, const VariableSet& aModifiedVars);
00287 
00288     // pseudo-constants
00289     static PRInt32 gRefCnt;
00290     static nsIXULSortService* gXULSortService;
00291 };
00292 
00293 PRInt32             nsXULContentBuilder::gRefCnt;
00294 nsIXULSortService*  nsXULContentBuilder::gXULSortService;
00295 
00296 NS_IMETHODIMP
00297 NS_NewXULContentBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult)
00298 {
00299     NS_PRECONDITION(aOuter == nsnull, "no aggregation");
00300     if (aOuter)
00301         return NS_ERROR_NO_AGGREGATION;
00302 
00303     nsresult rv;
00304     nsXULContentBuilder* result = new nsXULContentBuilder();
00305     if (! result)
00306         return NS_ERROR_OUT_OF_MEMORY;
00307 
00308     NS_ADDREF(result); // stabilize
00309 
00310     rv = result->Init();
00311 
00312     if (NS_SUCCEEDED(rv))
00313         rv = result->QueryInterface(aIID, aResult);
00314 
00315     NS_RELEASE(result);
00316     return rv;
00317 }
00318 
00319 nsXULContentBuilder::nsXULContentBuilder()
00320 {
00321 }
00322 
00323 nsXULContentBuilder::~nsXULContentBuilder()
00324 {
00325     if (--gRefCnt == 0) {
00326         NS_IF_RELEASE(gXULSortService);
00327     }
00328 }
00329 
00330 nsresult
00331 nsXULContentBuilder::Init()
00332 {
00333     if (gRefCnt++ == 0) {
00334         nsresult rv = CallGetService(kXULSortServiceCID, &gXULSortService);
00335         if (NS_FAILED(rv))
00336             return rv;
00337     }
00338 
00339     return nsXULTemplateBuilder::Init();
00340 }
00341 
00342 PRBool
00343 nsXULContentBuilder::IsIgnoreableAttribute(PRInt32 aNameSpaceID, nsIAtom* aAttribute)
00344 {
00345     // XXX Note that we patently ignore namespaces. This is because
00346     // HTML elements lie and tell us that their attributes are
00347     // _always_ in the HTML namespace. Urgh.
00348 
00349     // never copy the ID attribute
00350     if (aAttribute == nsXULAtoms::id) {
00351         return PR_TRUE;
00352     }
00353     // never copy {}:uri attribute
00354     else if (aAttribute == nsXULAtoms::uri) {
00355         return PR_TRUE;
00356     }
00357     else {
00358         return PR_FALSE;
00359     }
00360 }
00361 
00362 nsresult
00363 nsXULContentBuilder::BuildContentFromTemplate(nsIContent *aTemplateNode,
00364                                               nsIContent *aResourceNode,
00365                                               nsIContent *aRealNode,
00366                                               PRBool aIsUnique,
00367                                               nsIRDFResource* aChild,
00368                                               PRBool aNotify,
00369                                               nsTemplateMatch* aMatch,
00370                                               nsIContent** aContainer,
00371                                               PRInt32* aNewIndexInContainer)
00372 {
00373     // This is the mother lode. Here is where we grovel through an
00374     // element in the template, copying children from the template
00375     // into the "real" content tree, performing substitution as we go
00376     // by looking stuff up in the RDF graph.
00377     //
00378     //   |aTemplateNode| is the element in the "template tree", whose
00379     //   children we will duplicate and move into the "real" content
00380     //   tree.
00381     //
00382     //   |aResourceNode| is the element in the "real" content tree that
00383     //   has the "id" attribute set to an RDF resource's URI. This is
00384     //   not directly used here, but rather passed down to the XUL
00385     //   sort service to perform container-level sort.
00386     //
00387     //   |aRealNode| is the element in the "real" content tree to which
00388     //   the new elements will be copied.
00389     //
00390     //   |aIsUnique| is set to "true" so long as content has been
00391     //   "unique" (or "above" the resource element) so far in the
00392     //   template.
00393     //
00394     //   |aChild| is the RDF resource at the end of a property link for
00395     //   which we are building content.
00396     //
00397     //   |aNotify| is set to "true" if content should be constructed
00398     //   "noisily"; that is, whether the document observers should be
00399     //   notified when new content is added to the content model.
00400     //
00401     //   |aContainer| is an out parameter that will be set to the first
00402     //   container element in the "real" content tree to which content
00403     //   was appended.
00404     //
00405     //   |aNewIndexInContainer| is an out parameter that will be set to
00406     //   the index in aContainer at which new content is first
00407     //   constructed.
00408     //
00409     // If |aNotify| is "false", then |aContainer| and
00410     // |aNewIndexInContainer| are used to determine where in the
00411     // content model new content is constructed. This allows a single
00412     // notification to be propagated to document observers.
00413     //
00414 
00415     nsresult rv;
00416 
00417 #ifdef PR_LOGGING
00418     // Dump out the template node's tag, the template ID, and the RDF
00419     // resource that is being used as the index into the graph.
00420     if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
00421         nsXPIDLCString resourceCStr;
00422         rv = aChild->GetValue(getter_Copies(resourceCStr));
00423         if (NS_FAILED(rv)) return rv;
00424 
00425         const char *tagstr;
00426         aTemplateNode->Tag()->GetUTF8String(&tagstr);
00427 
00428         nsAutoString templatestr;
00429         aTemplateNode->GetAttr(kNameSpaceID_None, nsXULAtoms::id, templatestr);
00430         nsCAutoString templatestrC;
00431         templatestrC.AssignWithConversion(templatestr);
00432         PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
00433                ("xultemplate[%p] build-content-from-template %s (template='%s') [%s]",
00434                 this,
00435                 tagstr,
00436                 templatestrC.get(),
00437                 resourceCStr.get()));
00438     }
00439 #endif
00440 
00441     // Iterate through all of the template children, constructing
00442     // "real" content model nodes for each "template" child.
00443     PRUint32 count = aTemplateNode->GetChildCount();
00444 
00445     for (PRUint32 kid = 0; kid < count; kid++) {
00446         nsIContent *tmplKid = aTemplateNode->GetChildAt(kid);
00447 
00448         PRInt32 nameSpaceID = tmplKid->GetNameSpaceID();
00449 
00450         // Check whether this element is the "resource" element. The
00451         // "resource" element is the element that is cookie-cutter
00452         // copied once for each different RDF resource specified by
00453         // |aChild|.
00454         //
00455         // Nodes that appear -above- the resource element
00456         // (that is, are ancestors of the resource element in the
00457         // content model) are unique across all values of |aChild|,
00458         // and are created only once.
00459         //
00460         // Nodes that appear -below- the resource element (that is,
00461         // are descnendants of the resource element in the conte
00462         // model), are cookie-cutter copied for each distinct value of
00463         // |aChild|.
00464         //
00465         // For example, in a <tree> template:
00466         //
00467         //   <tree>
00468         //     <template>
00469         //       <treechildren> [1]
00470         //         <treeitem uri="rdf:*"> [2]
00471         //           <treerow> [3]
00472         //             <treecell value="rdf:urn:foo" /> [4]
00473         //             <treecell value="rdf:urn:bar" /> [5]
00474         //           </treerow>
00475         //         </treeitem>
00476         //       </treechildren>
00477         //     </template>
00478         //   </tree>
00479         //
00480         // The <treeitem> element [2] is the "resource element". This
00481         // element, and all of its descendants ([3], [4], and [5])
00482         // will be duplicated for each different |aChild|
00483         // resource. It's ancestor <treechildren> [1] is unique, and
00484         // will only be created -once-, no matter how many <treeitem>s
00485         // are created below it.
00486         //
00487         // Note that |isResourceElement| and |isUnique| are mutually
00488         // exclusive.
00489         PRBool isResourceElement = PR_FALSE;
00490         PRBool isUnique = aIsUnique;
00491 
00492         {
00493             // We identify the resource element by presence of a
00494             // "uri='rdf:*'" attribute. (We also support the older
00495             // "uri='...'" syntax.)
00496             nsAutoString uri;
00497             tmplKid->GetAttr(kNameSpaceID_None, nsXULAtoms::uri, uri);
00498 
00499             if ( !uri.IsEmpty() ) {
00500               if (aMatch->mRule && uri[0] == PRUnichar('?')) {
00501                   isResourceElement = PR_TRUE;
00502                   isUnique = PR_FALSE;
00503 
00504                   // XXXwaterson hack! refactor me please
00505                   Value member;
00506                   aMatch->mAssignments.GetAssignmentFor(aMatch->mRule->GetMemberVariable(), &member);
00507                   aChild = VALUE_TO_IRDFRESOURCE(member);
00508               }
00509               else if (uri.EqualsLiteral("...") || uri.EqualsLiteral("rdf:*")) {
00510                   // If we -are- the resource element, then we are no
00511                   // matter unique.
00512                   isResourceElement = PR_TRUE;
00513                   isUnique = PR_FALSE;
00514               }
00515             }
00516         }
00517 
00518         nsIAtom *tag = tmplKid->Tag();
00519 
00520 #ifdef PR_LOGGING
00521         if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
00522             const char *tagname;
00523             tag->GetUTF8String(&tagname);
00524             PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
00525                    ("xultemplate[%p]     building %s %s %s",
00526                     this, tagname,
00527                     (isResourceElement ? "[resource]" : ""),
00528                     (isUnique ? "[unique]" : "")));
00529         }
00530 #endif
00531 
00532         // Set to PR_TRUE if the child we're trying to create now
00533         // already existed in the content model.
00534         PRBool realKidAlreadyExisted = PR_FALSE;
00535 
00536         nsCOMPtr<nsIContent> realKid;
00537         if (isUnique) {
00538             // The content is "unique"; that is, we haven't descended
00539             // far enough into the tempalte to hit the "resource"
00540             // element yet. |EnsureElementHasGenericChild()| will
00541             // conditionally create the element iff it isn't there
00542             // already.
00543             rv = EnsureElementHasGenericChild(aRealNode, nameSpaceID, tag, aNotify, getter_AddRefs(realKid));
00544             if (NS_FAILED(rv)) return rv;
00545 
00546             if (rv == NS_RDF_ELEMENT_WAS_THERE) {
00547                 realKidAlreadyExisted = PR_TRUE;
00548             }
00549             else {
00550                 // Mark the element's contents as being generated so
00551                 // that any re-entrant calls don't trigger an infinite
00552                 // recursion.
00553                 nsXULElement *xulcontent = nsXULElement::FromContent(realKid);
00554                 if (xulcontent) {
00555                     xulcontent->SetLazyState(nsXULElement::eTemplateContentsBuilt);
00556                 }
00557 
00558                 // Potentially remember the index of this element as the first
00559                 // element that we've generated. Note that we remember
00560                 // this -before- we recurse!
00561                 if (aContainer && !*aContainer) {
00562                     *aContainer = aRealNode;
00563                     NS_ADDREF(*aContainer);
00564 
00565                     PRUint32 indx = aRealNode->GetChildCount();
00566 
00567                     // Since EnsureElementHasGenericChild() added us, make
00568                     // sure to subtract one for our real index.
00569                     *aNewIndexInContainer = indx - 1;
00570                 }
00571             }
00572 
00573             // Recurse until we get to the resource element. Since
00574             // -we're- unique, assume that our child will be
00575             // unique. The check for the "resource" element at the top
00576             // of the function will trip this to |false| as soon as we
00577             // encounter it.
00578             rv = BuildContentFromTemplate(tmplKid, aResourceNode, realKid, PR_TRUE,
00579                                           aChild, aNotify, aMatch,
00580                                           aContainer, aNewIndexInContainer);
00581 
00582             if (NS_FAILED(rv)) return rv;
00583         }
00584         else if (isResourceElement) {
00585             // It's the "resource" element. Create a new element using
00586             // the namespace ID and tag from the template element.
00587             rv = CreateElement(nameSpaceID, tag, getter_AddRefs(realKid));
00588             if (NS_FAILED(rv)) return rv;
00589 
00590             // Add the resource element to the content support map so
00591             // we can the match based on content node later.
00592             mContentSupportMap.Put(realKid, aMatch);
00593 
00594             // Assign the element an 'id' attribute using the URI of
00595             // the |aChild| resource.
00596             const char *uri;
00597             rv = aChild->GetValueConst(&uri);
00598             NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get resource URI");
00599             if (NS_FAILED(rv)) return rv;
00600 
00601             // XXX because gcc-2.7.2.3 is too dumb to keep a
00602             // compiler-generated temporary around.
00603             NS_ConvertUTF8toUCS2 x(uri);
00604             const nsAString& id = x;
00605             rv = realKid->SetAttr(kNameSpaceID_None, nsXULAtoms::id, id, PR_FALSE);
00606             NS_ASSERTION(NS_SUCCEEDED(rv), "unable to set id attribute");
00607             if (NS_FAILED(rv)) return rv;
00608 
00609             if (! aNotify) {
00610                 // XUL document will watch us, and take care of making
00611                 // sure that we get added to or removed from the
00612                 // element map if aNotify is true. If not, we gotta do
00613                 // it ourselves. Yay.
00614                 nsCOMPtr<nsIXULDocument> xuldoc =
00615                     do_QueryInterface(mRoot->GetDocument());
00616                 if (xuldoc)
00617                     xuldoc->AddElementForID(id, realKid);
00618             }
00619 
00620             // Set up the element's 'container' and 'empty'
00621             // attributes.
00622             PRBool iscontainer, isempty;
00623             rv = CheckContainer(aChild, &iscontainer, &isempty);
00624             if (NS_FAILED(rv)) return rv;
00625 
00626             if (iscontainer) {
00627                 realKid->SetAttr(kNameSpaceID_None, nsXULAtoms::container,
00628                                  NS_LITERAL_STRING("true"), PR_FALSE);
00629 
00630                 if (! (mFlags & eDontTestEmpty)) {
00631                     NS_NAMED_LITERAL_STRING(true_, "true");
00632                     NS_NAMED_LITERAL_STRING(false_, "false");
00633 
00634                     realKid->SetAttr(kNameSpaceID_None, nsXULAtoms::empty,
00635                                      isempty ? true_ : false_,
00636                                      PR_FALSE);
00637                 }
00638             }
00639         }
00640         else if (tag == nsXULAtoms::textnode &&
00641                  nameSpaceID == kNameSpaceID_XUL) {
00642             // <xul:text value="..."> is replaced by text of the
00643             // actual value of the 'rdf:resource' attribute for the
00644             // given node.
00645             PRUnichar attrbuf[128];
00646             nsFixedString attrValue(attrbuf, NS_ARRAY_LENGTH(attrbuf), 0);
00647             rv = tmplKid->GetAttr(kNameSpaceID_None, nsXULAtoms::value, attrValue);
00648             if (NS_FAILED(rv)) return rv;
00649 
00650             if ((rv == NS_CONTENT_ATTR_HAS_VALUE) && (!attrValue.IsEmpty())) {
00651                 nsAutoString value;
00652                 rv = SubstituteText(*aMatch, attrValue, value);
00653                 if (NS_FAILED(rv)) return rv;
00654 
00655                 nsCOMPtr<nsITextContent> content;
00656                 rv = NS_NewTextNode(getter_AddRefs(content),
00657                                     mRoot->GetNodeInfo()->NodeInfoManager());
00658                 if (NS_FAILED(rv)) return rv;
00659 
00660                 content->SetText(value, PR_FALSE);
00661 
00662                 rv = aRealNode->AppendChildTo(content, aNotify);
00663                 if (NS_FAILED(rv)) return rv;
00664 
00665                 // XXX Don't bother remembering text nodes as the
00666                 // first element we've generated?
00667             }
00668         }
00669         else if (tmplKid->IsContentOfType(nsIContent::eTEXT)) {
00670             nsCOMPtr<nsIDOMNode> tmplTextNode = do_QueryInterface(tmplKid);
00671             if (!tmplTextNode) {
00672                 NS_ERROR("textnode not implementing nsIDOMNode??");
00673                 return NS_ERROR_FAILURE;
00674             }
00675             nsCOMPtr<nsIDOMNode> clonedNode;
00676             tmplTextNode->CloneNode(PR_FALSE, getter_AddRefs(clonedNode));
00677             nsCOMPtr<nsIContent> clonedContent = do_QueryInterface(clonedNode);
00678             if (!clonedContent) {
00679                 NS_ERROR("failed to clone textnode");
00680                 return NS_ERROR_FAILURE;
00681             }
00682             rv = aRealNode->AppendChildTo(clonedContent, aNotify);
00683             if (NS_FAILED(rv)) return rv;
00684         }
00685         else {
00686             // It's just a generic element. Create it!
00687             rv = CreateElement(nameSpaceID, tag, getter_AddRefs(realKid));
00688             if (NS_FAILED(rv)) return rv;
00689         }
00690 
00691         if (realKid && !realKidAlreadyExisted) {
00692             // Potentially remember the index of this element as the
00693             // first element that we've generated.
00694             if (aContainer && !*aContainer) {
00695                 *aContainer = aRealNode;
00696                 NS_ADDREF(*aContainer);
00697 
00698                 PRUint32 indx = aRealNode->GetChildCount();
00699 
00700                 // Since we haven't inserted any content yet, our new
00701                 // index in the container will be the current count of
00702                 // elements in the container.
00703                 *aNewIndexInContainer = indx;
00704             }
00705 
00706             // Remember the template kid from which we created the
00707             // real kid. This allows us to sync back up with the
00708             // template to incrementally build content.
00709             mTemplateMap.Put(realKid, tmplKid);
00710 
00711             // Copy all attributes from the template to the new
00712             // element.
00713             PRUint32 numAttribs = tmplKid->GetAttrCount();
00714 
00715             for (PRUint32 attr = 0; attr < numAttribs; attr++) {
00716                 PRInt32 attribNameSpaceID;
00717                 nsCOMPtr<nsIAtom> attribName, prefix;
00718 
00719                 rv = tmplKid->GetAttrNameAt(attr, &attribNameSpaceID,
00720                                             getter_AddRefs(attribName),
00721                                             getter_AddRefs(prefix));
00722                 if (NS_FAILED(rv)) return rv;
00723 
00724                 if (! IsIgnoreableAttribute(attribNameSpaceID, attribName)) {
00725                     // Create a buffer here, because there's a good
00726                     // chance that an attribute in the template is
00727                     // going to be an RDF URI, which is usually
00728                     // longish.
00729                     PRUnichar attrbuf[128];
00730                     nsFixedString attribValue(attrbuf, NS_ARRAY_LENGTH(attrbuf), 0);
00731                     rv = tmplKid->GetAttr(attribNameSpaceID, attribName, attribValue);
00732                     if (NS_FAILED(rv)) return rv;
00733 
00734                     if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
00735                         nsAutoString value;
00736                         rv = SubstituteText(*aMatch, attribValue, value);
00737                         if (NS_FAILED(rv)) return rv;
00738 
00739                         rv = realKid->SetAttr(attribNameSpaceID, attribName, value, PR_FALSE);
00740                         if (NS_FAILED(rv)) return rv;
00741                     }
00742                 }
00743             }
00744 
00745             // Add any persistent attributes
00746             if (isResourceElement) {
00747                 rv = AddPersistentAttributes(tmplKid, aChild, realKid);
00748                 if (NS_FAILED(rv)) return rv;
00749             }
00750 
00751             
00752             nsXULElement *xulcontent = nsXULElement::FromContent(realKid);
00753             if (xulcontent) {
00754                 PRUint32 count2 = tmplKid->GetChildCount();
00755 
00756                 if (count2 == 0 && !isResourceElement) {
00757                     // If we're at a leaf node, then we'll eagerly
00758                     // mark the content as having its template &
00759                     // container contents built. This avoids a useless
00760                     // trip back to the template builder only to find
00761                     // that we've got no work to do!
00762                     xulcontent->SetLazyState(nsXULElement::eTemplateContentsBuilt);
00763                     xulcontent->SetLazyState(nsXULElement::eContainerContentsBuilt);
00764                 }
00765                 else {
00766                     // Just mark the XUL element as requiring more work to
00767                     // be done. We'll get around to it when somebody asks
00768                     // for it.
00769                     xulcontent->SetLazyState(nsXULElement::eChildrenMustBeRebuilt);
00770                 }
00771             }
00772             else {
00773                 // Otherwise, it doesn't support lazy instantiation,
00774                 // and we have to recurse "by hand". Note that we
00775                 // _don't_ need to notify: we'll add the entire
00776                 // subtree in a single whack.
00777                 //
00778                 // Note that we don't bother passing aContainer and
00779                 // aNewIndexInContainer down: since we're HTML, we
00780                 // -know- that we -must- have just been created.
00781                 rv = BuildContentFromTemplate(tmplKid, aResourceNode, realKid, isUnique,
00782                                               aChild, PR_FALSE, aMatch,
00783                                               nsnull /* don't care */,
00784                                               nsnull /* don't care */);
00785 
00786                 if (NS_FAILED(rv)) return rv;
00787 
00788                 if (isResourceElement) {
00789                     rv = CreateContainerContents(realKid, aChild, PR_FALSE,
00790                                                  nsnull /* don't care */,
00791                                                  nsnull /* don't care */);
00792                     if (NS_FAILED(rv)) return rv;
00793                 }
00794             }
00795 
00796             // We'll _already_ have added the unique elements; but if
00797             // it's -not- unique, then use the XUL sort service now to
00798             // append the element to the content model.
00799             if (! isUnique) {
00800                 rv = NS_ERROR_UNEXPECTED;
00801 
00802                 if (gXULSortService && isResourceElement) {
00803                     rv = gXULSortService->InsertContainerNode(mCompDB, &sortState,
00804                                                               mRoot, aResourceNode,
00805                                                               aRealNode, realKid,
00806                                                               aNotify);
00807                 }
00808 
00809                 if (NS_FAILED(rv)) {
00810                     rv = aRealNode->AppendChildTo(realKid, aNotify);
00811                     NS_ASSERTION(NS_SUCCEEDED(rv), "unable to insert element");
00812                 }
00813             }
00814         }
00815     }
00816 
00817     return NS_OK;
00818 }
00819 
00820 
00821 nsresult
00822 nsXULContentBuilder::AddPersistentAttributes(nsIContent* aTemplateNode,
00823                                              nsIRDFResource* aResource,
00824                                              nsIContent* aRealNode)
00825 {
00826     nsresult rv;
00827 
00828     nsAutoString persist;
00829     rv = aTemplateNode->GetAttr(kNameSpaceID_None, nsXULAtoms::persist, persist);
00830     if (NS_FAILED(rv)) return rv;
00831 
00832     if (rv != NS_CONTENT_ATTR_HAS_VALUE)
00833         return NS_OK;
00834 
00835     nsAutoString attribute;
00836     while (!persist.IsEmpty()) {
00837         attribute.Truncate();
00838 
00839         PRInt32 offset = persist.FindCharInSet(" ,");
00840         if (offset > 0) {
00841             persist.Left(attribute, offset);
00842             persist.Cut(0, offset + 1);
00843         }
00844         else {
00845             attribute = persist;
00846             persist.Truncate();
00847         }
00848 
00849         attribute.Trim(" ");
00850 
00851         if (attribute.IsEmpty())
00852             break;
00853 
00854         nsCOMPtr<nsIAtom> tag;
00855         PRInt32 nameSpaceID;
00856 
00857         nsCOMPtr<nsINodeInfo> ni =
00858             aTemplateNode->GetExistingAttrNameFromQName(attribute);
00859         if (ni) {
00860             tag = ni->NameAtom();
00861             nameSpaceID = ni->NamespaceID();
00862         }
00863         else {
00864             tag = do_GetAtom(attribute);
00865             NS_ENSURE_TRUE(tag, NS_ERROR_OUT_OF_MEMORY);
00866 
00867             nameSpaceID = kNameSpaceID_None;
00868         }
00869 
00870         nsCOMPtr<nsIRDFResource> property;
00871         rv = nsXULContentUtils::GetResource(nameSpaceID, tag, getter_AddRefs(property));
00872         if (NS_FAILED(rv)) return rv;
00873 
00874         nsCOMPtr<nsIRDFNode> target;
00875         rv = mDB->GetTarget(aResource, property, PR_TRUE, getter_AddRefs(target));
00876         if (NS_FAILED(rv)) return rv;
00877 
00878         if (! target)
00879             continue;
00880 
00881         nsCOMPtr<nsIRDFLiteral> value = do_QueryInterface(target);
00882         NS_ASSERTION(value != nsnull, "unable to stomach that sort of node");
00883         if (! value)
00884             continue;
00885 
00886         const PRUnichar* valueStr;
00887         rv = value->GetValueConst(&valueStr);
00888         if (NS_FAILED(rv)) return rv;
00889 
00890         rv = aRealNode->SetAttr(nameSpaceID, tag, nsDependentString(valueStr),
00891                                 PR_FALSE);
00892         if (NS_FAILED(rv)) return rv;
00893     }
00894 
00895     return NS_OK;
00896 }
00897 
00898 nsresult
00899 nsXULContentBuilder::SynchronizeUsingTemplate(nsIContent* aTemplateNode,
00900                                               nsIContent* aRealElement,
00901                                               nsTemplateMatch& aMatch,
00902                                               const VariableSet& aModifiedVars)
00903 {
00904     nsresult rv;
00905 
00906     // check all attributes on the template node; if they reference a resource,
00907     // update the equivalent attribute on the content node
00908 
00909     PRUint32 numAttribs = aTemplateNode->GetAttrCount();
00910 
00911     for (PRUint32 loop = 0; loop < numAttribs; ++loop) {
00912         PRInt32    attribNameSpaceID;
00913         nsCOMPtr<nsIAtom> attribName, prefix;
00914         rv = aTemplateNode->GetAttrNameAt(loop, &attribNameSpaceID,
00915                                           getter_AddRefs(attribName),
00916                                           getter_AddRefs(prefix));
00917         if (NS_FAILED(rv)) break;
00918 
00919         // See if it's one of the attributes that we unilaterally
00920         // ignore. If so, on to the next one...
00921         if (IsIgnoreableAttribute(attribNameSpaceID, attribName))
00922             continue;
00923 
00924         nsAutoString attribValue;
00925         rv = aTemplateNode->GetAttr(attribNameSpaceID,
00926                                     attribName,
00927                                     attribValue);
00928 
00929         if (! IsAttrImpactedByVars(aMatch, attribValue, aModifiedVars))
00930             continue;
00931 
00932         nsAutoString newvalue;
00933         SubstituteText(aMatch, attribValue, newvalue);
00934 
00935         if (!newvalue.IsEmpty()) {
00936             aRealElement->SetAttr(attribNameSpaceID,
00937                                   attribName,
00938                                   newvalue,
00939                                   PR_TRUE);
00940         }
00941         else {
00942             aRealElement->UnsetAttr(attribNameSpaceID,
00943                                     attribName,
00944                                     PR_TRUE);
00945         }
00946     }
00947 
00948     // See if we've generated kids for this node yet. If we have, then
00949     // recursively sync up template kids with content kids
00950     PRBool contentsGenerated = PR_TRUE;
00951     nsXULElement *xulcontent = nsXULElement::FromContent(aRealElement);
00952     if (xulcontent) {
00953         contentsGenerated = xulcontent->GetLazyState(nsXULElement::eTemplateContentsBuilt);
00954     }
00955     else {
00956         // HTML content will _always_ have been generated up-front
00957     }
00958 
00959     if (contentsGenerated) {
00960         PRUint32 count = aTemplateNode->GetChildCount();
00961 
00962         for (PRUint32 loop = 0; loop < count; ++loop) {
00963             nsIContent *tmplKid = aTemplateNode->GetChildAt(loop);
00964 
00965             if (! tmplKid)
00966                 break;
00967 
00968             nsIContent *realKid = aRealElement->GetChildAt(loop);
00969 
00970             if (! realKid)
00971                 break;
00972 
00973             rv = SynchronizeUsingTemplate(tmplKid, realKid, aMatch, aModifiedVars);
00974             if (NS_FAILED(rv)) return rv;
00975         }
00976     }
00977 
00978     return NS_OK;
00979 }
00980 
00981 PRBool
00982 nsXULContentBuilder::IsDirectlyContainedBy(nsIContent* aChild, nsIContent* aParent)
00983 {
00984     // This routine uses the <template> to determine if aChild is
00985     // "directly contained by" aParent. It does so by walking up the
00986     // template subtree in parallel with the generated subtree.
00987     NS_PRECONDITION(aChild != nsnull, "null ptr");
00988     if (! aChild)
00989         return PR_FALSE;
00990 
00991     // First, we need to find the template from which this element was
00992     // generated.
00993     nsCOMPtr<nsIContent> tmpl;
00994     mTemplateMap.GetTemplateFor(aChild, getter_AddRefs(tmpl));
00995     if (! tmpl)
00996         return PR_FALSE;
00997 
00998     // Now walk up the template subtree in parallel with the generated
00999     // subtree.
01000     nsINodeInfo *ni;
01001     nsCOMPtr<nsIContent> generated(aChild);
01002 
01003     do {
01004         // XXX - gcc 2.95.x -O3 builds are known to break if
01005         // we declare nsCOMPtrs inside this loop.  Moving them
01006         // out of the loop or using a normal pointer works
01007         // around this problem.
01008         // http://bugzilla.mozilla.org/show_bug.cgi?id=61501
01009 
01010         // Walk up the generated tree
01011         generated = generated->GetParent();
01012         if (! generated) 
01013             return PR_FALSE;
01014 
01015         // Walk up the template tree
01016         tmpl = tmpl->GetParent();
01017         if (! tmpl)
01018             return PR_FALSE;
01019 
01020         // The content within a template ends when we hit the
01021         // <template> or <rule> element in the simple syntax, or the
01022         // <action> element in the extended syntax.
01023         ni = tmpl->GetNodeInfo();
01024     } while (!ni->Equals(nsXULAtoms::templateAtom, kNameSpaceID_XUL) &&
01025              !ni->Equals(nsXULAtoms::rule, kNameSpaceID_XUL) &&
01026              !ni->Equals(nsXULAtoms::action, kNameSpaceID_XUL));
01027 
01028     // Did we find the generated parent?
01029     return PRBool(generated.get() == aParent);
01030 }
01031 
01032 
01033 nsresult
01034 nsXULContentBuilder::RemoveMember(nsIContent* aContainerElement,
01035                                   nsIRDFResource* aMember,
01036                                   PRBool aNotify)
01037 {
01038     // This works as follows. It finds all of the elements in the
01039     // document that correspond to aMember. Any that are contained
01040     // within aContainerElement are removed from their direct parent.
01041     nsresult rv;
01042 
01043     nsCOMPtr<nsISupportsArray> elements;
01044     rv = NS_NewISupportsArray(getter_AddRefs(elements));
01045     if (NS_FAILED(rv)) return rv;
01046 
01047     rv = GetElementsForResource(aMember, elements);
01048     if (NS_FAILED(rv)) return rv;
01049 
01050     PRUint32 cnt;
01051     rv = elements->Count(&cnt);
01052     if (NS_FAILED(rv)) return rv;
01053 
01054     for (PRInt32 i = PRInt32(cnt) - 1; i >= 0; --i) {
01055         nsISupports* isupports = elements->ElementAt(i);
01056         nsCOMPtr<nsIContent> child( do_QueryInterface(isupports) );
01057         NS_IF_RELEASE(isupports);
01058 
01059         if (! IsDirectlyContainedBy(child, aContainerElement))
01060             continue;
01061 
01062         nsCOMPtr<nsIContent> parent = child->GetParent();
01063 
01064         PRInt32 pos = parent->IndexOf(child);
01065 
01066         NS_ASSERTION(pos >= 0, "parent doesn't think this child has an index");
01067         if (pos < 0) continue;
01068 
01069         // Note: RemoveChildAt sets |child|'s document to null so that
01070         // it'll get knocked out of the XUL doc's resource-to-element
01071         // map.
01072         rv = parent->RemoveChildAt(pos, aNotify);
01073         if (NS_FAILED(rv)) return rv;
01074 
01075         // Remove from the content support map.
01076         mContentSupportMap.Remove(child);
01077 
01078         // Remove from the template map
01079         mTemplateMap.Remove(child);
01080 
01081 #ifdef PR_LOGGING
01082         if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_ALWAYS)) {
01083             const char *parentTagStr;
01084             parent->Tag()->GetUTF8String(&parentTagStr);
01085 
01086             const char *childTagStr;
01087             child->Tag()->GetUTF8String(&childTagStr);
01088 
01089             const char* resourceCStr;
01090             rv = aMember->GetValueConst(&resourceCStr);
01091             if (NS_FAILED(rv)) return rv;
01092             
01093             PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
01094                    ("xultemplate[%p] remove-member %s->%s [%s]",
01095                     this,
01096                     parentTagStr,
01097                     childTagStr,
01098                     resourceCStr));
01099         }
01100 #endif
01101     }
01102 
01103     return NS_OK;
01104 }
01105 
01106 
01107 nsresult
01108 nsXULContentBuilder::CreateTemplateAndContainerContents(nsIContent* aElement,
01109                                                         nsIContent** aContainer,
01110                                                         PRInt32* aNewIndexInContainer)
01111 {
01112     // Generate both 1) the template content for the current element,
01113     // and 2) recursive subcontent (if the current element refers to a
01114     // container resource in the RDF graph).
01115 
01116     // If we're asked to return the first generated child, then
01117     // initialize to "none".
01118     if (aContainer) {
01119         *aContainer = nsnull;
01120         *aNewIndexInContainer = -1;
01121     }
01122 
01123     // Create the current resource's contents from the template, if
01124     // appropriate
01125     nsCOMPtr<nsIContent> tmpl;
01126     mTemplateMap.GetTemplateFor(aElement, getter_AddRefs(tmpl));
01127 
01128     if (tmpl)
01129         CreateTemplateContents(aElement, tmpl, aContainer, aNewIndexInContainer);
01130 
01131     nsCOMPtr<nsIRDFResource> resource;
01132     nsXULContentUtils::GetElementRefResource(aElement, getter_AddRefs(resource));
01133     if (resource) {
01134         // The element has a resource; that means that it corresponds
01135         // to something in the graph, so we need to go to the graph to
01136         // create its contents.
01137         CreateContainerContents(aElement, resource, PR_FALSE, aContainer, aNewIndexInContainer);
01138     }
01139 
01140     return NS_OK;
01141 }
01142 
01143 
01144 nsresult
01145 nsXULContentBuilder::CreateContainerContents(nsIContent* aElement,
01146                                              nsIRDFResource* aResource,
01147                                              PRBool aNotify,
01148                                              nsIContent** aContainer,
01149                                              PRInt32* aNewIndexInContainer)
01150 {
01151     // Avoid re-entrant builds for the same resource.
01152     if (IsActivated(aResource))
01153         return NS_OK;
01154 
01155     ActivationEntry entry(aResource, &mTop);
01156 
01157     // Create the contents of a container by iterating over all of the
01158     // "containment" arcs out of the element's resource.
01159     nsresult rv;
01160 
01161     // Compile the rules now, if they haven't been already.
01162     if (! mRulesCompiled) {
01163         rv = CompileRules();
01164         if (NS_FAILED(rv)) return rv;
01165     }
01166     
01167     if (aContainer) {
01168         *aContainer = nsnull;
01169         *aNewIndexInContainer = -1;
01170     }
01171 
01172     // The tree widget is special. If the item isn't open, then just
01173     // "pretend" that there aren't any contents here. We'll create
01174     // them when OpenContainer() gets called.
01175     if (IsLazyWidgetItem(aElement) && !IsOpen(aElement))
01176         return NS_OK;
01177 
01178     // See if the element's templates contents have been generated:
01179     // this prevents a re-entrant call from triggering another
01180     // generation.
01181     nsXULElement *xulcontent = nsXULElement::FromContent(aElement);
01182     if (xulcontent) {
01183         if (xulcontent->GetLazyState(nsXULElement::eContainerContentsBuilt))
01184             return NS_OK;
01185 
01186         // Now mark the element's contents as being generated so that
01187         // any re-entrant calls don't trigger an infinite recursion.
01188         xulcontent->SetLazyState(nsXULElement::eContainerContentsBuilt);
01189     }
01190     else {
01191         // HTML is always needs to be generated.
01192         //
01193         // XXX Big ass-umption here -- I am assuming that this will
01194         // _only_ ever get called (in the case of an HTML element)
01195         // when the XUL builder is descending thru the graph and
01196         // stumbles on a template that is rooted at an HTML element.
01197         // (/me crosses fingers...)
01198     }
01199 
01200     // Seed the rule network with assignments for the content and
01201     // container variables
01202     //
01203     // XXXwaterson could this code be shared with
01204     // nsXULTemplateBuilder::Propagate()?
01205     Instantiation seed;
01206     seed.AddAssignment(mContentVar, Value(aElement));
01207 
01208     InstantiationSet instantiations;
01209     instantiations.Append(seed);
01210 
01211     // Propagate the assignments through the network
01212     nsClusterKeySet newkeys;
01213     mRules.GetRoot()->Propagate(instantiations, &newkeys);
01214 
01215     // Iterate through newly added keys to determine which rules fired
01216     nsClusterKeySet::ConstIterator last = newkeys.Last();
01217     for (nsClusterKeySet::ConstIterator key = newkeys.First(); key != last; ++key) {
01218         nsConflictSet::MatchCluster* matches =
01219             mConflictSet.GetMatchesForClusterKey(*key);
01220 
01221         if (! matches)
01222             continue;
01223 
01224         nsTemplateMatch* match = 
01225             mConflictSet.GetMatchWithHighestPriority(matches);
01226 
01227         NS_ASSERTION(match != nsnull, "no best match in match set");
01228         if (! match)
01229             continue;
01230 
01231         // Grab the template node
01232         nsCOMPtr<nsIContent> tmpl;
01233         match->mRule->GetContent(getter_AddRefs(tmpl));
01234 
01235         BuildContentFromTemplate(tmpl, aElement, aElement, PR_TRUE,
01236                                  VALUE_TO_IRDFRESOURCE(key->mMemberValue),
01237                                  aNotify, match,
01238                                  aContainer, aNewIndexInContainer);
01239 
01240         // Remember this as the "last" match
01241         matches->mLastMatch = match;
01242     }
01243 
01244     return NS_OK;
01245 }
01246 
01247 
01248 nsresult
01249 nsXULContentBuilder::CreateTemplateContents(nsIContent* aElement,
01250                                             nsIContent* aTemplateElement,
01251                                             nsIContent** aContainer,
01252                                             PRInt32* aNewIndexInContainer)
01253 {
01254     // Create the contents of an element using the templates
01255     nsresult rv;
01256 
01257     // See if the element's templates contents have been generated:
01258     // this prevents a re-entrant call from triggering another
01259     // generation.
01260     nsXULElement *xulcontent = nsXULElement::FromContent(aElement);
01261     if (! xulcontent)
01262         return NS_OK; // HTML content is _always_ generated up-front
01263 
01264     if (xulcontent->GetLazyState(nsXULElement::eTemplateContentsBuilt))
01265         return NS_OK;
01266 
01267     // Now mark the element's contents as being generated so that
01268     // any re-entrant calls don't trigger an infinite recursion.
01269     xulcontent->SetLazyState(nsXULElement::eTemplateContentsBuilt);
01270 
01271     // Crawl up the content model until we find the "resource" element
01272     // that spawned this template.
01273     nsCOMPtr<nsIRDFResource> resource;
01274 
01275     nsCOMPtr<nsIContent> element;
01276     for (element = aElement; element; element = element->GetParent()) {
01277         nsXULContentUtils::GetElementRefResource(element, getter_AddRefs(resource));
01278         if (resource)
01279             break;
01280     }
01281 
01282     NS_ASSERTION(element != nsnull, "walked off the top of the content tree");
01283     if (! element)
01284         return NS_ERROR_FAILURE;
01285 
01286     nsTemplateMatch* match = nsnull;
01287     mContentSupportMap.Get(element, &match);
01288 
01289     NS_ASSERTION(match != nsnull, "no match in the content support map");
01290     if (! match)
01291         return NS_ERROR_FAILURE;
01292 
01293     rv = BuildContentFromTemplate(aTemplateElement, aElement, aElement, PR_FALSE, resource, PR_FALSE,
01294                                   match, aContainer, aNewIndexInContainer);
01295 
01296     if (NS_FAILED(rv)) return rv;
01297 
01298     return NS_OK;
01299 }
01300 
01301 nsresult
01302 nsXULContentBuilder::EnsureElementHasGenericChild(nsIContent* parent,
01303                                                   PRInt32 nameSpaceID,
01304                                                   nsIAtom* tag,
01305                                                   PRBool aNotify,
01306                                                   nsIContent** result)
01307 {
01308     nsresult rv;
01309 
01310     rv = nsXULContentUtils::FindChildByTag(parent, nameSpaceID, tag, result);
01311     if (NS_FAILED(rv)) return rv;
01312 
01313     if (rv == NS_RDF_NO_VALUE) {
01314         // we need to construct a new child element.
01315         nsCOMPtr<nsIContent> element;
01316 
01317         rv = CreateElement(nameSpaceID, tag, getter_AddRefs(element));
01318         if (NS_FAILED(rv)) return rv;
01319 
01320         // XXX Note that the notification ensures we won't batch insertions! This could be bad! - Dave
01321         rv = parent->AppendChildTo(element, aNotify);
01322         if (NS_FAILED(rv)) return rv;
01323 
01324         *result = element;
01325         NS_ADDREF(*result);
01326         return NS_RDF_ELEMENT_GOT_CREATED;
01327     }
01328     else {
01329         return NS_RDF_ELEMENT_WAS_THERE;
01330     }
01331 }
01332 
01333 PRBool
01334 nsXULContentBuilder::IsOpen(nsIContent* aElement)
01335 {
01336     nsresult rv;
01337 
01338     // XXXhyatt - use XBL service to obtain base tag.
01339 
01340     nsIAtom *tag = aElement->Tag();
01341 
01342     // Treat the 'root' element as always open, -unless- it's a
01343     // menu/menupopup. We don't need to "fake" these as being open.
01344     if ((aElement == mRoot) && aElement->IsContentOfType(nsIContent::eXUL) &&
01345         (tag != nsXULAtoms::menu) &&
01346         (tag != nsXULAtoms::menubutton) &&
01347         (tag != nsXULAtoms::toolbarbutton) &&
01348         (tag != nsXULAtoms::button))
01349       return PR_TRUE;
01350 
01351     nsAutoString value;
01352     rv = aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::open, value);
01353     NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get open attribute");
01354 
01355     return (rv == NS_CONTENT_ATTR_HAS_VALUE &&
01356             value.EqualsLiteral("true"));
01357 }
01358 
01359 
01360 nsresult
01361 nsXULContentBuilder::RemoveGeneratedContent(nsIContent* aElement)
01362 {
01363     // Keep a queue of "ungenerated" elements that we have to probe
01364     // for generated content.
01365     nsAutoVoidArray ungenerated;
01366     ungenerated.AppendElement(aElement);
01367 
01368     PRInt32 count;
01369     while (0 != (count = ungenerated.Count())) {
01370         // Pull the next "ungenerated" element off the queue.
01371         PRInt32 last = count - 1;
01372         nsIContent* element = NS_STATIC_CAST(nsIContent*, ungenerated[last]);
01373         ungenerated.RemoveElementAt(last);
01374 
01375         PRUint32 i = element->GetChildCount();
01376 
01377         while (i-- > 0) {
01378             nsCOMPtr<nsIContent> child = element->GetChildAt(i);
01379 
01380             // Optimize for the <template> element, because we *know*
01381             // it won't have any generated content: there's no reason
01382             // to even check this subtree.
01383             nsINodeInfo *ni = element->GetNodeInfo();
01384             if (!ni || ni->Equals(nsXULAtoms::templateAtom, kNameSpaceID_XUL))
01385                 continue;
01386 
01387             // If the element is in the template map, then we
01388             // assume it's been generated and nuke it.
01389             nsCOMPtr<nsIContent> tmpl;
01390             mTemplateMap.GetTemplateFor(child, getter_AddRefs(tmpl));
01391 
01392             if (! tmpl) {
01393                 // No 'template' attribute, so this must not have been
01394                 // generated. We'll need to examine its kids.
01395                 ungenerated.AppendElement(child);
01396                 continue;
01397             }
01398 
01399             // If we get here, it's "generated". Bye bye!
01400             element->RemoveChildAt(i, PR_TRUE);
01401 
01402             // Remove element from the conflict set.
01403             // XXXwaterson should this be moved into NoteGeneratedSubtreeRemoved?
01404             nsTemplateMatchSet firings(mConflictSet.GetPool());
01405             nsTemplateMatchSet retractions(mConflictSet.GetPool());
01406             mConflictSet.Remove(nsContentTestNode::Element(child), firings, retractions);
01407 
01408             // Remove this and any children from the content support map.
01409             mContentSupportMap.Remove(child);
01410 
01411             // Remove from the template map
01412             mTemplateMap.Remove(child);
01413         }
01414     }
01415 
01416     return NS_OK;
01417 }
01418 
01419 PRBool
01420 nsXULContentBuilder::IsLazyWidgetItem(nsIContent* aElement)
01421 {
01422     // Determine if this is a <tree>, <treeitem>, or <menu> element
01423 
01424     if (!aElement->IsContentOfType(nsIContent::eXUL)) {
01425         return PR_FALSE;
01426     }
01427 
01428     // XXXhyatt Use the XBL service to obtain a base tag.
01429 
01430     nsIAtom *tag = aElement->Tag();
01431 
01432     return (tag == nsXULAtoms::menu ||
01433             tag == nsXULAtoms::menulist ||
01434             tag == nsXULAtoms::menubutton ||
01435             tag == nsXULAtoms::toolbarbutton ||
01436             tag == nsXULAtoms::button ||
01437             tag == nsXULAtoms::treeitem);
01438 }
01439 
01440 nsresult
01441 nsXULContentBuilder::GetElementsForResource(nsIRDFResource* aResource,
01442                                             nsISupportsArray* aElements)
01443 {
01444     const char *uri;
01445     aResource->GetValueConst(&uri);
01446 
01447     nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(mRoot->GetDocument());
01448     NS_ASSERTION(xuldoc, "expected a XUL document");
01449     if (! xuldoc)
01450         return NS_ERROR_FAILURE;
01451 
01452     return xuldoc->GetElementsForID(NS_ConvertUTF8toUCS2(uri), aElements);
01453 }
01454 
01455 nsresult
01456 nsXULContentBuilder::CreateElement(PRInt32 aNameSpaceID,
01457                                    nsIAtom* aTag,
01458                                    nsIContent** aResult)
01459 {
01460     nsCOMPtr<nsIDocument> doc = mRoot->GetDocument();
01461     NS_ASSERTION(doc != nsnull, "not initialized");
01462     if (! doc)
01463         return NS_ERROR_NOT_INITIALIZED;
01464 
01465     nsresult rv;
01466     nsCOMPtr<nsIContent> result;
01467 
01468     nsCOMPtr<nsINodeInfo> nodeInfo;
01469     doc->NodeInfoManager()->GetNodeInfo(aTag, nsnull, aNameSpaceID,
01470                                         getter_AddRefs(nodeInfo));
01471 
01472     rv = NS_NewElement(getter_AddRefs(result), aNameSpaceID, nodeInfo);
01473     if (NS_FAILED(rv)) return rv;
01474 
01475     *aResult = result;
01476     NS_ADDREF(*aResult);
01477     return NS_OK;
01478 }
01479 
01480 nsresult
01481 nsXULContentBuilder::SetContainerAttrs(nsIContent *aElement, const nsTemplateMatch* aMatch)
01482 {
01483     NS_PRECONDITION(aMatch->mRule != nsnull, "null ptr");
01484     if (! aMatch->mRule)
01485         return NS_ERROR_NULL_POINTER;
01486 
01487     Value containerval;
01488     aMatch->mAssignments.GetAssignmentFor(aMatch->mRule->GetContainerVariable(), &containerval);
01489 
01490     nsAutoString oldcontainer;
01491     aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::container, oldcontainer);
01492 
01493     PRBool iscontainer, isempty;
01494     CheckContainer(VALUE_TO_IRDFRESOURCE(containerval), &iscontainer, &isempty);
01495 
01496     NS_NAMED_LITERAL_STRING(true_, "true");
01497     NS_NAMED_LITERAL_STRING(false_, "false");
01498     
01499     const nsAString& newcontainer =
01500         iscontainer ? true_ : false_;
01501 
01502     if (oldcontainer != newcontainer) {
01503         aElement->SetAttr(kNameSpaceID_None, nsXULAtoms::container,
01504                           newcontainer, PR_TRUE);
01505     }
01506 
01507     if (! (mFlags & eDontTestEmpty)) {
01508         nsAutoString oldempty;
01509         aElement->GetAttr(kNameSpaceID_None, nsXULAtoms::empty, oldempty);
01510 
01511         const nsAString& newempty =
01512             (iscontainer && isempty) ? true_ : false_;
01513 
01514         if (oldempty != newempty) {
01515             aElement->SetAttr(kNameSpaceID_None, nsXULAtoms::empty,
01516                               newempty, PR_TRUE);
01517         }
01518     }
01519 
01520     return NS_OK;
01521 }
01522 
01523 
01524 //----------------------------------------------------------------------
01525 //
01526 // nsIXULTemplateBuilder methods
01527 //
01528 
01529 NS_IMETHODIMP
01530 nsXULContentBuilder::CreateContents(nsIContent* aElement)
01531 {
01532     NS_PRECONDITION(aElement != nsnull, "null ptr");
01533     if (! aElement)
01534         return NS_ERROR_NULL_POINTER;
01535 
01536     NS_ASSERTION(IsElementInBuilder(aElement, this), "element not managed by this template builder");
01537 
01538     return CreateTemplateAndContainerContents(aElement, nsnull /* don't care */, nsnull /* don't care */);
01539 }
01540 
01541 //----------------------------------------------------------------------
01542 //
01543 // nsIDocumentObserver methods
01544 //
01545 
01546 void
01547 nsXULContentBuilder::AttributeChanged(nsIDocument* aDocument,
01548                                       nsIContent*  aContent,
01549                                       PRInt32      aNameSpaceID,
01550                                       nsIAtom*     aAttribute,
01551                                       PRInt32      aModType)
01552 {
01553     // Handle "open" and "close" cases. We do this handling before
01554     // we've notified the observer, so that content is already created
01555     // for the frame system to walk.
01556     if ((aContent->GetNameSpaceID() == kNameSpaceID_XUL) &&
01557         (aAttribute == nsXULAtoms::open)) {
01558         // We're on a XUL tag, and an ``open'' attribute changed.
01559         nsAutoString open;
01560         aContent->GetAttr(kNameSpaceID_None, nsXULAtoms::open, open);
01561 
01562         if (open.EqualsLiteral("true"))
01563             OpenContainer(aContent);
01564         else
01565             CloseContainer(aContent);
01566     }
01567 
01568     // Pass along to the generic template builder.
01569     nsXULTemplateBuilder::AttributeChanged(aDocument, aContent, aNameSpaceID,
01570                                            aAttribute, aModType);
01571 }
01572 
01573 void
01574 nsXULContentBuilder::DocumentWillBeDestroyed(nsIDocument *aDocument)
01575 {
01576     // Break circular references
01577     mContentSupportMap.Clear();
01578 
01579     nsXULTemplateBuilder::DocumentWillBeDestroyed(aDocument);
01580 }
01581 
01582 
01583 //----------------------------------------------------------------------
01584 //
01585 // nsXULTemplateBuilder methods
01586 //
01587 
01588 nsresult
01589 nsXULContentBuilder::ReplaceMatch(nsIRDFResource* aMember,
01590                                   const nsTemplateMatch* aOldMatch,
01591                                   nsTemplateMatch* aNewMatch)
01592 {
01593     if (aOldMatch) {
01594         // See if we need to yank anything out of the content
01595         // model to handle the newly matched rule. If the
01596         // instantiation has a assignment for the content
01597         // variable, there's content that's been built that we
01598         // need to pull.
01599         Value value;
01600         PRBool hasassignment =
01601             aOldMatch->mAssignments.GetAssignmentFor(mContentVar, &value);
01602 
01603         NS_ASSERTION(hasassignment, "no content assignment");
01604         if (! hasassignment)
01605             return NS_ERROR_UNEXPECTED;
01606 
01607         nsIContent* content = VALUE_TO_ICONTENT(value);
01608 
01609         PRInt32 membervar = aOldMatch->mRule->GetMemberVariable();
01610 
01611         hasassignment = aOldMatch->mAssignments.GetAssignmentFor(membervar, &value);
01612         NS_ASSERTION(hasassignment, "no member assignment");
01613         if (! hasassignment)
01614             return NS_ERROR_UNEXPECTED;
01615 
01616         nsIRDFResource* member = VALUE_TO_IRDFRESOURCE(value);
01617 
01618         RemoveMember(content, member, PR_TRUE);
01619 
01620         if (!aNewMatch) {
01621             // If there's no new match, then go ahead an update the
01622             // container attributes now.
01623             SetContainerAttrs(content, aOldMatch);
01624         }
01625     }
01626 
01627     if (aNewMatch) {
01628         // Get the content node to which we were bound
01629         Value value;
01630         PRBool hasassignment =
01631             aNewMatch->mAssignments.GetAssignmentFor(mContentVar, &value);
01632 
01633         NS_ASSERTION(hasassignment, "no content assignment");
01634         if (! hasassignment)
01635             return NS_ERROR_UNEXPECTED;
01636 
01637         nsIContent* content = VALUE_TO_ICONTENT(value);
01638 
01639         // Update the 'empty' attribute. Do this *first*, because
01640         // we may decide to nuke aNewMatch in a minute...
01641         SetContainerAttrs(content, aNewMatch);
01642 
01643         // See if we've built the container contents for "content"
01644         // yet. If not, we don't need to build any content. This
01645         // happens, for example, if we recieve an assertion on a
01646         // closed folder in a tree widget or on a menu that hasn't
01647         // yet been dropped.
01648         PRBool contentsGenerated = PR_TRUE;
01649         nsXULElement *xulcontent = nsXULElement::FromContent(content);
01650         if (xulcontent)
01651             contentsGenerated = xulcontent->GetLazyState(nsXULElement::eContainerContentsBuilt);
01652 
01653         if (contentsGenerated) {
01654             nsCOMPtr<nsIContent> tmpl;
01655             aNewMatch->mRule->GetContent(getter_AddRefs(tmpl));
01656 
01657             BuildContentFromTemplate(tmpl, content, content, PR_TRUE,
01658                                      aMember, PR_TRUE, aNewMatch,
01659                                      nsnull, nsnull);
01660         }
01661     }
01662 
01663     return NS_OK;
01664 }
01665 
01666 
01667 nsresult
01668 nsXULContentBuilder::SynchronizeMatch(nsTemplateMatch* match, const VariableSet& modified)
01669 {
01670     const nsTemplateRule* rule = match->mRule;
01671 
01672     Value memberValue;
01673     match->mAssignments.GetAssignmentFor(rule->GetMemberVariable(), &memberValue);
01674 
01675     nsIRDFResource* resource = VALUE_TO_IRDFRESOURCE(memberValue);
01676     NS_ASSERTION(resource != nsnull, "no content");
01677     if (! resource)
01678         return NS_ERROR_FAILURE;
01679 
01680 #ifdef PR_LOGGING
01681     if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
01682         const char* uri;
01683         resource->GetValueConst(&uri);
01684 
01685         PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
01686                ("xultemplate[%p] synchronize-all [%s] begin", this, uri));
01687     }
01688 #endif
01689 
01690     // Now that we've got the resource of the member variable, we
01691     // should be able to update its kids appropriately
01692     nsSupportsArray elements;
01693     GetElementsForResource(resource, &elements);
01694 
01695     PRUint32 cnt = 0;
01696     elements.Count(&cnt);
01697 
01698 #ifdef PR_LOGGING
01699     if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG) && cnt == 0) {
01700         const char* uri;
01701         resource->GetValueConst(&uri);
01702 
01703         PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
01704                ("xultemplate[%p] synchronize-all [%s] is not in element map", this, uri));
01705     }
01706 #endif
01707 
01708     for (PRInt32 i = PRInt32(cnt) - 1; i >= 0; --i) {
01709         nsCOMPtr<nsIContent> element = do_QueryElementAt(&elements, i);
01710         if (! IsElementInBuilder(element, this))
01711             continue;
01712 
01713         nsCOMPtr<nsIContent> templateNode;
01714         mTemplateMap.GetTemplateFor(element, getter_AddRefs(templateNode));
01715 
01716         NS_ASSERTION(templateNode, "couldn't find template node for element");
01717         if (! templateNode)
01718             continue;
01719 
01720         // this node was created by a XUL template, so update it accordingly
01721         SynchronizeUsingTemplate(templateNode, element, *match, modified);
01722     }
01723         
01724 #ifdef PR_LOGGING
01725     if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) {
01726         const char* uri;
01727         resource->GetValueConst(&uri);
01728 
01729         PR_LOG(gXULTemplateLog, PR_LOG_DEBUG,
01730                ("xultemplate[%p] synchronize-all [%s] end", this, uri));
01731     }
01732 #endif
01733 
01734     return NS_OK;
01735 }
01736 
01737 //----------------------------------------------------------------------
01738 //
01739 // Implementation methods
01740 //
01741 
01742 nsresult
01743 nsXULContentBuilder::OpenContainer(nsIContent* aElement)
01744 {
01745     // See if we're responsible for this element
01746     if (! IsElementInBuilder(aElement, this))
01747         return NS_OK;
01748 
01749     nsCOMPtr<nsIRDFResource> resource;
01750     nsXULContentUtils::GetElementRefResource(aElement, getter_AddRefs(resource));
01751 
01752     // If it has no resource, there's nothing that we need to be
01753     // concerned about here.
01754     if (! resource)
01755         return NS_OK;
01756 
01757     // The element has a resource; that means that it corresponds
01758     // to something in the graph, so we need to go to the graph to
01759     // create its contents.
01760     //
01761     // Create the container's contents "quietly" (i.e., |aNotify ==
01762     // PR_FALSE|), and then use the |container| and |newIndex| to
01763     // notify layout where content got created.
01764     nsCOMPtr<nsIContent> container;
01765     PRInt32 newIndex;
01766     CreateContainerContents(aElement, resource, PR_FALSE, getter_AddRefs(container), &newIndex);
01767 
01768     if (container && IsLazyWidgetItem(aElement)) {
01769         // The tree widget is special, and has to be spanked every
01770         // time we add content to a container.
01771         nsCOMPtr<nsIDocument> doc = mRoot->GetDocument();
01772         NS_ASSERTION(doc, "root element has no document");
01773         if (! doc)
01774             return NS_ERROR_UNEXPECTED;
01775 
01776         mozAutoDocUpdate upd(container->GetCurrentDoc(), UPDATE_CONTENT_MODEL,
01777                              PR_TRUE);        
01778         doc->ContentAppended(container, newIndex);
01779     }
01780 
01781     return NS_OK;
01782 }
01783 
01784 nsresult
01785 nsXULContentBuilder::CloseContainer(nsIContent* aElement)
01786 {
01787 #if 0 // Um, what was this really supposed to do?
01788     // See if we're responsible for this element
01789     if (! IsElementInBuilder(aElement, this))
01790         return NS_OK;
01791 #endif
01792 
01793     return NS_OK;
01794 }
01795 
01796 nsresult
01797 nsXULContentBuilder::InitializeRuleNetwork()
01798 {
01799     nsresult rv;
01800     rv = nsXULTemplateBuilder::InitializeRuleNetwork();
01801     if (NS_FAILED(rv)) return rv;
01802 
01803     mContentVar = mRules.CreateAnonymousVariable();
01804     return NS_OK;
01805 }
01806 
01807 nsresult
01808 nsXULContentBuilder::InitializeRuleNetworkForSimpleRules(InnerNode** aChildNode)
01809 {
01810     // For simple rules, the rule network will start off looking
01811     // something like this:
01812     //
01813     //   (root)-->(content ^id ?a)-->(?a ^member ?b)
01814     //
01815     nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(mRoot->GetDocument());
01816     NS_ASSERTION(xuldoc, "expected a XUL Document");
01817     if (! xuldoc)
01818         return NS_ERROR_UNEXPECTED;
01819 
01820     nsContentTestNode* idnode =
01821         new nsContentTestNode(mRules.GetRoot(),
01822                               mConflictSet,
01823                               xuldoc,
01824                               this,
01825                               mContentVar,
01826                               mContainerVar,
01827                               nsnull);
01828 
01829     if (! idnode)
01830         return NS_ERROR_OUT_OF_MEMORY;
01831 
01832     mRules.GetRoot()->AddChild(idnode);
01833     mRules.AddNode(idnode);
01834 
01835     // Create (?container ^member ?member)
01836     nsRDFConMemberTestNode* membernode =
01837         new nsRDFConMemberTestNode(idnode,
01838                                    mConflictSet,
01839                                    mDB,
01840                                    mContainmentProperties,
01841                                    mContainerVar,
01842                                    mMemberVar);
01843 
01844     if (! membernode)
01845         return NS_ERROR_OUT_OF_MEMORY;
01846 
01847     idnode->AddChild(membernode);
01848     mRules.AddNode(membernode);
01849 
01850     mRDFTests.Add(membernode);
01851 
01852     *aChildNode = membernode;
01853     return NS_OK;
01854 }
01855 
01856 nsresult
01857 nsXULContentBuilder::RebuildAll()
01858 {
01859     NS_PRECONDITION(mRoot != nsnull, "not initialized");
01860     if (! mRoot)
01861         return NS_ERROR_NOT_INITIALIZED;
01862 
01863     nsCOMPtr<nsIDocument> doc = mRoot->GetDocument();
01864 
01865     // Bail out early if we are being torn down.
01866     if (!doc)
01867         return NS_OK;
01868 
01869     // See if it's a XUL element whose contents have never even
01870     // been generated. If so, short-circuit and bail; there's nothing
01871     // for us to "rebuild" yet. They'll get built correctly the next
01872     // time somebody asks for them. 
01873     nsXULElement *xulcontent = nsXULElement::FromContent(mRoot);
01874 
01875     if (xulcontent &&
01876         !xulcontent->GetLazyState(nsXULElement::eContainerContentsBuilt))
01877         return NS_OK;
01878 
01879     // If we get here, then we've tried to generate content for this
01880     // element. Remove it.
01881     nsresult rv = RemoveGeneratedContent(mRoot);
01882     if (NS_FAILED(rv)) return rv;
01883 
01884     // Nuke the content support map and conflict set completely.
01885     mContentSupportMap.Clear();
01886     mTemplateMap.Clear();
01887     mConflictSet.Clear();
01888 
01889     rv = CompileRules();
01890     if (NS_FAILED(rv)) return rv;
01891 
01892     // Forces the XUL element to remember that it needs to
01893     // re-generate its children next time around.
01894     if (xulcontent) {
01895         xulcontent->SetLazyState(nsXULElement::eChildrenMustBeRebuilt);
01896         xulcontent->ClearLazyState(nsXULElement::eTemplateContentsBuilt);
01897         xulcontent->ClearLazyState(nsXULElement::eContainerContentsBuilt);
01898     }
01899 
01900     // Now, regenerate both the template- and container-generated
01901     // contents for the current element...
01902     nsCOMPtr<nsIContent> container;
01903     PRInt32 newIndex;
01904     CreateTemplateAndContainerContents(mRoot, getter_AddRefs(container), &newIndex);
01905 
01906     if (container) {
01907         nsCOMPtr<nsIDocument> doc = mRoot->GetDocument();
01908         NS_ASSERTION(doc, "root element has no document");
01909         if (! doc)
01910             return NS_ERROR_UNEXPECTED;
01911 
01912         mozAutoDocUpdate upd(container->GetCurrentDoc(), UPDATE_CONTENT_MODEL,
01913                              PR_TRUE);
01914         doc->ContentAppended(container, newIndex);
01915     }
01916 
01917     return NS_OK;
01918 }
01919 
01920 nsresult
01921 nsXULContentBuilder::CompileCondition(nsIAtom* aTag,
01922                                       nsTemplateRule* aRule,
01923                                       nsIContent* aCondition,
01924                                       InnerNode* aParentNode,
01925                                       TestNode** aResult)
01926 {
01927     nsresult rv;
01928 
01929     if (aTag == nsXULAtoms::content) {
01930         rv = CompileContentCondition(aRule, aCondition, aParentNode, aResult);
01931     }
01932     else {
01933         rv = nsXULTemplateBuilder::CompileCondition(aTag, aRule, aCondition, aParentNode, aResult);
01934     }
01935 
01936     return rv;
01937 }
01938 
01939 nsresult
01940 nsXULContentBuilder::CompileContentCondition(nsTemplateRule* aRule,
01941                                              nsIContent* aCondition,
01942                                              InnerNode* aParentNode,
01943                                              TestNode** aResult)
01944 {
01945     // Compile a <content> condition, which currently must be of the form:
01946     //
01947     //  <content uri="?var" tag="?tag" />
01948     //
01949     // XXXwaterson Right now, exactly one <content> condition is
01950     // required per rule. It creates a nsContentTestNode, binding the
01951     // content variable to the global content variable that's used
01952     // during match propagation. The 'uri' attribute must be set.
01953 
01954     // uri
01955     nsAutoString uri;
01956     aCondition->GetAttr(kNameSpaceID_None, nsXULAtoms::uri, uri);
01957 
01958     if (uri[0] != PRUnichar('?')) {
01959         PR_LOG(gXULTemplateLog, PR_LOG_ALWAYS,
01960                ("xultemplate[%p] on <content> test, expected 'uri' attribute to name a variable", this));
01961 
01962         return NS_OK;
01963     }
01964 
01965     PRInt32 urivar = mRules.LookupSymbol(uri.get());
01966     if (! urivar) {
01967         if (mContainerSymbol.IsEmpty()) {
01968             // If the container symbol was not explictly declared on
01969             // the <template> tag, or we haven't seen a previous rule
01970             // whose <content> condition defined it, then we'll
01971             // implictly define it *now*.
01972             mContainerSymbol = uri;
01973             urivar = mContainerVar;
01974         }
01975         else
01976             urivar = mRules.CreateAnonymousVariable();
01977 
01978         mRules.PutSymbol(uri.get(), urivar);
01979     }
01980 
01981     // tag
01982     nsCOMPtr<nsIAtom> tag;
01983 
01984     nsAutoString tagstr;
01985     aCondition->GetAttr(kNameSpaceID_None, nsXULAtoms::tag, tagstr);
01986 
01987     if (!tagstr.IsEmpty()) {
01988         tag = do_GetAtom(tagstr);
01989     }
01990 
01991     nsCOMPtr<nsIXULDocument> xuldoc = do_QueryInterface(mRoot->GetDocument());
01992     NS_ASSERTION(xuldoc, "root element has no document");
01993     if (! xuldoc)
01994         return NS_ERROR_FAILURE;
01995 
01996     // XXXwaterson By binding the content to the global mContentVar,
01997     // we're essentially saying that each rule *must* have exactly one
01998     // <content id="?x"/> condition.
01999     TestNode* testnode = 
02000         new nsContentTestNode(aParentNode,
02001                               mConflictSet,
02002                               xuldoc,
02003                               this,
02004                               mContentVar, // XXX see above
02005                               urivar,
02006                               tag);
02007 
02008     if (! testnode)
02009         return NS_ERROR_OUT_OF_MEMORY;
02010 
02011     *aResult = testnode;
02012     return NS_OK;
02013 }
02014 
02015 PRBool
02016 nsXULContentBuilder::CompileSimpleAttributeCondition(PRInt32 aNameSpaceID,
02017                                                      nsIAtom* aAttribute,
02018                                                      const nsAString& aValue,
02019                                                      InnerNode* aParentNode,
02020                                                      TestNode** aResult)
02021 {
02022     if ((aNameSpaceID == kNameSpaceID_None) && (aAttribute == nsXULAtoms::parent)) {
02023         // The "parent" test.
02024         //
02025         // XXXwaterson this is wrong: we can't add this below the
02026         // the previous node, because it'll cause an unconstrained
02027         // search if we ever came "up" through this path. Need a
02028         // JoinNode in here somewhere.
02029         nsCOMPtr<nsIAtom> tag = do_GetAtom(aValue);
02030 
02031         *aResult = new nsContentTagTestNode(aParentNode, mConflictSet, mContentVar, tag);
02032         if (*aResult)
02033             return PR_TRUE;
02034     }
02035 
02036     return PR_FALSE;
02037 }