/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Communicator client code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Robert Churchill * David Hyatt * Chris Waterson * Pierre Phaneuf * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsContentCID.h" #include "nsIDocument.h" #include "nsIDOMNodeList.h" #include "nsIDOMXULDocument.h" #include "nsINodeInfo.h" #include "nsIPrincipal.h" #include "nsIServiceManager.h" #include "nsITextContent.h" #include "nsIXULDocument.h" #include "nsIXULSortService.h" #include "nsClusterKeySet.h" #include "nsContentSupportMap.h" #include "nsContentTestNode.h" #include "nsContentTagTestNode.h" #include "nsInstantiationNode.h" #include "nsRDFConMemberTestNode.h" #include "nsRDFPropertyTestNode.h" #include "nsRDFSort.h" #include "nsTemplateRule.h" #include "nsTemplateMap.h" #include "nsVoidArray.h" #include "nsXPIDLString.h" #include "nsXULAtoms.h" #include "nsLayoutAtoms.h" #include "nsXULContentUtils.h" #include "nsXULElement.h" #include "nsXULTemplateBuilder.h" #include "nsSupportsArray.h" #include "nsNodeInfoManager.h" #include "nsContentCreatorFunctions.h" #include "nsContentUtils.h" #include "jsapi.h" #include "pldhash.h" #include "rdf.h" //----------------------------------------------------------------------0 static NS_DEFINE_CID(kXULSortServiceCID, NS_XULSORTSERVICE_CID); PRBool IsElementInBuilder(nsIContent *aContent, nsIXULTemplateBuilder *aBuilder) { // Make sure that the element is contained within the heirarchy // that we're supposed to be processing. nsCOMPtr xuldoc = do_QueryInterface(aContent->GetDocument()); if (! xuldoc) return PR_FALSE; nsCOMPtr content = aContent; do { nsCOMPtr builder; xuldoc->GetTemplateBuilderFor(content, getter_AddRefs(builder)); if (builder) { if (builder == aBuilder) return PR_TRUE; // aBuilder is the builder for this element. // We found a builder, but it's not aBuilder. break; } content = content->GetParent(); } while (content); return PR_FALSE; } //---------------------------------------------------------------------- // // Return values for EnsureElementHasGenericChild() // #define NS_RDF_ELEMENT_GOT_CREATED NS_RDF_NO_VALUE #define NS_RDF_ELEMENT_WAS_THERE NS_OK //---------------------------------------------------------------------- // // nsXULContentBuilder // class nsXULContentBuilder : public nsXULTemplateBuilder { public: // nsIXULTemplateBuilder interface NS_IMETHOD CreateContents(nsIContent* aElement); // nsIDocumentObserver interface virtual void AttributeChanged(nsIDocument* aDocument, nsIContent* aContent, PRInt32 aNameSpaceID, nsIAtom* aAttribute, PRInt32 aModType); void DocumentWillBeDestroyed(nsIDocument* aDocument); protected: friend NS_IMETHODIMP NS_NewXULContentBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult); nsXULContentBuilder(); virtual ~nsXULContentBuilder(); nsresult Init(); // Implementation methods nsresult OpenContainer(nsIContent* aElement); nsresult CloseContainer(nsIContent* aElement); PRBool IsIgnoreableAttribute(PRInt32 aNameSpaceID, nsIAtom* aAtom); nsresult BuildContentFromTemplate(nsIContent *aTemplateNode, nsIContent *aResourceNode, nsIContent *aRealNode, PRBool aIsUnique, nsIRDFResource* aChild, PRBool aNotify, nsTemplateMatch* aMatch, nsIContent** aContainer, PRInt32* aNewIndexInContainer); nsresult AddPersistentAttributes(nsIContent* aTemplateNode, nsIRDFResource* aResource, nsIContent* aRealNode); nsresult SynchronizeUsingTemplate(nsIContent *aTemplateNode, nsIContent* aRealNode, nsTemplateMatch& aMatch, const VariableSet& aModifiedVars); PRBool IsDirectlyContainedBy(nsIContent* aChild, nsIContent* aParent); nsresult RemoveMember(nsIContent* aContainerElement, nsIRDFResource* aMember, PRBool aNotify); nsresult CreateTemplateAndContainerContents(nsIContent* aElement, nsIContent** aContainer, PRInt32* aNewIndexInContainer); nsresult CreateContainerContents(nsIContent* aElement, nsIRDFResource* aResource, PRBool aNotify, nsIContent** aContainer, PRInt32* aNewIndexInContainer); nsresult CreateTemplateContents(nsIContent* aElement, nsIContent* aTemplateElement, nsIContent** aContainer, PRInt32* aNewIndexInContainer); nsresult EnsureElementHasGenericChild(nsIContent* aParent, PRInt32 aNameSpaceID, nsIAtom* aTag, PRBool aNotify, nsIContent** aResult); PRBool IsOpen(nsIContent* aElement); nsresult RemoveGeneratedContent(nsIContent* aElement); PRBool IsLazyWidgetItem(nsIContent* aElement); nsresult GetElementsForResource(nsIRDFResource* aResource, nsISupportsArray* aElements); nsresult CreateElement(PRInt32 aNameSpaceID, nsIAtom* aTag, nsIContent** aResult); nsresult SetContainerAttrs(nsIContent *aElement, const nsTemplateMatch* aMatch); virtual nsresult InitializeRuleNetwork(); virtual nsresult InitializeRuleNetworkForSimpleRules(InnerNode** aChildNode); virtual nsresult RebuildAll(); virtual nsresult CompileCondition(nsIAtom* aTag, nsTemplateRule* aRule, nsIContent* aCondition, InnerNode* aParentNode, TestNode** aResult); nsresult CompileContentCondition(nsTemplateRule* aRule, nsIContent* aCondition, InnerNode* aParentNode, TestNode** aResult); /** * Override default implementation to provide for ``parent'' test */ virtual PRBool CompileSimpleAttributeCondition(PRInt32 aNameSpaceID, nsIAtom* aAttribute, const nsAString& aValue, InnerNode* aParentNode, TestNode** aResult); /** * Maintains a mapping between elements in the DOM and the matches * in the conflict set that they support. */ nsContentSupportMap mContentSupportMap; /** * The variable that represents the ``resource element'' in the * rules for this builder. */ PRInt32 mContentVar; /** * Maintains a mapping from an element in the DOM to the template * element that it was created from. */ nsTemplateMap mTemplateMap; /** * Information about the currently active sort */ nsRDFSortState sortState; virtual nsresult ReplaceMatch(nsIRDFResource* aMember, const nsTemplateMatch* aOldMatch, nsTemplateMatch* aNewMatch); virtual nsresult SynchronizeMatch(nsTemplateMatch* aMatch, const VariableSet& aModifiedVars); // pseudo-constants static PRInt32 gRefCnt; static nsIXULSortService* gXULSortService; }; PRInt32 nsXULContentBuilder::gRefCnt; nsIXULSortService* nsXULContentBuilder::gXULSortService; NS_IMETHODIMP NS_NewXULContentBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult) { NS_PRECONDITION(aOuter == nsnull, "no aggregation"); if (aOuter) return NS_ERROR_NO_AGGREGATION; nsresult rv; nsXULContentBuilder* result = new nsXULContentBuilder(); if (! result) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(result); // stabilize rv = result->Init(); if (NS_SUCCEEDED(rv)) rv = result->QueryInterface(aIID, aResult); NS_RELEASE(result); return rv; } nsXULContentBuilder::nsXULContentBuilder() { } nsXULContentBuilder::~nsXULContentBuilder() { if (--gRefCnt == 0) { NS_IF_RELEASE(gXULSortService); } } nsresult nsXULContentBuilder::Init() { if (gRefCnt++ == 0) { nsresult rv = CallGetService(kXULSortServiceCID, &gXULSortService); if (NS_FAILED(rv)) return rv; } return nsXULTemplateBuilder::Init(); } PRBool nsXULContentBuilder::IsIgnoreableAttribute(PRInt32 aNameSpaceID, nsIAtom* aAttribute) { // XXX Note that we patently ignore namespaces. This is because // HTML elements lie and tell us that their attributes are // _always_ in the HTML namespace. Urgh. // never copy the ID attribute if (aAttribute == nsXULAtoms::id) { return PR_TRUE; } // never copy {}:uri attribute else if (aAttribute == nsXULAtoms::uri) { return PR_TRUE; } else { return PR_FALSE; } } nsresult nsXULContentBuilder::BuildContentFromTemplate(nsIContent *aTemplateNode, nsIContent *aResourceNode, nsIContent *aRealNode, PRBool aIsUnique, nsIRDFResource* aChild, PRBool aNotify, nsTemplateMatch* aMatch, nsIContent** aContainer, PRInt32* aNewIndexInContainer) { // This is the mother lode. Here is where we grovel through an // element in the template, copying children from the template // into the "real" content tree, performing substitution as we go // by looking stuff up in the RDF graph. // // |aTemplateNode| is the element in the "template tree", whose // children we will duplicate and move into the "real" content // tree. // // |aResourceNode| is the element in the "real" content tree that // has the "id" attribute set to an RDF resource's URI. This is // not directly used here, but rather passed down to the XUL // sort service to perform container-level sort. // // |aRealNode| is the element in the "real" content tree to which // the new elements will be copied. // // |aIsUnique| is set to "true" so long as content has been // "unique" (or "above" the resource element) so far in the // template. // // |aChild| is the RDF resource at the end of a property link for // which we are building content. // // |aNotify| is set to "true" if content should be constructed // "noisily"; that is, whether the document observers should be // notified when new content is added to the content model. // // |aContainer| is an out parameter that will be set to the first // container element in the "real" content tree to which content // was appended. // // |aNewIndexInContainer| is an out parameter that will be set to // the index in aContainer at which new content is first // constructed. // // If |aNotify| is "false", then |aContainer| and // |aNewIndexInContainer| are used to determine where in the // content model new content is constructed. This allows a single // notification to be propagated to document observers. // nsresult rv; #ifdef PR_LOGGING // Dump out the template node's tag, the template ID, and the RDF // resource that is being used as the index into the graph. if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { nsXPIDLCString resourceCStr; rv = aChild->GetValue(getter_Copies(resourceCStr)); if (NS_FAILED(rv)) return rv; const char *tagstr; aTemplateNode->Tag()->GetUTF8String(&tagstr); nsAutoString templatestr; aTemplateNode->GetAttr(kNameSpaceID_None, nsXULAtoms::id, templatestr); nsCAutoString templatestrC; templatestrC.AssignWithConversion(templatestr); PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, ("xultemplate[%p] build-content-from-template %s (template='%s') [%s]", this, tagstr, templatestrC.get(), resourceCStr.get())); } #endif // Iterate through all of the template children, constructing // "real" content model nodes for each "template" child. PRUint32 count = aTemplateNode->GetChildCount(); for (PRUint32 kid = 0; kid < count; kid++) { nsIContent *tmplKid = aTemplateNode->GetChildAt(kid); PRInt32 nameSpaceID = tmplKid->GetNameSpaceID(); // Check whether this element is the "resource" element. The // "resource" element is the element that is cookie-cutter // copied once for each different RDF resource specified by // |aChild|. // // Nodes that appear -above- the resource element // (that is, are ancestors of the resource element in the // content model) are unique across all values of |aChild|, // and are created only once. // // Nodes that appear -below- the resource element (that is, // are descnendants of the resource element in the conte // model), are cookie-cutter copied for each distinct value of // |aChild|. // // For example, in a template: // // // // // // The element [2] is the "resource element". This // element, and all of its descendants ([3], [4], and [5]) // will be duplicated for each different |aChild| // resource. It's ancestor [1] is unique, and // will only be created -once-, no matter how many s // are created below it. // // Note that |isResourceElement| and |isUnique| are mutually // exclusive. PRBool isResourceElement = PR_FALSE; PRBool isUnique = aIsUnique; { // We identify the resource element by presence of a // "uri='rdf:*'" attribute. (We also support the older // "uri='...'" syntax.) nsAutoString uri; tmplKid->GetAttr(kNameSpaceID_None, nsXULAtoms::uri, uri); if ( !uri.IsEmpty() ) { if (aMatch->mRule && uri[0] == PRUnichar('?')) { isResourceElement = PR_TRUE; isUnique = PR_FALSE; // XXXwaterson hack! refactor me please Value member; aMatch->mAssignments.GetAssignmentFor(aMatch->mRule->GetMemberVariable(), &member); aChild = VALUE_TO_IRDFRESOURCE(member); } else if (uri.EqualsLiteral("...") || uri.EqualsLiteral("rdf:*")) { // If we -are- the resource element, then we are no // matter unique. isResourceElement = PR_TRUE; isUnique = PR_FALSE; } } } nsIAtom *tag = tmplKid->Tag(); #ifdef PR_LOGGING if (PR_LOG_TEST(gXULTemplateLog, PR_LOG_DEBUG)) { const char *tagname; tag->GetUTF8String(&tagname); PR_LOG(gXULTemplateLog, PR_LOG_DEBUG, ("xultemplate[%p] building %s %s %s", this, tagname, (isResourceElement ? "[resource]" : ""), (isUnique ? "[unique]" : ""))); } #endif // Set to PR_TRUE if the child we're trying to create now // already existed in the content model. PRBool realKidAlreadyExisted = PR_FALSE; nsCOMPtr realKid; if (isUnique) { // The content is "unique"; that is, we haven't descended // far enough into the tempalte to hit the "resource" // element yet. |EnsureElementHasGenericChild()| will // conditionally create the element iff it isn't there // already. rv = EnsureElementHasGenericChild(aRealNode, nameSpaceID, tag, aNotify, getter_AddRefs(realKid)); if (NS_FAILED(rv)) return rv; if (rv == NS_RDF_ELEMENT_WAS_THERE) { realKidAlreadyExisted = PR_TRUE; } else { // Mark the element's contents as being generated so // that any re-entrant calls don't trigger an infinite // recursion. nsXULElement *xulcontent = nsXULElement::FromContent(realKid); if (xulcontent) { xulcontent->SetLazyState(nsXULElement::eTemplateContentsBuilt); } // Potentially remember the index of this element as the first // element that we've generated. Note that we remember // this -before- we recurse! if (aContainer && !*aContainer) { *aContainer = aRealNode; NS_ADDREF(*aContainer); PRUint32 indx = aRealNode->GetChildCount(); // Since EnsureElementHasGenericChild() added us, make // sure to subtract one for our real index. *aNewIndexInContainer = indx - 1; } } // Recurse until we get to the resource element. Since // -we're- unique, assume that our child will be // unique. The check for the "resource" element at the top // of the function will trip this to |false| as soon as we // encounter it. rv = BuildContentFromTemplate(tmplKid, aResourceNode, realKid, PR_TRUE, aChild, aNotify, aMatch, aContainer, aNewIndexInContainer); if (NS_FAILED(rv)) return rv; } else if (isResourceElement) { // It's the "resource" element. Create a new element using // the namespace ID and tag from the template element. rv = CreateElement(nameSpaceID, tag, getter_AddRefs(realKid)); if (NS_FAILED(rv)) return rv; // Add the resource element to the content support map so // we can the match based on content node later. mContentSupportMap.Put(realKid, aMatch); // Assign the element an 'id' attribute using the URI of // the |aChild| resource. const char *uri; rv = aChild->GetValueConst(&uri); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get resource URI"); if (NS_FAILED(rv)) return rv; // XXX because gcc-2.7.2.3 is too dumb to keep a // compiler-generated temporary around. NS_ConvertUTF8toUCS2 x(uri); const nsAString& id = x; rv = realKid->SetAttr(kNameSpaceID_None, nsXULAtoms::id, id, PR_FALSE); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to set id attribute"); if (NS_FAILED(rv)) return rv; if (! aNotify) { // XUL document will watch us, and take care of making // sure that we get added to or removed from the // element map if aNotify is true. If not, we gotta do // it ourselves. Yay. nsCOMPtr xuldoc = do_QueryInterface(mRoot->GetDocument()); if (xuldoc) xuldoc->AddElementForID(id, realKid); } // Set up the element's 'container' and 'empty' // attributes. PRBool iscontainer, isempty; rv = CheckContainer(aChild, &iscontainer, &isempty); if (NS_FAILED(rv)) return rv; if (iscontainer) { realKid->SetAttr(kNameSpaceID_None, nsXULAtoms::container, NS_LITERAL_STRING("true"), PR_FALSE); if (! (mFlags & eDontTestEmpty)) { NS_NAMED_LITERAL_STRING(true_, "true"); NS_NAMED_LITERAL_STRING(false_, "false"); realKid->SetAttr(kNameSpaceID_None, nsXULAtoms::empty, isempty ? true_ : false_, PR_FALSE); } } } else if (tag == nsXULAtoms::textnode && nameSpaceID == kNameSpaceID_XUL) { // is replaced by text of the // actual value of the 'rdf:resource' attribute for the // given node. PRUnichar attrbuf[128]; nsFixedString attrValue(attrbuf, NS_ARRAY_LENGTH(attrbuf), 0); rv = tmplKid->GetAttr(kNameSpaceID_None, nsXULAtoms::value, attrValue); if (NS_FAILED(rv)) return rv; if ((rv == NS_CONTENT_ATTR_HAS_VALUE) && (!attrValue.IsEmpty())) { nsAutoString value; rv = SubstituteText(*aMatch, attrValue, value); if (NS_FAILED(rv)) return rv; nsCOMPtr content; rv = NS_NewTextNode(getter_AddRefs(content), mRoot->GetNodeInfo()->NodeInfoManager()); if (NS_FAILED(rv)) return rv; content->SetText(value, PR_FALSE); rv = aRealNode->AppendChildTo(content, aNotify); if (NS_FAILED(rv)) return rv; // XXX Don't bother remembering text nodes as the // first element we've generated? } } else if (tmplKid->IsContentOfType(nsIContent::eTEXT)) { nsCOMPtr tmplTextNode = do_QueryInterface(tmplKid); if (!tmplTextNode) { NS_ERROR("textnode not implementing nsIDOMNode??"); return NS_ERROR_FAILURE; } nsCOMPtr clonedNode; tmplTextNode->CloneNode(PR_FALSE, getter_AddRefs(clonedNode)); nsCOMPtr clonedContent = do_QueryInterface(clonedNode); if (!clonedContent) { NS_ERROR("failed to clone textnode"); return NS_ERROR_FAILURE; } rv = aRealNode->AppendChildTo(clonedContent, aNotify); if (NS_FAILED(rv)) return rv; } else { // It's just a generic element. Create it! rv = CreateElement(nameSpaceID, tag, getter_AddRefs(realKid)); if (NS_FAILED(rv)) return rv; } if (realKid && !realKidAlreadyExisted) { // Potentially remember the index of this element as the // first element that we've generated. if (aContainer && !*aContainer) { *aContainer = aRealNode; NS_ADDREF(*aContainer); PRUint32 indx = aRealNode->GetChildCount(); // Since we haven't inserted any content yet, our new // index in the container will be the current count of // elements in the container. *aNewIndexInContainer = indx; } // Remember the template kid from which we created the // real kid. This allows us to sync back up with the // template to incrementally build content. mTemplateMap.Put(realKid, tmplKid); // Copy all attributes from the template to the new // element. PRUint32 numAttribs = tmplKid->GetAttrCount(); for (PRUint32 attr = 0; attr < numAttribs; attr++) { PRInt32 attribNameSpaceID; nsCOMPtr attribName, prefix; rv = tmplKid->GetAttrNameAt(attr, &attribNameSpaceID, getter_AddRefs(attribName), getter_AddRefs(prefix)); if (NS_FAILED(rv)) return rv; if (! IsIgnoreableAttribute(attribNameSpaceID, attribName)) { // Create a buffer here, because there's a good // chance that an attribute in the template is // going to be an RDF URI, which is usually // longish. PRUnichar attrbuf[128]; nsFixedString attribValue(attrbuf, NS_ARRAY_LENGTH(attrbuf), 0); rv = tmplKid->GetAttr(attribNameSpaceID, attribName, attribValue); if (NS_FAILED(rv)) return rv; if (rv == NS_CONTENT_ATTR_HAS_VALUE) { nsAutoString value; rv = SubstituteText(*aMatch, attribValue, value); if (NS_FAILED(rv)) return rv; rv = realKid->SetAttr(attribNameSpaceID, attribName, value, PR_FALSE); if (NS_FAILED(rv)) return rv; } } } // Add any persistent attributes if (isResourceElement) { rv = AddPersistentAttributes(tmplKid, aChild, realKid); if (NS_FAILED(rv)) return rv; } nsXULElement *xulcontent = nsXULElement::FromContent(realKid); if (xulcontent) { PRUint32 count2 = tmplKid->GetChildCount(); if (count2 == 0 && !isResourceElement) { // If we're at a leaf node, then we'll eagerly // mark the content as having its template & // container contents built. This avoids a useless // trip back to the template builder only to find // that we've got no work to do! xulcontent->SetLazyState(nsXULElement::eTemplateContentsBuilt); xulcontent->SetLazyState(nsXULElement::eContainerContentsBuilt); } else { // Just mark the XUL element as requiring more work to // be done. We'll get around to it when somebody asks // for it. xulcontent->SetLazyState(nsXULElement::eChildrenMustBeRebuilt); } } else { // Otherwise, it doesn't support lazy instantiation, // and we have to recurse "by hand". Note that we // _don't_ need to notify: we'll add the entire // subtree in a single whack. // // Note that we don't bother passing aContainer and // aNewIndexInContainer down: since we're HTML, we // -know- that we -must- have just been created. rv = BuildContentFromTemplate(tmplKid, aResourceNode, realKid, isUnique, aChild, PR_FALSE, aMatch, nsnull /* don't care */, nsnull /* don't care */); if (NS_FAILED(rv)) return rv; if (isResourceElement) { rv = CreateContainerContents(realKid, aChild, PR_FALSE, nsnull /* don't care */, nsnull /* don't care */); if (NS_FAILED(rv)) return rv; } } // We'll _already_ have added the unique elements; but if // it's -not- unique, then use the XUL sort service now to // append the element to the content model. if (! isUnique) { rv = NS_ERROR_UNEXPECTED; if (gXULSortService && isResourceElement) { rv = gXULSortService->InsertContainerNode(mCompDB, &sortState, mRoot, aResourceNode, aRealNode, realKid, aNotify); } if (NS_FAILED(rv)) { rv = aRealNode->AppendChildTo(realKid, aNotify); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to insert element"); } } } } return NS_OK; } nsresult nsXULContentBuilder::AddPersistentAttributes(nsIContent* aTemplateNode, nsIRDFResource* aResource, nsIContent* aRealNode) { nsresult rv; nsAutoString persist; rv = aTemplateNode->GetAttr(kNameSpaceID_None, nsXULAtoms::persist, persist); if (NS_FAILED(rv)) return rv; if (rv != NS_CONTENT_ATTR_HAS_VALUE) return NS_OK; nsAutoString attribute; while (!persist.IsEmpty()) { attribute.Truncate(); PRInt32 offset = persist.FindCharInSet(" ,"); if (offset > 0) { persist.Left(attribute, offset); persist.Cut(0, offset + 1); } else { attribute = persist; persist.Truncate(); } attribute.Trim(" "); if (attribute.IsEmpty()) break; nsCOMPtr tag; PRInt32 nameSpaceID; nsCOMPtr ni = aTemplateNode->GetExistingAttrNameFromQName(attribute); if (ni) { tag = ni->NameAtom(); nameSpaceID = ni->NamespaceID(); } else { tag = do_GetAtom(attribute); NS_ENSURE_TRUE(tag, NS_ERROR_OUT_OF_MEMORY); nameSpaceID = kNameSpaceID_None; } nsCOMPtr property; rv = nsXULContentUtils::GetResource(nameSpaceID, tag, getter_AddRefs(property)); if (NS_FAILED(rv)) return rv; nsCOMPtr target; rv = mDB->GetTarget(aResource, property, PR_TRUE, getter_AddRefs(target)); if (NS_FAILED(rv)) return rv; if (! target) continue; nsCOMPtr value = do_QueryInterface(target); NS_ASSERTION(value != nsnull, "unable to stomach that sort of node"); if (! value) continue; const PRUnichar* valueStr; rv = value->GetValueConst(&valueStr); if (NS_FAILED(rv)) return rv; rv = aRealNode->SetAttr(nameSpaceID, tag, nsDependentString(valueStr), PR_FALSE); if (NS_FAILED(rv)) return rv; } return NS_OK; } nsresult nsXULContentBuilder::SynchronizeUsingTemplate(nsIContent* aTemplateNode, nsIContent* aRealElement, nsTemplateMatch& aMatch, const VariableSet& aModifiedVars) { nsresult rv; // check all attributes on the template node; if they reference a resource, // update the equivalent attribute on the content node PRUint32 numAttribs = aTemplateNode->GetAttrCount(); for (PRUint32 loop = 0; loop < numAttribs; ++loop) { PRInt32 attribNameSpaceID; nsCOMPtr attribName, prefix; rv = aTemplateNode->GetAttrNameAt(loop, &attribNameSpaceID, getter_AddRefs(attribName), getter_AddRefs(prefix)); if (NS_FAILED(rv)) break; // See if it's one of the attributes that we unilaterally // ignore. If so, on to the next one... if (IsIgnoreableAttribute(attribNameSpaceID, attribName)) continue; nsAutoString attribValue; rv = aTemplateNode->GetAttr(attribNameSpaceID, attribName, attribValue); if (! IsAttrImpactedByVars(aMatch, attribValue, aModifiedVars)) continue; nsAutoString newvalue; SubstituteText(aMatch, attribValue, newvalue); if (!newvalue.IsEmpty()) { aRealElement->SetAttr(attribNameSpaceID, attribName, newvalue, PR_TRUE); } else { aRealElement->UnsetAttr(attribNameSpaceID, attribName, PR_TRUE); } } // See if we've generated kids for this node yet. If we have, then // recursively sync up template kids with content kids PRBool contentsGenerated = PR_TRUE; nsXULElement *xulcontent = nsXULElement::FromContent(aRealElement); if (xulcontent) { contentsGenerated = xulcontent->GetLazyState(nsXULElement::eTemplateContentsBuilt); } else { // HTML content will _always_ have been generated up-front } if (contentsGenerated) { PRUint32 count = aTemplateNode->GetChildCount(); for (PRUint32 loop = 0; loop < count; ++loop) { nsIContent *tmplKid = aTemplateNode->GetChildAt(loop); if (! tmplKid) break; nsIContent *realKid = aRealElement->GetChildAt(loop); if (! realKid) break; rv = SynchronizeUsingTemplate(tmplKid, realKid, aMatch, aModifiedVars); if (NS_FAILED(rv)) return rv; } } return NS_OK; } PRBool nsXULContentBuilder::IsDirectlyContainedBy(nsIContent* aChild, nsIContent* aParent) { // This routine uses the