From c6f830f3c19e5596ef26cf9343ecaa817576a293 Mon Sep 17 00:00:00 2001 From: "waterson%netscape.com" Date: Tue, 31 Aug 1999 06:40:41 +0000 Subject: [PATCH] Bug 12811. Fix frame system notifications s.t. as _few_ as possible get forwarded through to layout. Temporarily remove timer hacks from tree widget. git-svn-id: svn://10.0.0.236/trunk@45312 18797224-902f-48f8-a5cc-f745e15eee43 --- .../xul/document/src/nsXULDocument.cpp | 276 +++---- .../templates/src/nsXULTemplateBuilder.cpp | 719 ++++++++++++++---- .../public/nsIRDFContentModelBuilder.h | 18 + mozilla/rdf/content/src/nsRDFContentUtils.cpp | 2 +- .../rdf/content/src/nsRDFGenericBuilder.cpp | 719 ++++++++++++++---- mozilla/rdf/content/src/nsRDFGenericBuilder.h | 274 ------- mozilla/rdf/content/src/nsRDFXULBuilder.cpp | 36 +- mozilla/rdf/content/src/nsXULDocument.cpp | 276 +++---- .../rdf/content/src/nsXULTemplateBuilder.cpp | 719 ++++++++++++++---- 9 files changed, 2046 insertions(+), 993 deletions(-) delete mode 100644 mozilla/rdf/content/src/nsRDFGenericBuilder.h diff --git a/mozilla/content/xul/document/src/nsXULDocument.cpp b/mozilla/content/xul/document/src/nsXULDocument.cpp index 3295fb4839c..204122f41c9 100644 --- a/mozilla/content/xul/document/src/nsXULDocument.cpp +++ b/mozilla/content/xul/document/src/nsXULDocument.cpp @@ -802,7 +802,7 @@ public: nsresult OpenWidgetItem(nsIContent* aElement); nsresult CloseWidgetItem(nsIContent* aElement); - nsresult RemoveAndRebuildGeneratedChildren(nsIContent* aElement); + nsresult RebuildWidgetItem(nsIContent* aElement); nsresult AddElementToMap(nsIContent* aElement, PRBool aDeep); @@ -2101,14 +2101,14 @@ XULDocumentImpl::ContentStatesChanged(nsIContent* aContent1, nsIContent* aConten } NS_IMETHODIMP -XULDocumentImpl::AttributeChanged(nsIContent* aChild, +XULDocumentImpl::AttributeChanged(nsIContent* aElement, nsIAtom* aAttribute, PRInt32 aHint) { nsresult rv; PRInt32 nameSpaceID; - rv = aChild->GetNameSpaceID(nameSpaceID); + rv = aElement->GetNameSpaceID(nameSpaceID); if (NS_FAILED(rv)) return rv; // First see if we need to update our element map. @@ -2120,27 +2120,7 @@ XULDocumentImpl::AttributeChanged(nsIContent* aChild, // That'll have removed _both_ the 'ref' and 'id' entries from // the map. So add 'em back now. - rv = AddElementToMap(aChild, PR_FALSE); - if (NS_FAILED(rv)) return rv; - } - } - - // Handle "special" cases. - if (nameSpaceID == kNameSpaceID_XUL) { - if (aAttribute == kOpenAtom) { - nsAutoString open; - rv = aChild->GetAttribute(kNameSpaceID_None, kOpenAtom, open); - if (NS_FAILED(rv)) return rv; - - if ((rv == NS_CONTENT_ATTR_HAS_VALUE) && (open.Equals("true"))) { - OpenWidgetItem(aChild); - } - else { - CloseWidgetItem(aChild); - } - } - else if (aAttribute == kRefAtom) { - rv = RemoveAndRebuildGeneratedChildren(aChild); + rv = AddElementToMap(aElement, PR_FALSE); if (NS_FAILED(rv)) return rv; } } @@ -2148,11 +2128,34 @@ XULDocumentImpl::AttributeChanged(nsIContent* aChild, // Now notify external observers for (PRInt32 i = 0; i < mObservers.Count(); i++) { nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers[i]; - observer->AttributeChanged(this, aChild, aAttribute, aHint); + observer->AttributeChanged(this, aElement, aAttribute, aHint); if (observer != (nsIDocumentObserver*)mObservers.ElementAt(i)) { i--; } } + + // Handle "special" cases. We do this handling _after_ we've + // notified the observer to ensure that any frames that are + // caching information (e.g., the tree widget and the 'open' + // attribute) will notice things properly. + if (nameSpaceID == kNameSpaceID_XUL) { + if (aAttribute == kOpenAtom) { + nsAutoString open; + rv = aElement->GetAttribute(kNameSpaceID_None, kOpenAtom, open); + if (NS_FAILED(rv)) return rv; + + if ((rv == NS_CONTENT_ATTR_HAS_VALUE) && (open.Equals("true"))) { + OpenWidgetItem(aElement); + } + else { + CloseWidgetItem(aElement); + } + } + else if (aAttribute == kRefAtom) { + RebuildWidgetItem(aElement); + } + } + return NS_OK; } @@ -4483,10 +4486,17 @@ XULDocumentImpl::ReleaseEvent(const nsString& aType) nsresult XULDocumentImpl::OpenWidgetItem(nsIContent* aElement) { + NS_PRECONDITION(aElement != nsnull, "null ptr"); + if (! aElement) + return NS_ERROR_NULL_POINTER; + + if (! mBuilders) + return NS_ERROR_NOT_INITIALIZED; + + nsresult rv; + #ifdef PR_LOGGING if (PR_LOG_TEST(gXULLog, PR_LOG_DEBUG)) { - nsresult rv; - nsCOMPtr tag; rv = aElement->GetTag(*getter_AddRefs(tag)); if (NS_FAILED(rv)) return rv; @@ -4499,12 +4509,39 @@ XULDocumentImpl::OpenWidgetItem(nsIContent* aElement) (const char*) nsCAutoString(tagStr))); } #endif - return CreateContents(aElement); + + PRUint32 cnt = 0; + rv = mBuilders->Count(&cnt); + NS_ASSERTION(NS_SUCCEEDED(rv), "Count failed"); + for (PRUint32 i = 0; i < cnt; ++i) { + // XXX we should QueryInterface() here + nsIRDFContentModelBuilder* builder + = (nsIRDFContentModelBuilder*) mBuilders->ElementAt(i); + + NS_ASSERTION(builder != nsnull, "null ptr"); + if (! builder) + continue; + + rv = builder->OpenContainer(aElement); + NS_ASSERTION(NS_SUCCEEDED(rv), "error opening container"); + // XXX ignore error code? + + NS_RELEASE(builder); + } + + return NS_OK; } nsresult XULDocumentImpl::CloseWidgetItem(nsIContent* aElement) { + NS_PRECONDITION(aElement != nsnull, "null ptr"); + if (! aElement) + return NS_ERROR_NULL_POINTER; + + if (! mBuilders) + return NS_ERROR_NOT_INITIALIZED; + nsresult rv; #ifdef PR_LOGGING @@ -4522,163 +4559,78 @@ XULDocumentImpl::CloseWidgetItem(nsIContent* aElement) } #endif - // Find the tag that contains the children so that we can remove - // all of the children. - // - // XXX We make a bit of a leap here and assume that the same - // template that was used to generate _us_ was used to generate - // our _kids_. I'm sure this'll break when we do toolbars or - // something. - nsAutoString tmplID; - rv = aElement->GetAttribute(kNameSpaceID_None, kTemplateAtom, tmplID); - if (NS_FAILED(rv)) return rv; + PRUint32 cnt = 0; + rv = mBuilders->Count(&cnt); + NS_ASSERTION(NS_SUCCEEDED(rv), "Count failed"); + for (PRUint32 i = 0; i < cnt; ++i) { + // XXX we should QueryInterface() here + nsIRDFContentModelBuilder* builder + = (nsIRDFContentModelBuilder*) mBuilders->ElementAt(i); - if (rv != NS_CONTENT_ATTR_HAS_VALUE) - return NS_OK; + NS_ASSERTION(builder != nsnull, "null ptr"); + if (! builder) + continue; - nsCOMPtr tmplDOMEle; - rv = GetElementById(tmplID, getter_AddRefs(tmplDOMEle)); - if (NS_FAILED(rv)) return rv; + rv = builder->CloseContainer(aElement); + NS_ASSERTION(NS_SUCCEEDED(rv), "error closing container"); + // XXX ignore error code? - nsCOMPtr tmpl = do_QueryInterface(tmplDOMEle); - if (! tmpl) - return NS_ERROR_UNEXPECTED; - - nsCOMPtr tmplParent; - rv = tmpl->GetParent(*getter_AddRefs(tmplParent)); - if (NS_FAILED(rv)) return rv; - - NS_ASSERTION(tmplParent != nsnull, "template node has no parent"); - if (! tmplParent) - return NS_ERROR_UNEXPECTED; - - nsCOMPtr tmplParentTag; - rv = tmplParent->GetTag(*getter_AddRefs(tmplParentTag)); - if (NS_FAILED(rv)) return rv; - - nsCOMPtr childcontainer; - if ((tmplParentTag.get() == kRuleAtom) || (tmplParentTag.get() == kTemplateAtom)) { - childcontainer = dont_QueryInterface(aElement); + NS_RELEASE(builder); } - else { - rv = nsRDFContentUtils::FindChildByTag(aElement, - kNameSpaceID_XUL, - tmplParentTag, - getter_AddRefs(childcontainer)); - - if (NS_FAILED(rv)) return rv; - - if (rv == NS_RDF_NO_VALUE) { - // No tag; must've already been closed - return NS_OK; - } - } - - PRInt32 count; - rv = childcontainer->ChildCount(count); - NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get count of the parent's children"); - if (NS_FAILED(rv)) return rv; - - while (--count >= 0) { - nsCOMPtr child; - rv = childcontainer->ChildAt(count, *getter_AddRefs(child)); - if (NS_FAILED(rv)) return rv; - - rv = childcontainer->RemoveChildAt(count, PR_TRUE); - NS_ASSERTION(NS_SUCCEEDED(rv), "error removing child"); - - do { - // If it's _not_ a XUL element, then we want to blow it and - // all of its kids out of the XUL document's - // resource-to-element map. - nsCOMPtr resource; - rv = nsRDFContentUtils::GetElementResource(child, getter_AddRefs(resource)); - if (NS_FAILED(rv)) break; - - PRBool isXULElement; - rv = mDocumentDataSource->HasAssertion(resource, kRDF_instanceOf, kXUL_element, PR_TRUE, &isXULElement); - if (NS_FAILED(rv)) break; - - if (! isXULElement) - break; - - rv = child->SetDocument(nsnull, PR_TRUE); - if (NS_FAILED(rv)) return rv; - } while (0); - } - - // Clear the container-contents-generated attribute so that the next time we - // come back, we'll regenerate the kids we just killed. - rv = aElement->UnsetAttribute(kNameSpaceID_None, - kContainerContentsGeneratedAtom, - PR_FALSE); - if (NS_FAILED(rv)) return rv; - - // This is a _total_ hack to make sure that any XUL we blow away - // gets rebuilt. - rv = childcontainer->UnsetAttribute(kNameSpaceID_None, - kXULContentsGeneratedAtom, - PR_FALSE); - if (NS_FAILED(rv)) return rv; - - rv = childcontainer->SetAttribute(kNameSpaceID_None, - kLazyContentAtom, - "true", - PR_FALSE); - if (NS_FAILED(rv)) return rv; return NS_OK; } nsresult -XULDocumentImpl::RemoveAndRebuildGeneratedChildren(nsIContent* aElement) +XULDocumentImpl::RebuildWidgetItem(nsIContent* aElement) { + NS_PRECONDITION(aElement != nsnull, "null ptr"); + if (! aElement) + return NS_ERROR_NULL_POINTER; + + if (! mBuilders) + return NS_ERROR_NOT_INITIALIZED; + nsresult rv; - PRInt32 count; - rv = aElement->ChildCount(count); - if (NS_FAILED(rv)) return rv; - - while (--count >= 0) { - nsCOMPtr child; - rv = aElement->ChildAt(count, *getter_AddRefs(child)); +#ifdef PR_LOGGING + if (PR_LOG_TEST(gXULLog, PR_LOG_DEBUG)) { + nsCOMPtr tag; + rv = aElement->GetTag(*getter_AddRefs(tag)); if (NS_FAILED(rv)) return rv; - nsAutoString tmplID; - rv = child->GetAttribute(kNameSpaceID_None, kTemplateAtom, tmplID); - if (NS_FAILED(rv)) return rv; + nsAutoString tagStr; + tag->ToString(tagStr); - if (rv != NS_CONTENT_ATTR_HAS_VALUE) + PR_LOG(gXULLog, PR_LOG_DEBUG, + ("xuldoc close-widget-item %s", + (const char*) nsCAutoString(tagStr))); + } +#endif + + PRUint32 cnt = 0; + rv = mBuilders->Count(&cnt); + NS_ASSERTION(NS_SUCCEEDED(rv), "Count failed"); + for (PRUint32 i = 0; i < cnt; ++i) { + // XXX we should QueryInterface() here + nsIRDFContentModelBuilder* builder + = (nsIRDFContentModelBuilder*) mBuilders->ElementAt(i); + + NS_ASSERTION(builder != nsnull, "null ptr"); + if (! builder) continue; - // It's a generated element. Remove it, and set its document - // to null so that it'll get knocked out of the XUL doc's - // resource-to-element map. - rv = aElement->RemoveChildAt(count, PR_TRUE); - NS_ASSERTION(NS_SUCCEEDED(rv), "error removing child"); + rv = builder->RebuildContainer(aElement); + NS_ASSERTION(NS_SUCCEEDED(rv), "error rebuilding container"); + // XXX ignore error code? - rv = child->SetDocument(nsnull, PR_TRUE); - if (NS_FAILED(rv)) return rv; + NS_RELEASE(builder); } - // Clear the contents-generated attribute so that the next time we - // come back, we'll regenerate the kids we just killed. - rv = aElement->UnsetAttribute(kNameSpaceID_None, - kTemplateContentsGeneratedAtom, - PR_FALSE); - if (NS_FAILED(rv)) return rv; - - rv = aElement->UnsetAttribute(kNameSpaceID_None, - kContainerContentsGeneratedAtom, - PR_FALSE); - if (NS_FAILED(rv)) return rv; - - rv = CreateContents(aElement); - if (NS_FAILED(rv)) return rv; - return NS_OK; } + diff --git a/mozilla/content/xul/templates/src/nsXULTemplateBuilder.cpp b/mozilla/content/xul/templates/src/nsXULTemplateBuilder.cpp index 0b22cc91838..cffb60dd5f7 100644 --- a/mozilla/content/xul/templates/src/nsXULTemplateBuilder.cpp +++ b/mozilla/content/xul/templates/src/nsXULTemplateBuilder.cpp @@ -41,38 +41,36 @@ #include "nsIDOMElement.h" #include "nsIDOMNode.h" #include "nsIDOMXULDocument.h" +#include "nsIDOMXULElement.h" #include "nsIDocument.h" +#include "nsIHTMLContent.h" +#include "nsIHTMLElementFactory.h" #include "nsINameSpaceManager.h" -#include "nsIRDFContentModelBuilder.h" #include "nsIRDFCompositeDataSource.h" +#include "nsIRDFContainerUtils.h" +#include "nsIRDFContentModelBuilder.h" #include "nsIRDFDocument.h" #include "nsIRDFNode.h" #include "nsIRDFObserver.h" +#include "nsIRDFRemoteDataSource.h" #include "nsIRDFService.h" #include "nsIServiceManager.h" -#include "nsINameSpaceManager.h" -#include "nsIServiceManager.h" #include "nsISupportsArray.h" #include "nsITextContent.h" +#include "nsITimer.h" #include "nsIURL.h" +#include "nsIXULSortService.h" #include "nsLayoutCID.h" #include "nsRDFCID.h" #include "nsRDFContentUtils.h" #include "nsString.h" +#include "nsVoidArray.h" #include "nsXPIDLString.h" +#include "prlog.h" #include "rdf.h" #include "rdfutil.h" -#include "nsITimer.h" -#include "nsIDOMXULElement.h" -#include "nsVoidArray.h" -#include "nsIXULSortService.h" -#include "nsIHTMLElementFactory.h" -#include "nsIHTMLContent.h" -#include "nsRDFGenericBuilder.h" -#include "prlog.h" -#include "nsIRDFRemoteDataSource.h" - +// Return values for EnsureElementHasGenericChild() #define NS_RDF_ELEMENT_WAS_CREATED NS_RDF_NO_VALUE #define NS_RDF_ELEMENT_WAS_THERE NS_OK @@ -107,8 +105,252 @@ static NS_DEFINE_CID(kIHTMLElementFactoryIID, NS_IHTML_ELEMENT_FACTORY_IID); //////////////////////////////////////////////////////////////////////// +class RDFGenericBuilderImpl : public nsIRDFContentModelBuilder, + public nsIRDFObserver +{ +public: + RDFGenericBuilderImpl(); + virtual ~RDFGenericBuilderImpl(); + + nsresult Init(); + + // nsISupports interface + NS_DECL_ISUPPORTS + + // nsIRDFContentModelBuilder interface + NS_IMETHOD SetDocument(nsIRDFDocument* aDocument); + NS_IMETHOD SetDataBase(nsIRDFCompositeDataSource* aDataBase); + NS_IMETHOD GetDataBase(nsIRDFCompositeDataSource** aDataBase); + NS_IMETHOD CreateRootContent(nsIRDFResource* aResource); + NS_IMETHOD SetRootContent(nsIContent* aElement); + NS_IMETHOD CreateContents(nsIContent* aElement); + NS_IMETHOD OpenContainer(nsIContent* aContainer); + NS_IMETHOD CloseContainer(nsIContent* aContainer); + NS_IMETHOD RebuildContainer(nsIContent* aContainer); + NS_IMETHOD CreateElement(PRInt32 aNameSpaceID, + nsIAtom* aTag, + nsIRDFResource* aResource, + nsIContent** aResult); + + // nsIRDFObserver interface + NS_IMETHOD OnAssert(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget); + + NS_IMETHOD OnUnassert(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget); + + NS_IMETHOD OnChange(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aOldTarget, + nsIRDFNode* aNewTarget); + + NS_IMETHOD OnMove(nsIRDFResource* aOldSource, + nsIRDFResource* aNewSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget); + + // Implementation methods + nsresult + FindTemplate(nsIContent* aElement, + nsIRDFResource* aProperty, + nsIRDFResource* aChild, + nsIContent **theTemplate); + + nsresult + IsTemplateRuleMatch(nsIContent* aElement, + nsIRDFResource* aProperty, + nsIRDFResource* aChild, + nsIContent *aRule, + PRBool *isMatch); + + PRBool + IsIgnoreableAttribute(PRInt32 aNameSpaceID, nsIAtom* aAtom); + + nsresult + GetSubstitutionText(nsIRDFResource* aResource, + const nsString& aSubstitution, + nsString& aResult); + + nsresult + BuildContentFromTemplate(nsIContent *aTemplateNode, + nsIContent *aRealNode, + PRBool aIsUnique, + nsIRDFResource* aChild, + PRInt32 aNaturalOrderPos, + PRBool aNotify); + + nsresult + CreateWidgetItem(nsIContent* aElement, + nsIRDFResource* aProperty, + nsIRDFResource* aChild, + PRInt32 aNaturalOrderPos, + PRBool aNotify); + + enum eUpdateAction { eSet, eClear }; + +#if 0 + PRBool + IsAttributePersisent(nsIContent *element, PRInt32 aNameSpaceID, nsIAtom* aAtom); + + void + GetPersistentAttributes(nsIContent *realKid); + + void + PersistAttribute(nsIContent *element, + PRInt32 aNameSpaceID, + nsIAtom* aAtom, + nsString aValue, + eUpdateAction action); + + void + PersistProperty(nsIContent *element, + nsIRDFResource *aProperty, + nsIRDFNode *aTarget, + eUpdateAction action); +#endif + + nsresult + SynchronizeUsingTemplate(nsIContent *aTemplateNode, + nsIContent* aRealNode, + eUpdateAction aAction, + nsIRDFResource* aProperty, + nsIRDFNode* aValue); + + nsresult + RemoveWidgetItem(nsIContent* aElement, + nsIRDFResource* aProperty, + nsIRDFResource* aValue, + PRBool aNotify); + + nsresult + CreateContainerContents(nsIContent* aElement, nsIRDFResource* aResource, PRBool aNotify); + + nsresult + CreateTemplateContents(nsIContent* aElement, const nsString& aTemplateID); + + nsresult + EnsureElementHasGenericChild(nsIContent* aParent, + PRInt32 aNameSpaceID, + nsIAtom* aTag, + PRBool aNotify, + nsIContent** aResult); + + PRBool + IsContainmentProperty(nsIContent* aElement, nsIRDFResource* aProperty); + + PRBool + IsIgnoredProperty(nsIContent* aElement, nsIRDFResource* aProperty); + + PRBool + IsContainer(nsIContent* aParentElement, nsIRDFResource* aTargetResource); + + PRBool + IsEmpty(nsIContent* aParentElement, nsIRDFResource* aContainer); + + PRBool + IsOpen(nsIContent* aElement); + + PRBool + IsElementInWidget(nsIContent* aElement); + + PRBool + IsResourceElement(nsIContent* aElement); + + nsresult + GetDOMNodeResource(nsIDOMNode* aNode, nsIRDFResource** aResource); + + nsresult + GetResource(PRInt32 aNameSpaceID, + nsIAtom* aNameAtom, + nsIRDFResource** aResource); + + nsresult FindInsertionPoint(nsIContent* aElement, nsIContent** aResult); + nsresult RemoveGeneratedContent(nsIContent* aElement); + nsresult FindFirstGeneratedChild(nsIContent* aElement, PRInt32* aIndex); + + // XXX. Urg. Hack until layout can batch reflows. See bug 10818. + PRBool + IsTreeWidgetItem(nsIContent* aElement); + + PRBool + IsReflowScheduled(); + + nsresult + ScheduleReflow(); + + static void + ForceTreeReflow(nsITimer* aTimer, void* aClosure); + +protected: + nsIRDFDocument* mDocument; // [WEAK] + + // We are an observer of the composite datasource. The cycle is + // broken by out-of-band SetDataBase(nsnull) call when document is + // destroyed. + nsCOMPtr mDB; + nsCOMPtr mRoot; + + nsCOMPtr mTimer; + + static nsIRDFDataSource *mLocalstore; + static PRBool persistLock; + + // pseudo-constants + static nsrefcnt gRefCnt; + static nsIRDFService* gRDFService; + static nsIRDFContainerUtils* gRDFContainerUtils; + static nsINameSpaceManager* gNameSpaceManager; + static nsIHTMLElementFactory* gHTMLElementFactory; + + static nsIAtom* kContainerAtom; + static nsIAtom* kLazyContentAtom; + static nsIAtom* kIsContainerAtom; + static nsIAtom* kIsEmptyAtom; + static nsIAtom* kXULContentsGeneratedAtom; + static nsIAtom* kTemplateContentsGeneratedAtom; + static nsIAtom* kContainerContentsGeneratedAtom; + static nsIAtom* kNaturalOrderPosAtom; + static nsIAtom* kIdAtom; + static nsIAtom* kPersistAtom; + static nsIAtom* kOpenAtom; + static nsIAtom* kEmptyAtom; + static nsIAtom* kResourceAtom; + static nsIAtom* kURIAtom; + static nsIAtom* kContainmentAtom; + static nsIAtom* kIgnoreAtom; + static nsIAtom* kRefAtom; + static nsIAtom* kValueAtom; + + static nsIAtom* kTemplateAtom; + static nsIAtom* kRuleAtom; + static nsIAtom* kTextAtom; + static nsIAtom* kPropertyAtom; + static nsIAtom* kInstanceOfAtom; + + static nsIAtom* kTreeAtom; + static nsIAtom* kTreeChildrenAtom; + static nsIAtom* kTreeItemAtom; + + static PRInt32 kNameSpaceID_RDF; + static PRInt32 kNameSpaceID_XUL; + + static nsIRDFResource* kNC_Title; + static nsIRDFResource* kNC_child; + static nsIRDFResource* kNC_Column; + static nsIRDFResource* kNC_Folder; + static nsIRDFResource* kRDF_child; + static nsIRDFResource* kRDF_instanceOf; + static nsIRDFResource* kXUL_element; + + static nsIXULSortService* gXULSortService; +}; + +//////////////////////////////////////////////////////////////////////// + nsrefcnt RDFGenericBuilderImpl::gRefCnt = 0; -nsIXULSortService* RDFGenericBuilderImpl::XULSortService = nsnull; +nsIXULSortService* RDFGenericBuilderImpl::gXULSortService = nsnull; nsIAtom* RDFGenericBuilderImpl::kContainerAtom; nsIAtom* RDFGenericBuilderImpl::kLazyContentAtom; @@ -252,7 +494,7 @@ RDFGenericBuilderImpl::~RDFGenericBuilderImpl(void) nsServiceManager::ReleaseService(kRDFServiceCID, gRDFService); nsServiceManager::ReleaseService(kRDFContainerUtilsCID, gRDFContainerUtils); - nsServiceManager::ReleaseService(kXULSortServiceCID, XULSortService); + nsServiceManager::ReleaseService(kXULSortServiceCID, gXULSortService); NS_RELEASE(gNameSpaceManager); NS_IF_RELEASE(gHTMLElementFactory); } @@ -351,7 +593,7 @@ RDFGenericBuilderImpl::Init() rv = nsServiceManager::GetService(kXULSortServiceCID, kIXULSortServiceIID, - (nsISupports**) &XULSortService); + (nsISupports**) &gXULSortService); if (NS_FAILED(rv)) return rv; rv = nsComponentManager::CreateInstance(kHTMLElementFactoryCID, @@ -475,19 +717,23 @@ RDFGenericBuilderImpl::SetRootContent(nsIContent* aElement) NS_IMETHODIMP RDFGenericBuilderImpl::CreateContents(nsIContent* aElement) { - nsresult rv; + NS_PRECONDITION(aElement != nsnull, "null ptr"); + if (! aElement) + return NS_ERROR_NULL_POINTER; // First, make sure that the element is in the right widget -- ours. if (!IsElementInWidget(aElement)) return NS_OK; + nsresult rv; + nsCOMPtr resource; rv = nsRDFContentUtils::GetElementRefResource(aElement, getter_AddRefs(resource)); if (NS_SUCCEEDED(rv)) { // The element has a resource; that means that it corresponds // to something in the graph, so we need to go to the graph to // create its contents. - rv = CreateContainerContents(aElement, resource); + rv = CreateContainerContents(aElement, resource, PR_FALSE); if (NS_FAILED(rv)) return rv; } @@ -505,6 +751,192 @@ RDFGenericBuilderImpl::CreateContents(nsIContent* aElement) } +NS_IMETHODIMP +RDFGenericBuilderImpl::OpenContainer(nsIContent* aElement) +{ + nsresult rv; + + // First, make sure that the element is in the right widget -- ours. + if (!IsElementInWidget(aElement)) + return NS_OK; + + nsCOMPtr resource; + rv = nsRDFContentUtils::GetElementRefResource(aElement, getter_AddRefs(resource)); + + // If it has no resource, there's nothing that we need to be + // concerned about here. + if (NS_FAILED(rv)) + return NS_OK; + + // The element has a resource; that means that it corresponds + // to something in the graph, so we need to go to the graph to + // create its contents. + rv = CreateContainerContents(aElement, resource, PR_TRUE); + if (NS_FAILED(rv)) return rv; + + if (rv == NS_RDF_ELEMENT_WAS_CREATED) { + nsCOMPtr tag; + rv = aElement->GetTag(*getter_AddRefs(tag)); + if (NS_FAILED(rv)) return rv; + + if (tag.get() == kTreeItemAtom) { + nsCOMPtr insertionpoint; + rv = FindInsertionPoint(aElement, getter_AddRefs(insertionpoint)); + if (NS_FAILED(rv)) return rv; + + if (! insertionpoint) { + // No content got built. Bail! + return NS_OK; + } + + // Find the first element beneath the insertion point that + // is generated from a template. + PRInt32 indx; + rv = FindFirstGeneratedChild(insertionpoint, &indx); + if (NS_FAILED(rv)) return rv; + + if (indx != -1) { + nsCOMPtr doc = do_QueryInterface(mDocument); + if (! doc) + return NS_ERROR_UNEXPECTED; + + rv = doc->ContentAppended(insertionpoint, indx); + if (NS_FAILED(rv)) return rv; + } + } + } + + return NS_OK; +} + +NS_IMETHODIMP +RDFGenericBuilderImpl::CloseContainer(nsIContent* aElement) +{ + NS_PRECONDITION(aElement != nsnull, "null ptr"); + if (! aElement) + return NS_ERROR_NULL_POINTER; + + // First, make sure that the element is in the right widget -- ours. + if (!IsElementInWidget(aElement)) + return NS_OK; + + nsresult rv; + + nsCOMPtr tag; + rv = aElement->GetTag(*getter_AddRefs(tag)); + if (NS_FAILED(rv)) return rv; + + // If it's not a tree, just bail. Keep the content around until + // the cows come home. + if (tag.get() != kTreeItemAtom) + return NS_OK; + + // Find the tag that contains the children so that we can remove + // all of the children. + // + // XXX We do this as a (premature?) optimization so that nodes + // which are not being displayed don't hang around taking up + // space. Unfortunately, the tree widget currently _relies_ on + // this behavior and will break if we don't do it :-(. + nsCOMPtr insertionpoint; + rv = FindInsertionPoint(aElement, getter_AddRefs(insertionpoint)); + if (NS_FAILED(rv)) return rv; + + if (insertionpoint) { + PRInt32 count; + rv = insertionpoint->ChildCount(count); + if (NS_FAILED(rv)) return rv; + + rv = RemoveGeneratedContent(insertionpoint); + if (NS_FAILED(rv)) return rv; + } + + // Clear the contents-generated attribute so that the next time we + // come back, we'll regenerate the kids we just killed. + rv = aElement->UnsetAttribute(kNameSpaceID_None, + kContainerContentsGeneratedAtom, + PR_FALSE); + if (NS_FAILED(rv)) return rv; + + // XXX Hack. Setting this attribute forces the RDF element to + // remember that it needs to be re-generated next time around. + rv = aElement->SetAttribute(kNameSpaceID_None, + kLazyContentAtom, + "true", + PR_FALSE); + if (NS_FAILED(rv)) return rv; + + return NS_OK; +} + + +NS_IMETHODIMP +RDFGenericBuilderImpl::RebuildContainer(nsIContent* aElement) +{ + nsresult rv; + + // Remove any generated children from this node + rv = RemoveGeneratedContent(aElement); + if (NS_FAILED(rv)) return rv; + + // Clear the contents-generated attribute so that the next time we + // come back, we'll regenerate the kids we just killed. + rv = aElement->UnsetAttribute(kNameSpaceID_None, + kContainerContentsGeneratedAtom, + PR_FALSE); + if (NS_FAILED(rv)) return rv; + + // XXX Hack. Setting this attribute forces the RDF element to + // remember that it needs to be re-generated next time around. + rv = aElement->SetAttribute(kNameSpaceID_None, + kLazyContentAtom, + "true", + PR_FALSE); + if (NS_FAILED(rv)) return rv; + + // Since we're changing the 'ref' attribute, we'll need to + // rebuild content for _this_ resource template, too. + rv = aElement->UnsetAttribute(kNameSpaceID_None, + kTemplateContentsGeneratedAtom, + PR_FALSE); + if (NS_FAILED(rv)) return rv; + + // Do it!!! + rv = CreateContents(aElement); + if (NS_FAILED(rv)) return rv; + + // Now see where on earth the content was _really_ appended so we + // can tell the frames to go reflow themselves. Start with _this_ + // element. + nsCOMPtr insertionpoint = dont_QueryInterface(aElement); + + PRInt32 indx; + rv = FindFirstGeneratedChild(insertionpoint, &indx); + if (NS_FAILED(rv)) return rv; + + if (indx == -1) { + // Okay, nothing got inserted directly beneath this node; see + // if the it was inserted somewhere _below_ us... + rv = FindInsertionPoint(aElement, getter_AddRefs(insertionpoint)); + if (NS_FAILED(rv)) return rv; + + if ((insertionpoint != nsnull) && (insertionpoint.get() != aElement)) { + rv = FindFirstGeneratedChild(insertionpoint, &indx); + if (NS_FAILED(rv)) return rv; + } + } + + if (indx != -1) { + nsCOMPtr doc = do_QueryInterface(mDocument); + + rv = doc->ContentAppended(insertionpoint, indx); + if (NS_FAILED(rv)) return rv; + } + + return NS_OK; +} + + NS_IMETHODIMP RDFGenericBuilderImpl::CreateElement(PRInt32 aNameSpaceID, nsIAtom* aTag, @@ -634,27 +1066,19 @@ RDFGenericBuilderImpl::OnAssert(nsIRDFResource* aSource, // Okay, it's a "live" element, so go ahead and append the new // child to this node. - - // XXX Bug 10818. - PRBool notify = PR_TRUE; - if (IsTreeWidgetItem(element)) { - if (!IsReflowScheduled()) { - rv = ScheduleReflow(); - if (NS_FAILED(rv)) return rv; - } - else { - // a reflow has been scheduled. we'll add the - // element but won't notify right now. - notify = PR_FALSE; - } - } - - rv = CreateWidgetItem(element, aProperty, resource, 0, notify); + rv = CreateWidgetItem(element, aProperty, resource, 0, PR_TRUE); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create widget item"); if (NS_FAILED(rv)) return rv; - - rv = element->SetAttribute(kNameSpaceID_None, kEmptyAtom, "false", PR_TRUE); + + // Update the "empty" attribute + nsAutoString empty; + rv = element->GetAttribute(kNameSpaceID_None, kEmptyAtom, empty); if (NS_FAILED(rv)) return rv; + + if ((rv != NS_CONTENT_ATTR_HAS_VALUE) || (! empty.Equals("false"))) { + rv = element->SetAttribute(kNameSpaceID_None, kEmptyAtom, "false", PR_TRUE); + if (NS_FAILED(rv)) return rv; + } } else { // Either the target of the assertion is not a resource, @@ -762,8 +1186,14 @@ RDFGenericBuilderImpl::OnUnassert(nsIRDFResource* aSource, if (NS_FAILED(rv)) return rv; if (numKids == 0) { - rv = element->SetAttribute(kNameSpaceID_None, kEmptyAtom, "true", PR_TRUE); + nsAutoString empty; + rv = element->GetAttribute(kNameSpaceID_None, kEmptyAtom, empty); if (NS_FAILED(rv)) return rv; + + if ((rv != NS_CONTENT_ATTR_HAS_VALUE) && (! empty.Equals("true"))) { + rv = element->SetAttribute(kNameSpaceID_None, kEmptyAtom, "true", PR_TRUE); + if (NS_FAILED(rv)) return rv; + } } } @@ -1020,7 +1450,7 @@ RDFGenericBuilderImpl::GetPersistentAttributes(nsIContent *element) literal->GetValueConst(&uniLiteral); if (uniLiteral) { - rv = element->SetAttribute(nameSpaceID, tag, uniLiteral, PR_TRUE); + rv = element->SetAttribute(nameSpaceID, tag, uniLiteral, PR_FALSE); } } } @@ -1430,7 +1860,7 @@ RDFGenericBuilderImpl::BuildContentFromTemplate(nsIContent *aTemplateNode, nsCOMPtr realKid; if (aIsUnique) { - rv = EnsureElementHasGenericChild(aRealNode, nameSpaceID, tag, getter_AddRefs(realKid)); + rv = EnsureElementHasGenericChild(aRealNode, nameSpaceID, tag, aNotify, getter_AddRefs(realKid)); if (NS_FAILED(rv)) return rv; if (rv == NS_RDF_ELEMENT_WAS_THERE) { @@ -1489,7 +1919,7 @@ RDFGenericBuilderImpl::BuildContentFromTemplate(nsIContent *aTemplateNode, rv = content->SetText(text.GetUnicode(), text.Length(), PR_FALSE); if (NS_FAILED(rv)) return rv; - rv = aRealNode->AppendChildTo(nsCOMPtr( do_QueryInterface(content) ), PR_FALSE); + rv = aRealNode->AppendChildTo(nsCOMPtr( do_QueryInterface(content) ), aNotify); if (NS_FAILED(rv)) return rv; } } @@ -1564,15 +1994,14 @@ RDFGenericBuilderImpl::BuildContentFromTemplate(nsIContent *aTemplateNode, // We'll _already_ have added the unique elements. if (! aIsUnique) { - // Note: add into tree, but only sort if its a resource element! - if ((nsnull != XULSortService) && (isResourceElement)) { - rv = XULSortService->InsertContainerNode(aRealNode, realKid, aNotify); - if (NS_FAILED(rv)) { - aRealNode->AppendChildTo(realKid, aNotify); - } + // Add into content model, special casing treeitems. + if ((nsnull != gXULSortService) && (isResourceElement) && (tag.get() == kTreeItemAtom)) { + rv = gXULSortService->InsertContainerNode(aRealNode, realKid, aNotify); + NS_ASSERTION(NS_SUCCEEDED(rv), "unable to insert element via sort service"); } else { - aRealNode->AppendChildTo(realKid, aNotify); + rv = aRealNode->AppendChildTo(realKid, aNotify); + NS_ASSERTION(NS_SUCCEEDED(rv), "unable to insert element"); } } @@ -1605,12 +2034,8 @@ RDFGenericBuilderImpl::CreateWidgetItem(nsIContent *aElement, return NS_OK; } - rv = BuildContentFromTemplate(tmpl, - aElement, - PR_TRUE, - aChild, - aNaturalOrderPos, - aNotify); + rv = BuildContentFromTemplate(tmpl, aElement, PR_TRUE, aChild, aNaturalOrderPos, aNotify); + NS_ASSERTION(NS_SUCCEEDED(rv), "unable to build content from template"); return rv; } @@ -1777,7 +2202,7 @@ RDFGenericBuilderImpl::RemoveWidgetItem(nsIContent* aElement, nsresult -RDFGenericBuilderImpl::CreateContainerContents(nsIContent* aElement, nsIRDFResource* aResource) +RDFGenericBuilderImpl::CreateContainerContents(nsIContent* aElement, nsIRDFResource* aResource, PRBool aNotify) { // Create the contents of a container by iterating over all of the // "containment" arcs out of the element's resource. @@ -1801,7 +2226,7 @@ RDFGenericBuilderImpl::CreateContainerContents(nsIContent* aElement, nsIRDFResou if (NS_FAILED(rv)) return rv; if ((rv == NS_CONTENT_ATTR_HAS_VALUE) && (attrValue.EqualsIgnoreCase("true"))) - return NS_OK; + return NS_RDF_ELEMENT_WAS_CREATED; // Now mark the element's contents as being generated so that // any re-entrant calls don't trigger an infinite recursion. @@ -1821,11 +2246,6 @@ RDFGenericBuilderImpl::CreateContainerContents(nsIContent* aElement, nsIRDFResou rv = mDB->ArcLabelsOut(aResource, getter_AddRefs(properties)); if (NS_FAILED(rv)) return rv; - // rjc - sort - nsCOMPtr tempArray; - rv = NS_NewISupportsArray(getter_AddRefs(tempArray)); - if NS_FAILED(rv) return rv; - while (1) { PRBool hasMore; rv = properties->HasMoreElements(&hasMore); @@ -1865,74 +2285,13 @@ RDFGenericBuilderImpl::CreateContainerContents(nsIContent* aElement, nsIRDFResou rv = targets->GetNext(getter_AddRefs(isupportsNext)); if (NS_FAILED(rv)) return rv; - nsCOMPtr valueResource = do_QueryInterface(isupportsNext); - NS_ASSERTION(valueResource != nsnull, "not a resource"); - if (! valueResource) + nsCOMPtr target = do_QueryInterface(isupportsNext); + NS_ASSERTION(target != nsnull, "not a resource"); + if (! target) continue; - // XXX hack: always append value resource 1st due to sort - // callback implementation - tempArray->AppendElement(valueResource); - tempArray->AppendElement(property); - } - } - - PRUint32 numElements; - rv = tempArray->Count(&numElements); - if (NS_FAILED(rv)) return rv; - - if (numElements == 0) - return NS_OK; - - // Tree widget hackery. To be removed if and when the - // reflow lock is exposed. See bug 10818. - PRBool istree = IsTreeWidgetItem(aElement); - - { - // XXX change to use nsVoidArray? - nsIRDFResource** flatArray = new nsIRDFResource*[numElements]; - if (! flatArray) return NS_ERROR_OUT_OF_MEMORY; - - // flatten array of resources, sort them, then add as item elements - PRUint32 loop; - for (loop=0; loopElementAt(loop); - - if (XULSortService) { - XULSortService->OpenContainer(mDB, aElement, flatArray, numElements/2, 2*sizeof(nsIRDFResource *)); - } - - // This will insert all of the elements into the - // container, but _won't_ bother layout about it. - for (loop=0; loop=0; i--) { - NS_IF_RELEASE(flatArray[i]); - } - - delete [] flatArray; - if (NS_FAILED(rv)) return rv; - } - - if (istree) { - nsCOMPtr doc = do_QueryInterface(mDocument); - if (! doc) - return NS_ERROR_UNEXPECTED; - - nsCOMPtr treechildren; - rv = nsRDFContentUtils::FindChildByTag(aElement, - kNameSpaceID_XUL, - kTreeChildrenAtom, - getter_AddRefs(treechildren)); - - if (NS_SUCCEEDED(rv)) { - // _Now_ tell layout that we've mucked with the container. This'll - // force a reflow and get the content displayed. - rv = doc->ContentAppended(treechildren, 0); + rv = CreateWidgetItem(aElement, property, target, -1, aNotify); + NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create item"); if (NS_FAILED(rv)) return rv; } } @@ -2000,7 +2359,7 @@ RDFGenericBuilderImpl::CreateTemplateContents(nsIContent* aElement, const nsStri element = parent; } - rv = BuildContentFromTemplate(tmpl, aElement, PR_FALSE, resource, -1, PR_TRUE); + rv = BuildContentFromTemplate(tmpl, aElement, PR_FALSE, resource, -1, PR_FALSE); if (NS_FAILED(rv)) return rv; return NS_OK; @@ -2010,6 +2369,7 @@ nsresult RDFGenericBuilderImpl::EnsureElementHasGenericChild(nsIContent* parent, PRInt32 nameSpaceID, nsIAtom* tag, + PRBool aNotify, nsIContent** result) { nsresult rv; @@ -2026,7 +2386,7 @@ RDFGenericBuilderImpl::EnsureElementHasGenericChild(nsIContent* parent, if (NS_FAILED(rv)) return rv; // XXX Note that the notification ensures we won't batch insertions! This could be bad! - Dave - rv = parent->AppendChildTo(element, PR_TRUE); + rv = parent->AppendChildTo(element, aNotify); if (NS_FAILED(rv)) return rv; *result = element; @@ -2381,6 +2741,109 @@ RDFGenericBuilderImpl::GetResource(PRInt32 aNameSpaceID, } +nsresult +RDFGenericBuilderImpl::FindInsertionPoint(nsIContent* aElement, nsIContent** aResult) +{ + nsresult rv; + + // XXX Hack-o-rama. This needs to be fixed to actually grovel over + // the template n' stuff. + nsCOMPtr tag; + rv = aElement->GetTag(*getter_AddRefs(tag)); + if (NS_FAILED(rv)) return rv; + + nsAutoString tagName; + rv = tag->ToString(tagName); + if (NS_FAILED(rv)) return rv; + + if (tagName.Equals("tree") || tagName.Equals("treeitem")) { + rv = nsRDFContentUtils::FindChildByTag(aElement, kNameSpaceID_XUL, NS_NewAtom("treechildren"), aResult); + if (NS_FAILED(rv)) return rv; + } + else if (tagName.Equals("menu")) { + rv = nsRDFContentUtils::FindChildByTag(aElement, kNameSpaceID_XUL, NS_NewAtom("menupopup"), aResult); + if (NS_FAILED(rv)) return rv; + } + else { + *aResult = aElement; + NS_ADDREF(*aResult); + } + + return NS_OK; +} + + +nsresult +RDFGenericBuilderImpl::RemoveGeneratedContent(nsIContent* aElement) +{ + // Remove all the content beneath aElement that has been generated + // from a template. + + nsresult rv; + + PRInt32 count; + rv = aElement->ChildCount(count); + if (NS_FAILED(rv)) return rv; + + while (--count >= 0) { + nsCOMPtr child; + rv = aElement->ChildAt(count, *getter_AddRefs(child)); + if (NS_FAILED(rv)) return rv; + + nsAutoString tmplID; + rv = child->GetAttribute(kNameSpaceID_None, kTemplateAtom, tmplID); + if (NS_FAILED(rv)) return rv; + + if (rv != NS_CONTENT_ATTR_HAS_VALUE) + continue; + + // It's a generated element. Remove it, and set its document + // to null so that it'll get knocked out of the XUL doc's + // resource-to-element map. + rv = aElement->RemoveChildAt(count, PR_TRUE); + NS_ASSERTION(NS_SUCCEEDED(rv), "error removing child"); + + rv = child->SetDocument(nsnull, PR_TRUE); + if (NS_FAILED(rv)) return rv; + } + + return NS_OK; +} + + +nsresult +RDFGenericBuilderImpl::FindFirstGeneratedChild(nsIContent* aElement, PRInt32* aIndex) +{ + // Find the first kid of aElement that has been generated from a + // template. + + nsresult rv; + + PRInt32 count; + rv = aElement->ChildCount(count); + if (NS_FAILED(rv)) return rv; + + PRInt32 i = 0; + while (i < count) { + nsCOMPtr child; + rv = aElement->ChildAt(i, *getter_AddRefs(child)); + if (NS_FAILED(rv)) return rv; + + nsAutoString tmplID; + rv = child->GetAttribute(kNameSpaceID_None, kTemplateAtom, tmplID); + if (NS_FAILED(rv)) return rv; + + if (rv == NS_CONTENT_ATTR_HAS_VALUE) + break; + + ++i; + } + + *aIndex = (i < count) ? i : -1; + return NS_OK; +} + + PRBool RDFGenericBuilderImpl::IsTreeWidgetItem(nsIContent* aElement) { diff --git a/mozilla/rdf/content/public/nsIRDFContentModelBuilder.h b/mozilla/rdf/content/public/nsIRDFContentModelBuilder.h index 8af3daf0b54..7f913a1b4c9 100644 --- a/mozilla/rdf/content/public/nsIRDFContentModelBuilder.h +++ b/mozilla/rdf/content/public/nsIRDFContentModelBuilder.h @@ -67,6 +67,23 @@ public: */ NS_IMETHOD CreateContents(nsIContent* aElement) = 0; + /** + * 'Open' a container element that was closed before. This gives + * the container a chance to populate its contents. + */ + NS_IMETHOD OpenContainer(nsIContent* aContainer) = 0; + + /** + * 'Close' an open container. This gives the container a chance to + * release unused content nodes. + */ + NS_IMETHOD CloseContainer(nsIContent* aContainer) = 0; + + /** + * Rebuild the contents of a container. + */ + NS_IMETHOD RebuildContainer(nsIContent* aContainer) = 0; + /** * Construct an element. This is exposed as a public method, * because the document implementation may need to call it to @@ -76,6 +93,7 @@ public: nsIAtom* aTag, nsIRDFResource* aResource, nsIContent** aResult) = 0; + }; extern nsresult NS_NewXULTemplateBuilder(nsIRDFContentModelBuilder** aResult); diff --git a/mozilla/rdf/content/src/nsRDFContentUtils.cpp b/mozilla/rdf/content/src/nsRDFContentUtils.cpp index 49883fc4eb9..5e704743c1f 100644 --- a/mozilla/rdf/content/src/nsRDFContentUtils.cpp +++ b/mozilla/rdf/content/src/nsRDFContentUtils.cpp @@ -93,7 +93,7 @@ nsRDFContentUtils::AttachTextNode(nsIContent* parent, nsIRDFNode* value) if (NS_FAILED(rv)) return rv; // hook it up to the child - rv = parent->AppendChildTo(nsCOMPtr( do_QueryInterface(text) ), PR_TRUE); + rv = parent->AppendChildTo(nsCOMPtr( do_QueryInterface(text) ), PR_FALSE); if (NS_FAILED(rv)) return rv; return NS_OK; diff --git a/mozilla/rdf/content/src/nsRDFGenericBuilder.cpp b/mozilla/rdf/content/src/nsRDFGenericBuilder.cpp index 0b22cc91838..cffb60dd5f7 100644 --- a/mozilla/rdf/content/src/nsRDFGenericBuilder.cpp +++ b/mozilla/rdf/content/src/nsRDFGenericBuilder.cpp @@ -41,38 +41,36 @@ #include "nsIDOMElement.h" #include "nsIDOMNode.h" #include "nsIDOMXULDocument.h" +#include "nsIDOMXULElement.h" #include "nsIDocument.h" +#include "nsIHTMLContent.h" +#include "nsIHTMLElementFactory.h" #include "nsINameSpaceManager.h" -#include "nsIRDFContentModelBuilder.h" #include "nsIRDFCompositeDataSource.h" +#include "nsIRDFContainerUtils.h" +#include "nsIRDFContentModelBuilder.h" #include "nsIRDFDocument.h" #include "nsIRDFNode.h" #include "nsIRDFObserver.h" +#include "nsIRDFRemoteDataSource.h" #include "nsIRDFService.h" #include "nsIServiceManager.h" -#include "nsINameSpaceManager.h" -#include "nsIServiceManager.h" #include "nsISupportsArray.h" #include "nsITextContent.h" +#include "nsITimer.h" #include "nsIURL.h" +#include "nsIXULSortService.h" #include "nsLayoutCID.h" #include "nsRDFCID.h" #include "nsRDFContentUtils.h" #include "nsString.h" +#include "nsVoidArray.h" #include "nsXPIDLString.h" +#include "prlog.h" #include "rdf.h" #include "rdfutil.h" -#include "nsITimer.h" -#include "nsIDOMXULElement.h" -#include "nsVoidArray.h" -#include "nsIXULSortService.h" -#include "nsIHTMLElementFactory.h" -#include "nsIHTMLContent.h" -#include "nsRDFGenericBuilder.h" -#include "prlog.h" -#include "nsIRDFRemoteDataSource.h" - +// Return values for EnsureElementHasGenericChild() #define NS_RDF_ELEMENT_WAS_CREATED NS_RDF_NO_VALUE #define NS_RDF_ELEMENT_WAS_THERE NS_OK @@ -107,8 +105,252 @@ static NS_DEFINE_CID(kIHTMLElementFactoryIID, NS_IHTML_ELEMENT_FACTORY_IID); //////////////////////////////////////////////////////////////////////// +class RDFGenericBuilderImpl : public nsIRDFContentModelBuilder, + public nsIRDFObserver +{ +public: + RDFGenericBuilderImpl(); + virtual ~RDFGenericBuilderImpl(); + + nsresult Init(); + + // nsISupports interface + NS_DECL_ISUPPORTS + + // nsIRDFContentModelBuilder interface + NS_IMETHOD SetDocument(nsIRDFDocument* aDocument); + NS_IMETHOD SetDataBase(nsIRDFCompositeDataSource* aDataBase); + NS_IMETHOD GetDataBase(nsIRDFCompositeDataSource** aDataBase); + NS_IMETHOD CreateRootContent(nsIRDFResource* aResource); + NS_IMETHOD SetRootContent(nsIContent* aElement); + NS_IMETHOD CreateContents(nsIContent* aElement); + NS_IMETHOD OpenContainer(nsIContent* aContainer); + NS_IMETHOD CloseContainer(nsIContent* aContainer); + NS_IMETHOD RebuildContainer(nsIContent* aContainer); + NS_IMETHOD CreateElement(PRInt32 aNameSpaceID, + nsIAtom* aTag, + nsIRDFResource* aResource, + nsIContent** aResult); + + // nsIRDFObserver interface + NS_IMETHOD OnAssert(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget); + + NS_IMETHOD OnUnassert(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget); + + NS_IMETHOD OnChange(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aOldTarget, + nsIRDFNode* aNewTarget); + + NS_IMETHOD OnMove(nsIRDFResource* aOldSource, + nsIRDFResource* aNewSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget); + + // Implementation methods + nsresult + FindTemplate(nsIContent* aElement, + nsIRDFResource* aProperty, + nsIRDFResource* aChild, + nsIContent **theTemplate); + + nsresult + IsTemplateRuleMatch(nsIContent* aElement, + nsIRDFResource* aProperty, + nsIRDFResource* aChild, + nsIContent *aRule, + PRBool *isMatch); + + PRBool + IsIgnoreableAttribute(PRInt32 aNameSpaceID, nsIAtom* aAtom); + + nsresult + GetSubstitutionText(nsIRDFResource* aResource, + const nsString& aSubstitution, + nsString& aResult); + + nsresult + BuildContentFromTemplate(nsIContent *aTemplateNode, + nsIContent *aRealNode, + PRBool aIsUnique, + nsIRDFResource* aChild, + PRInt32 aNaturalOrderPos, + PRBool aNotify); + + nsresult + CreateWidgetItem(nsIContent* aElement, + nsIRDFResource* aProperty, + nsIRDFResource* aChild, + PRInt32 aNaturalOrderPos, + PRBool aNotify); + + enum eUpdateAction { eSet, eClear }; + +#if 0 + PRBool + IsAttributePersisent(nsIContent *element, PRInt32 aNameSpaceID, nsIAtom* aAtom); + + void + GetPersistentAttributes(nsIContent *realKid); + + void + PersistAttribute(nsIContent *element, + PRInt32 aNameSpaceID, + nsIAtom* aAtom, + nsString aValue, + eUpdateAction action); + + void + PersistProperty(nsIContent *element, + nsIRDFResource *aProperty, + nsIRDFNode *aTarget, + eUpdateAction action); +#endif + + nsresult + SynchronizeUsingTemplate(nsIContent *aTemplateNode, + nsIContent* aRealNode, + eUpdateAction aAction, + nsIRDFResource* aProperty, + nsIRDFNode* aValue); + + nsresult + RemoveWidgetItem(nsIContent* aElement, + nsIRDFResource* aProperty, + nsIRDFResource* aValue, + PRBool aNotify); + + nsresult + CreateContainerContents(nsIContent* aElement, nsIRDFResource* aResource, PRBool aNotify); + + nsresult + CreateTemplateContents(nsIContent* aElement, const nsString& aTemplateID); + + nsresult + EnsureElementHasGenericChild(nsIContent* aParent, + PRInt32 aNameSpaceID, + nsIAtom* aTag, + PRBool aNotify, + nsIContent** aResult); + + PRBool + IsContainmentProperty(nsIContent* aElement, nsIRDFResource* aProperty); + + PRBool + IsIgnoredProperty(nsIContent* aElement, nsIRDFResource* aProperty); + + PRBool + IsContainer(nsIContent* aParentElement, nsIRDFResource* aTargetResource); + + PRBool + IsEmpty(nsIContent* aParentElement, nsIRDFResource* aContainer); + + PRBool + IsOpen(nsIContent* aElement); + + PRBool + IsElementInWidget(nsIContent* aElement); + + PRBool + IsResourceElement(nsIContent* aElement); + + nsresult + GetDOMNodeResource(nsIDOMNode* aNode, nsIRDFResource** aResource); + + nsresult + GetResource(PRInt32 aNameSpaceID, + nsIAtom* aNameAtom, + nsIRDFResource** aResource); + + nsresult FindInsertionPoint(nsIContent* aElement, nsIContent** aResult); + nsresult RemoveGeneratedContent(nsIContent* aElement); + nsresult FindFirstGeneratedChild(nsIContent* aElement, PRInt32* aIndex); + + // XXX. Urg. Hack until layout can batch reflows. See bug 10818. + PRBool + IsTreeWidgetItem(nsIContent* aElement); + + PRBool + IsReflowScheduled(); + + nsresult + ScheduleReflow(); + + static void + ForceTreeReflow(nsITimer* aTimer, void* aClosure); + +protected: + nsIRDFDocument* mDocument; // [WEAK] + + // We are an observer of the composite datasource. The cycle is + // broken by out-of-band SetDataBase(nsnull) call when document is + // destroyed. + nsCOMPtr mDB; + nsCOMPtr mRoot; + + nsCOMPtr mTimer; + + static nsIRDFDataSource *mLocalstore; + static PRBool persistLock; + + // pseudo-constants + static nsrefcnt gRefCnt; + static nsIRDFService* gRDFService; + static nsIRDFContainerUtils* gRDFContainerUtils; + static nsINameSpaceManager* gNameSpaceManager; + static nsIHTMLElementFactory* gHTMLElementFactory; + + static nsIAtom* kContainerAtom; + static nsIAtom* kLazyContentAtom; + static nsIAtom* kIsContainerAtom; + static nsIAtom* kIsEmptyAtom; + static nsIAtom* kXULContentsGeneratedAtom; + static nsIAtom* kTemplateContentsGeneratedAtom; + static nsIAtom* kContainerContentsGeneratedAtom; + static nsIAtom* kNaturalOrderPosAtom; + static nsIAtom* kIdAtom; + static nsIAtom* kPersistAtom; + static nsIAtom* kOpenAtom; + static nsIAtom* kEmptyAtom; + static nsIAtom* kResourceAtom; + static nsIAtom* kURIAtom; + static nsIAtom* kContainmentAtom; + static nsIAtom* kIgnoreAtom; + static nsIAtom* kRefAtom; + static nsIAtom* kValueAtom; + + static nsIAtom* kTemplateAtom; + static nsIAtom* kRuleAtom; + static nsIAtom* kTextAtom; + static nsIAtom* kPropertyAtom; + static nsIAtom* kInstanceOfAtom; + + static nsIAtom* kTreeAtom; + static nsIAtom* kTreeChildrenAtom; + static nsIAtom* kTreeItemAtom; + + static PRInt32 kNameSpaceID_RDF; + static PRInt32 kNameSpaceID_XUL; + + static nsIRDFResource* kNC_Title; + static nsIRDFResource* kNC_child; + static nsIRDFResource* kNC_Column; + static nsIRDFResource* kNC_Folder; + static nsIRDFResource* kRDF_child; + static nsIRDFResource* kRDF_instanceOf; + static nsIRDFResource* kXUL_element; + + static nsIXULSortService* gXULSortService; +}; + +//////////////////////////////////////////////////////////////////////// + nsrefcnt RDFGenericBuilderImpl::gRefCnt = 0; -nsIXULSortService* RDFGenericBuilderImpl::XULSortService = nsnull; +nsIXULSortService* RDFGenericBuilderImpl::gXULSortService = nsnull; nsIAtom* RDFGenericBuilderImpl::kContainerAtom; nsIAtom* RDFGenericBuilderImpl::kLazyContentAtom; @@ -252,7 +494,7 @@ RDFGenericBuilderImpl::~RDFGenericBuilderImpl(void) nsServiceManager::ReleaseService(kRDFServiceCID, gRDFService); nsServiceManager::ReleaseService(kRDFContainerUtilsCID, gRDFContainerUtils); - nsServiceManager::ReleaseService(kXULSortServiceCID, XULSortService); + nsServiceManager::ReleaseService(kXULSortServiceCID, gXULSortService); NS_RELEASE(gNameSpaceManager); NS_IF_RELEASE(gHTMLElementFactory); } @@ -351,7 +593,7 @@ RDFGenericBuilderImpl::Init() rv = nsServiceManager::GetService(kXULSortServiceCID, kIXULSortServiceIID, - (nsISupports**) &XULSortService); + (nsISupports**) &gXULSortService); if (NS_FAILED(rv)) return rv; rv = nsComponentManager::CreateInstance(kHTMLElementFactoryCID, @@ -475,19 +717,23 @@ RDFGenericBuilderImpl::SetRootContent(nsIContent* aElement) NS_IMETHODIMP RDFGenericBuilderImpl::CreateContents(nsIContent* aElement) { - nsresult rv; + NS_PRECONDITION(aElement != nsnull, "null ptr"); + if (! aElement) + return NS_ERROR_NULL_POINTER; // First, make sure that the element is in the right widget -- ours. if (!IsElementInWidget(aElement)) return NS_OK; + nsresult rv; + nsCOMPtr resource; rv = nsRDFContentUtils::GetElementRefResource(aElement, getter_AddRefs(resource)); if (NS_SUCCEEDED(rv)) { // The element has a resource; that means that it corresponds // to something in the graph, so we need to go to the graph to // create its contents. - rv = CreateContainerContents(aElement, resource); + rv = CreateContainerContents(aElement, resource, PR_FALSE); if (NS_FAILED(rv)) return rv; } @@ -505,6 +751,192 @@ RDFGenericBuilderImpl::CreateContents(nsIContent* aElement) } +NS_IMETHODIMP +RDFGenericBuilderImpl::OpenContainer(nsIContent* aElement) +{ + nsresult rv; + + // First, make sure that the element is in the right widget -- ours. + if (!IsElementInWidget(aElement)) + return NS_OK; + + nsCOMPtr resource; + rv = nsRDFContentUtils::GetElementRefResource(aElement, getter_AddRefs(resource)); + + // If it has no resource, there's nothing that we need to be + // concerned about here. + if (NS_FAILED(rv)) + return NS_OK; + + // The element has a resource; that means that it corresponds + // to something in the graph, so we need to go to the graph to + // create its contents. + rv = CreateContainerContents(aElement, resource, PR_TRUE); + if (NS_FAILED(rv)) return rv; + + if (rv == NS_RDF_ELEMENT_WAS_CREATED) { + nsCOMPtr tag; + rv = aElement->GetTag(*getter_AddRefs(tag)); + if (NS_FAILED(rv)) return rv; + + if (tag.get() == kTreeItemAtom) { + nsCOMPtr insertionpoint; + rv = FindInsertionPoint(aElement, getter_AddRefs(insertionpoint)); + if (NS_FAILED(rv)) return rv; + + if (! insertionpoint) { + // No content got built. Bail! + return NS_OK; + } + + // Find the first element beneath the insertion point that + // is generated from a template. + PRInt32 indx; + rv = FindFirstGeneratedChild(insertionpoint, &indx); + if (NS_FAILED(rv)) return rv; + + if (indx != -1) { + nsCOMPtr doc = do_QueryInterface(mDocument); + if (! doc) + return NS_ERROR_UNEXPECTED; + + rv = doc->ContentAppended(insertionpoint, indx); + if (NS_FAILED(rv)) return rv; + } + } + } + + return NS_OK; +} + +NS_IMETHODIMP +RDFGenericBuilderImpl::CloseContainer(nsIContent* aElement) +{ + NS_PRECONDITION(aElement != nsnull, "null ptr"); + if (! aElement) + return NS_ERROR_NULL_POINTER; + + // First, make sure that the element is in the right widget -- ours. + if (!IsElementInWidget(aElement)) + return NS_OK; + + nsresult rv; + + nsCOMPtr tag; + rv = aElement->GetTag(*getter_AddRefs(tag)); + if (NS_FAILED(rv)) return rv; + + // If it's not a tree, just bail. Keep the content around until + // the cows come home. + if (tag.get() != kTreeItemAtom) + return NS_OK; + + // Find the tag that contains the children so that we can remove + // all of the children. + // + // XXX We do this as a (premature?) optimization so that nodes + // which are not being displayed don't hang around taking up + // space. Unfortunately, the tree widget currently _relies_ on + // this behavior and will break if we don't do it :-(. + nsCOMPtr insertionpoint; + rv = FindInsertionPoint(aElement, getter_AddRefs(insertionpoint)); + if (NS_FAILED(rv)) return rv; + + if (insertionpoint) { + PRInt32 count; + rv = insertionpoint->ChildCount(count); + if (NS_FAILED(rv)) return rv; + + rv = RemoveGeneratedContent(insertionpoint); + if (NS_FAILED(rv)) return rv; + } + + // Clear the contents-generated attribute so that the next time we + // come back, we'll regenerate the kids we just killed. + rv = aElement->UnsetAttribute(kNameSpaceID_None, + kContainerContentsGeneratedAtom, + PR_FALSE); + if (NS_FAILED(rv)) return rv; + + // XXX Hack. Setting this attribute forces the RDF element to + // remember that it needs to be re-generated next time around. + rv = aElement->SetAttribute(kNameSpaceID_None, + kLazyContentAtom, + "true", + PR_FALSE); + if (NS_FAILED(rv)) return rv; + + return NS_OK; +} + + +NS_IMETHODIMP +RDFGenericBuilderImpl::RebuildContainer(nsIContent* aElement) +{ + nsresult rv; + + // Remove any generated children from this node + rv = RemoveGeneratedContent(aElement); + if (NS_FAILED(rv)) return rv; + + // Clear the contents-generated attribute so that the next time we + // come back, we'll regenerate the kids we just killed. + rv = aElement->UnsetAttribute(kNameSpaceID_None, + kContainerContentsGeneratedAtom, + PR_FALSE); + if (NS_FAILED(rv)) return rv; + + // XXX Hack. Setting this attribute forces the RDF element to + // remember that it needs to be re-generated next time around. + rv = aElement->SetAttribute(kNameSpaceID_None, + kLazyContentAtom, + "true", + PR_FALSE); + if (NS_FAILED(rv)) return rv; + + // Since we're changing the 'ref' attribute, we'll need to + // rebuild content for _this_ resource template, too. + rv = aElement->UnsetAttribute(kNameSpaceID_None, + kTemplateContentsGeneratedAtom, + PR_FALSE); + if (NS_FAILED(rv)) return rv; + + // Do it!!! + rv = CreateContents(aElement); + if (NS_FAILED(rv)) return rv; + + // Now see where on earth the content was _really_ appended so we + // can tell the frames to go reflow themselves. Start with _this_ + // element. + nsCOMPtr insertionpoint = dont_QueryInterface(aElement); + + PRInt32 indx; + rv = FindFirstGeneratedChild(insertionpoint, &indx); + if (NS_FAILED(rv)) return rv; + + if (indx == -1) { + // Okay, nothing got inserted directly beneath this node; see + // if the it was inserted somewhere _below_ us... + rv = FindInsertionPoint(aElement, getter_AddRefs(insertionpoint)); + if (NS_FAILED(rv)) return rv; + + if ((insertionpoint != nsnull) && (insertionpoint.get() != aElement)) { + rv = FindFirstGeneratedChild(insertionpoint, &indx); + if (NS_FAILED(rv)) return rv; + } + } + + if (indx != -1) { + nsCOMPtr doc = do_QueryInterface(mDocument); + + rv = doc->ContentAppended(insertionpoint, indx); + if (NS_FAILED(rv)) return rv; + } + + return NS_OK; +} + + NS_IMETHODIMP RDFGenericBuilderImpl::CreateElement(PRInt32 aNameSpaceID, nsIAtom* aTag, @@ -634,27 +1066,19 @@ RDFGenericBuilderImpl::OnAssert(nsIRDFResource* aSource, // Okay, it's a "live" element, so go ahead and append the new // child to this node. - - // XXX Bug 10818. - PRBool notify = PR_TRUE; - if (IsTreeWidgetItem(element)) { - if (!IsReflowScheduled()) { - rv = ScheduleReflow(); - if (NS_FAILED(rv)) return rv; - } - else { - // a reflow has been scheduled. we'll add the - // element but won't notify right now. - notify = PR_FALSE; - } - } - - rv = CreateWidgetItem(element, aProperty, resource, 0, notify); + rv = CreateWidgetItem(element, aProperty, resource, 0, PR_TRUE); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create widget item"); if (NS_FAILED(rv)) return rv; - - rv = element->SetAttribute(kNameSpaceID_None, kEmptyAtom, "false", PR_TRUE); + + // Update the "empty" attribute + nsAutoString empty; + rv = element->GetAttribute(kNameSpaceID_None, kEmptyAtom, empty); if (NS_FAILED(rv)) return rv; + + if ((rv != NS_CONTENT_ATTR_HAS_VALUE) || (! empty.Equals("false"))) { + rv = element->SetAttribute(kNameSpaceID_None, kEmptyAtom, "false", PR_TRUE); + if (NS_FAILED(rv)) return rv; + } } else { // Either the target of the assertion is not a resource, @@ -762,8 +1186,14 @@ RDFGenericBuilderImpl::OnUnassert(nsIRDFResource* aSource, if (NS_FAILED(rv)) return rv; if (numKids == 0) { - rv = element->SetAttribute(kNameSpaceID_None, kEmptyAtom, "true", PR_TRUE); + nsAutoString empty; + rv = element->GetAttribute(kNameSpaceID_None, kEmptyAtom, empty); if (NS_FAILED(rv)) return rv; + + if ((rv != NS_CONTENT_ATTR_HAS_VALUE) && (! empty.Equals("true"))) { + rv = element->SetAttribute(kNameSpaceID_None, kEmptyAtom, "true", PR_TRUE); + if (NS_FAILED(rv)) return rv; + } } } @@ -1020,7 +1450,7 @@ RDFGenericBuilderImpl::GetPersistentAttributes(nsIContent *element) literal->GetValueConst(&uniLiteral); if (uniLiteral) { - rv = element->SetAttribute(nameSpaceID, tag, uniLiteral, PR_TRUE); + rv = element->SetAttribute(nameSpaceID, tag, uniLiteral, PR_FALSE); } } } @@ -1430,7 +1860,7 @@ RDFGenericBuilderImpl::BuildContentFromTemplate(nsIContent *aTemplateNode, nsCOMPtr realKid; if (aIsUnique) { - rv = EnsureElementHasGenericChild(aRealNode, nameSpaceID, tag, getter_AddRefs(realKid)); + rv = EnsureElementHasGenericChild(aRealNode, nameSpaceID, tag, aNotify, getter_AddRefs(realKid)); if (NS_FAILED(rv)) return rv; if (rv == NS_RDF_ELEMENT_WAS_THERE) { @@ -1489,7 +1919,7 @@ RDFGenericBuilderImpl::BuildContentFromTemplate(nsIContent *aTemplateNode, rv = content->SetText(text.GetUnicode(), text.Length(), PR_FALSE); if (NS_FAILED(rv)) return rv; - rv = aRealNode->AppendChildTo(nsCOMPtr( do_QueryInterface(content) ), PR_FALSE); + rv = aRealNode->AppendChildTo(nsCOMPtr( do_QueryInterface(content) ), aNotify); if (NS_FAILED(rv)) return rv; } } @@ -1564,15 +1994,14 @@ RDFGenericBuilderImpl::BuildContentFromTemplate(nsIContent *aTemplateNode, // We'll _already_ have added the unique elements. if (! aIsUnique) { - // Note: add into tree, but only sort if its a resource element! - if ((nsnull != XULSortService) && (isResourceElement)) { - rv = XULSortService->InsertContainerNode(aRealNode, realKid, aNotify); - if (NS_FAILED(rv)) { - aRealNode->AppendChildTo(realKid, aNotify); - } + // Add into content model, special casing treeitems. + if ((nsnull != gXULSortService) && (isResourceElement) && (tag.get() == kTreeItemAtom)) { + rv = gXULSortService->InsertContainerNode(aRealNode, realKid, aNotify); + NS_ASSERTION(NS_SUCCEEDED(rv), "unable to insert element via sort service"); } else { - aRealNode->AppendChildTo(realKid, aNotify); + rv = aRealNode->AppendChildTo(realKid, aNotify); + NS_ASSERTION(NS_SUCCEEDED(rv), "unable to insert element"); } } @@ -1605,12 +2034,8 @@ RDFGenericBuilderImpl::CreateWidgetItem(nsIContent *aElement, return NS_OK; } - rv = BuildContentFromTemplate(tmpl, - aElement, - PR_TRUE, - aChild, - aNaturalOrderPos, - aNotify); + rv = BuildContentFromTemplate(tmpl, aElement, PR_TRUE, aChild, aNaturalOrderPos, aNotify); + NS_ASSERTION(NS_SUCCEEDED(rv), "unable to build content from template"); return rv; } @@ -1777,7 +2202,7 @@ RDFGenericBuilderImpl::RemoveWidgetItem(nsIContent* aElement, nsresult -RDFGenericBuilderImpl::CreateContainerContents(nsIContent* aElement, nsIRDFResource* aResource) +RDFGenericBuilderImpl::CreateContainerContents(nsIContent* aElement, nsIRDFResource* aResource, PRBool aNotify) { // Create the contents of a container by iterating over all of the // "containment" arcs out of the element's resource. @@ -1801,7 +2226,7 @@ RDFGenericBuilderImpl::CreateContainerContents(nsIContent* aElement, nsIRDFResou if (NS_FAILED(rv)) return rv; if ((rv == NS_CONTENT_ATTR_HAS_VALUE) && (attrValue.EqualsIgnoreCase("true"))) - return NS_OK; + return NS_RDF_ELEMENT_WAS_CREATED; // Now mark the element's contents as being generated so that // any re-entrant calls don't trigger an infinite recursion. @@ -1821,11 +2246,6 @@ RDFGenericBuilderImpl::CreateContainerContents(nsIContent* aElement, nsIRDFResou rv = mDB->ArcLabelsOut(aResource, getter_AddRefs(properties)); if (NS_FAILED(rv)) return rv; - // rjc - sort - nsCOMPtr tempArray; - rv = NS_NewISupportsArray(getter_AddRefs(tempArray)); - if NS_FAILED(rv) return rv; - while (1) { PRBool hasMore; rv = properties->HasMoreElements(&hasMore); @@ -1865,74 +2285,13 @@ RDFGenericBuilderImpl::CreateContainerContents(nsIContent* aElement, nsIRDFResou rv = targets->GetNext(getter_AddRefs(isupportsNext)); if (NS_FAILED(rv)) return rv; - nsCOMPtr valueResource = do_QueryInterface(isupportsNext); - NS_ASSERTION(valueResource != nsnull, "not a resource"); - if (! valueResource) + nsCOMPtr target = do_QueryInterface(isupportsNext); + NS_ASSERTION(target != nsnull, "not a resource"); + if (! target) continue; - // XXX hack: always append value resource 1st due to sort - // callback implementation - tempArray->AppendElement(valueResource); - tempArray->AppendElement(property); - } - } - - PRUint32 numElements; - rv = tempArray->Count(&numElements); - if (NS_FAILED(rv)) return rv; - - if (numElements == 0) - return NS_OK; - - // Tree widget hackery. To be removed if and when the - // reflow lock is exposed. See bug 10818. - PRBool istree = IsTreeWidgetItem(aElement); - - { - // XXX change to use nsVoidArray? - nsIRDFResource** flatArray = new nsIRDFResource*[numElements]; - if (! flatArray) return NS_ERROR_OUT_OF_MEMORY; - - // flatten array of resources, sort them, then add as item elements - PRUint32 loop; - for (loop=0; loopElementAt(loop); - - if (XULSortService) { - XULSortService->OpenContainer(mDB, aElement, flatArray, numElements/2, 2*sizeof(nsIRDFResource *)); - } - - // This will insert all of the elements into the - // container, but _won't_ bother layout about it. - for (loop=0; loop=0; i--) { - NS_IF_RELEASE(flatArray[i]); - } - - delete [] flatArray; - if (NS_FAILED(rv)) return rv; - } - - if (istree) { - nsCOMPtr doc = do_QueryInterface(mDocument); - if (! doc) - return NS_ERROR_UNEXPECTED; - - nsCOMPtr treechildren; - rv = nsRDFContentUtils::FindChildByTag(aElement, - kNameSpaceID_XUL, - kTreeChildrenAtom, - getter_AddRefs(treechildren)); - - if (NS_SUCCEEDED(rv)) { - // _Now_ tell layout that we've mucked with the container. This'll - // force a reflow and get the content displayed. - rv = doc->ContentAppended(treechildren, 0); + rv = CreateWidgetItem(aElement, property, target, -1, aNotify); + NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create item"); if (NS_FAILED(rv)) return rv; } } @@ -2000,7 +2359,7 @@ RDFGenericBuilderImpl::CreateTemplateContents(nsIContent* aElement, const nsStri element = parent; } - rv = BuildContentFromTemplate(tmpl, aElement, PR_FALSE, resource, -1, PR_TRUE); + rv = BuildContentFromTemplate(tmpl, aElement, PR_FALSE, resource, -1, PR_FALSE); if (NS_FAILED(rv)) return rv; return NS_OK; @@ -2010,6 +2369,7 @@ nsresult RDFGenericBuilderImpl::EnsureElementHasGenericChild(nsIContent* parent, PRInt32 nameSpaceID, nsIAtom* tag, + PRBool aNotify, nsIContent** result) { nsresult rv; @@ -2026,7 +2386,7 @@ RDFGenericBuilderImpl::EnsureElementHasGenericChild(nsIContent* parent, if (NS_FAILED(rv)) return rv; // XXX Note that the notification ensures we won't batch insertions! This could be bad! - Dave - rv = parent->AppendChildTo(element, PR_TRUE); + rv = parent->AppendChildTo(element, aNotify); if (NS_FAILED(rv)) return rv; *result = element; @@ -2381,6 +2741,109 @@ RDFGenericBuilderImpl::GetResource(PRInt32 aNameSpaceID, } +nsresult +RDFGenericBuilderImpl::FindInsertionPoint(nsIContent* aElement, nsIContent** aResult) +{ + nsresult rv; + + // XXX Hack-o-rama. This needs to be fixed to actually grovel over + // the template n' stuff. + nsCOMPtr tag; + rv = aElement->GetTag(*getter_AddRefs(tag)); + if (NS_FAILED(rv)) return rv; + + nsAutoString tagName; + rv = tag->ToString(tagName); + if (NS_FAILED(rv)) return rv; + + if (tagName.Equals("tree") || tagName.Equals("treeitem")) { + rv = nsRDFContentUtils::FindChildByTag(aElement, kNameSpaceID_XUL, NS_NewAtom("treechildren"), aResult); + if (NS_FAILED(rv)) return rv; + } + else if (tagName.Equals("menu")) { + rv = nsRDFContentUtils::FindChildByTag(aElement, kNameSpaceID_XUL, NS_NewAtom("menupopup"), aResult); + if (NS_FAILED(rv)) return rv; + } + else { + *aResult = aElement; + NS_ADDREF(*aResult); + } + + return NS_OK; +} + + +nsresult +RDFGenericBuilderImpl::RemoveGeneratedContent(nsIContent* aElement) +{ + // Remove all the content beneath aElement that has been generated + // from a template. + + nsresult rv; + + PRInt32 count; + rv = aElement->ChildCount(count); + if (NS_FAILED(rv)) return rv; + + while (--count >= 0) { + nsCOMPtr child; + rv = aElement->ChildAt(count, *getter_AddRefs(child)); + if (NS_FAILED(rv)) return rv; + + nsAutoString tmplID; + rv = child->GetAttribute(kNameSpaceID_None, kTemplateAtom, tmplID); + if (NS_FAILED(rv)) return rv; + + if (rv != NS_CONTENT_ATTR_HAS_VALUE) + continue; + + // It's a generated element. Remove it, and set its document + // to null so that it'll get knocked out of the XUL doc's + // resource-to-element map. + rv = aElement->RemoveChildAt(count, PR_TRUE); + NS_ASSERTION(NS_SUCCEEDED(rv), "error removing child"); + + rv = child->SetDocument(nsnull, PR_TRUE); + if (NS_FAILED(rv)) return rv; + } + + return NS_OK; +} + + +nsresult +RDFGenericBuilderImpl::FindFirstGeneratedChild(nsIContent* aElement, PRInt32* aIndex) +{ + // Find the first kid of aElement that has been generated from a + // template. + + nsresult rv; + + PRInt32 count; + rv = aElement->ChildCount(count); + if (NS_FAILED(rv)) return rv; + + PRInt32 i = 0; + while (i < count) { + nsCOMPtr child; + rv = aElement->ChildAt(i, *getter_AddRefs(child)); + if (NS_FAILED(rv)) return rv; + + nsAutoString tmplID; + rv = child->GetAttribute(kNameSpaceID_None, kTemplateAtom, tmplID); + if (NS_FAILED(rv)) return rv; + + if (rv == NS_CONTENT_ATTR_HAS_VALUE) + break; + + ++i; + } + + *aIndex = (i < count) ? i : -1; + return NS_OK; +} + + PRBool RDFGenericBuilderImpl::IsTreeWidgetItem(nsIContent* aElement) { diff --git a/mozilla/rdf/content/src/nsRDFGenericBuilder.h b/mozilla/rdf/content/src/nsRDFGenericBuilder.h deleted file mode 100644 index 173ff4481d6..00000000000 --- a/mozilla/rdf/content/src/nsRDFGenericBuilder.h +++ /dev/null @@ -1,274 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * - * The contents of this file are subject to the Netscape Public License - * Version 1.0 (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/NPL/ - * - * 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 Netscape are Copyright (C) 1998 - * Netscape Communications Corporation. All Rights Reserved. - */ - -/* - - An implementation that builds a XUL - content model that is to be used with a certain widget. This class - is abstract. - */ - -#include "nsIRDFContainerUtils.h" -#include "nsIRDFContentModelBuilder.h" -#include "nsIRDFObserver.h" -#include "nsITimer.h" -#include "nsIXULSortService.h" - -class nsIRDFDocument; -class nsIRDFCompositeDataSource; -class nsIRDFResource; -class nsIContent; -class nsIRDFNode; -class nsIAtom; -class nsIRDFService; -class nsIHTMLElementFactory; - -class RDFGenericBuilderImpl : public nsIRDFContentModelBuilder, - public nsIRDFObserver -{ -public: - RDFGenericBuilderImpl(); - virtual ~RDFGenericBuilderImpl(); - - nsresult Init(); - - // nsISupports interface - NS_DECL_ISUPPORTS - - // nsIRDFContentModelBuilder interface - NS_IMETHOD SetDocument(nsIRDFDocument* aDocument); - NS_IMETHOD SetDataBase(nsIRDFCompositeDataSource* aDataBase); - NS_IMETHOD GetDataBase(nsIRDFCompositeDataSource** aDataBase); - NS_IMETHOD CreateRootContent(nsIRDFResource* aResource); - NS_IMETHOD SetRootContent(nsIContent* aElement); - NS_IMETHOD CreateContents(nsIContent* aElement); - NS_IMETHOD CreateElement(PRInt32 aNameSpaceID, - nsIAtom* aTag, - nsIRDFResource* aResource, - nsIContent** aResult); - - // nsIRDFObserver interface - NS_IMETHOD OnAssert(nsIRDFResource* aSource, - nsIRDFResource* aProperty, - nsIRDFNode* aTarget); - - NS_IMETHOD OnUnassert(nsIRDFResource* aSource, - nsIRDFResource* aProperty, - nsIRDFNode* aTarget); - - NS_IMETHOD OnChange(nsIRDFResource* aSource, - nsIRDFResource* aProperty, - nsIRDFNode* aOldTarget, - nsIRDFNode* aNewTarget); - - NS_IMETHOD OnMove(nsIRDFResource* aOldSource, - nsIRDFResource* aNewSource, - nsIRDFResource* aProperty, - nsIRDFNode* aTarget); - - // Implementation methods - nsresult - FindTemplate(nsIContent* aElement, - nsIRDFResource* aProperty, - nsIRDFResource* aChild, - nsIContent **theTemplate); - - nsresult - IsTemplateRuleMatch(nsIContent* aElement, - nsIRDFResource* aProperty, - nsIRDFResource* aChild, - nsIContent *aRule, - PRBool *isMatch); - - PRBool - IsIgnoreableAttribute(PRInt32 aNameSpaceID, nsIAtom* aAtom); - - nsresult - GetSubstitutionText(nsIRDFResource* aResource, - const nsString& aSubstitution, - nsString& aResult); - - nsresult - BuildContentFromTemplate(nsIContent *aTemplateNode, - nsIContent *aRealNode, - PRBool aIsUnique, - nsIRDFResource* aChild, - PRInt32 aNaturalOrderPos, - PRBool aNotify); - - nsresult - CreateWidgetItem(nsIContent* aElement, - nsIRDFResource* aProperty, - nsIRDFResource* aChild, - PRInt32 aNaturalOrderPos, - PRBool aNotify); - - enum eUpdateAction { eSet, eClear }; - -#if 0 - PRBool - IsAttributePersisent(nsIContent *element, PRInt32 aNameSpaceID, nsIAtom* aAtom); - - void - GetPersistentAttributes(nsIContent *realKid); - - void - PersistAttribute(nsIContent *element, - PRInt32 aNameSpaceID, - nsIAtom* aAtom, - nsString aValue, - eUpdateAction action); - - void - PersistProperty(nsIContent *element, - nsIRDFResource *aProperty, - nsIRDFNode *aTarget, - eUpdateAction action); -#endif - - nsresult - SynchronizeUsingTemplate(nsIContent *aTemplateNode, - nsIContent* aRealNode, - eUpdateAction aAction, - nsIRDFResource* aProperty, - nsIRDFNode* aValue); - - nsresult - RemoveWidgetItem(nsIContent* aElement, - nsIRDFResource* aProperty, - nsIRDFResource* aValue, - PRBool aNotify); - - nsresult - CreateContainerContents(nsIContent* aElement, nsIRDFResource* aResource); - - nsresult - CreateTemplateContents(nsIContent* aElement, const nsString& aTemplateID); - - nsresult - EnsureElementHasGenericChild(nsIContent* aParent, - PRInt32 aNameSpaceID, - nsIAtom* aTag, - nsIContent** aResult); - - PRBool - IsContainmentProperty(nsIContent* aElement, nsIRDFResource* aProperty); - - PRBool - IsIgnoredProperty(nsIContent* aElement, nsIRDFResource* aProperty); - - PRBool - IsContainer(nsIContent* aParentElement, nsIRDFResource* aTargetResource); - - PRBool - IsEmpty(nsIContent* aParentElement, nsIRDFResource* aContainer); - - PRBool - IsOpen(nsIContent* aElement); - - PRBool - IsElementInWidget(nsIContent* aElement); - - PRBool - IsResourceElement(nsIContent* aElement); - - nsresult - GetDOMNodeResource(nsIDOMNode* aNode, nsIRDFResource** aResource); - - nsresult - GetResource(PRInt32 aNameSpaceID, - nsIAtom* aNameAtom, - nsIRDFResource** aResource); - - // XXX. Urg. Hack until layout can batch reflows. See bug 10818. - PRBool - IsTreeWidgetItem(nsIContent* aElement); - - PRBool - IsReflowScheduled(); - - nsresult - ScheduleReflow(); - - static void - ForceTreeReflow(nsITimer* aTimer, void* aClosure); - -protected: - nsIRDFDocument* mDocument; // [WEAK] - - // We are an observer of the composite datasource. The cycle is - // broken by out-of-band SetDataBase(nsnull) call when document is - // destroyed. - nsCOMPtr mDB; - nsCOMPtr mRoot; - - nsCOMPtr mTimer; - - static nsIRDFDataSource *mLocalstore; - static PRBool persistLock; - - // pseudo-constants - static nsrefcnt gRefCnt; - static nsIRDFService* gRDFService; - static nsIRDFContainerUtils* gRDFContainerUtils; - static nsINameSpaceManager* gNameSpaceManager; - static nsIHTMLElementFactory* gHTMLElementFactory; - - static nsIAtom* kContainerAtom; - static nsIAtom* kLazyContentAtom; - static nsIAtom* kIsContainerAtom; - static nsIAtom* kIsEmptyAtom; - static nsIAtom* kXULContentsGeneratedAtom; - static nsIAtom* kTemplateContentsGeneratedAtom; - static nsIAtom* kContainerContentsGeneratedAtom; - static nsIAtom* kNaturalOrderPosAtom; - static nsIAtom* kIdAtom; - static nsIAtom* kPersistAtom; - static nsIAtom* kOpenAtom; - static nsIAtom* kEmptyAtom; - static nsIAtom* kResourceAtom; - static nsIAtom* kURIAtom; - static nsIAtom* kContainmentAtom; - static nsIAtom* kIgnoreAtom; - static nsIAtom* kRefAtom; - static nsIAtom* kValueAtom; - - static nsIAtom* kTemplateAtom; - static nsIAtom* kRuleAtom; - static nsIAtom* kTextAtom; - static nsIAtom* kPropertyAtom; - static nsIAtom* kInstanceOfAtom; - - static nsIAtom* kTreeAtom; - static nsIAtom* kTreeChildrenAtom; - static nsIAtom* kTreeItemAtom; - - static PRInt32 kNameSpaceID_RDF; - static PRInt32 kNameSpaceID_XUL; - - static nsIRDFResource* kNC_Title; - static nsIRDFResource* kNC_child; - static nsIRDFResource* kNC_Column; - static nsIRDFResource* kNC_Folder; - static nsIRDFResource* kRDF_child; - static nsIRDFResource* kRDF_instanceOf; - static nsIRDFResource* kXUL_element; - - static nsIXULSortService *XULSortService; -}; diff --git a/mozilla/rdf/content/src/nsRDFXULBuilder.cpp b/mozilla/rdf/content/src/nsRDFXULBuilder.cpp index 4745c361589..c3f023678ee 100644 --- a/mozilla/rdf/content/src/nsRDFXULBuilder.cpp +++ b/mozilla/rdf/content/src/nsRDFXULBuilder.cpp @@ -188,8 +188,6 @@ private: static nsIAtom* kDataSourcesAtom; static nsIAtom* kIdAtom; static nsIAtom* kInstanceOfAtom; - static nsIAtom* kTemplateContentsGeneratedAtom; - static nsIAtom* kContainerContentsGeneratedAtom; static nsIAtom* kMenuAtom; static nsIAtom* kMenuBarAtom; static nsIAtom* kKeysetAtom; @@ -225,6 +223,9 @@ public: NS_IMETHOD CreateRootContent(nsIRDFResource* aResource); NS_IMETHOD SetRootContent(nsIContent* aResource); NS_IMETHOD CreateContents(nsIContent* aElement); + NS_IMETHOD OpenContainer(nsIContent* aElement); + NS_IMETHOD CloseContainer(nsIContent* aElement); + NS_IMETHOD RebuildContainer(nsIContent* aElement); NS_IMETHOD CreateElement(PRInt32 aNameSpaceID, nsIAtom* aTagName, nsIRDFResource* aResource, @@ -342,8 +343,6 @@ nsIAtom* RDFXULBuilderImpl::kLazyContentAtom; nsIAtom* RDFXULBuilderImpl::kDataSourcesAtom; nsIAtom* RDFXULBuilderImpl::kIdAtom; nsIAtom* RDFXULBuilderImpl::kInstanceOfAtom; -nsIAtom* RDFXULBuilderImpl::kTemplateContentsGeneratedAtom; -nsIAtom* RDFXULBuilderImpl::kContainerContentsGeneratedAtom; nsIAtom* RDFXULBuilderImpl::kMenuAtom; nsIAtom* RDFXULBuilderImpl::kMenuBarAtom; nsIAtom* RDFXULBuilderImpl::kKeysetAtom; @@ -418,8 +417,6 @@ RDFXULBuilderImpl::Init() kDataSourcesAtom = NS_NewAtom("datasources"); kIdAtom = NS_NewAtom("id"); kInstanceOfAtom = NS_NewAtom("instanceof"); - kTemplateContentsGeneratedAtom = NS_NewAtom("itemcontentsgenerated"); - kContainerContentsGeneratedAtom = NS_NewAtom("containercontentsgenerated"); kMenuAtom = NS_NewAtom("menu"); kMenuBarAtom = NS_NewAtom("menubar"); kKeysetAtom = NS_NewAtom("keyset"); @@ -491,8 +488,6 @@ RDFXULBuilderImpl::~RDFXULBuilderImpl(void) NS_IF_RELEASE(kLazyContentAtom); NS_IF_RELEASE(kXULContentsGeneratedAtom); NS_IF_RELEASE(kIdAtom); - NS_IF_RELEASE(kTemplateContentsGeneratedAtom); - NS_IF_RELEASE(kContainerContentsGeneratedAtom); NS_IF_RELEASE(kDataSourcesAtom); NS_IF_RELEASE(kTreeAtom); NS_IF_RELEASE(kMenuAtom); @@ -749,6 +744,27 @@ RDFXULBuilderImpl::CreateContents(nsIContent* aElement) } +NS_IMETHODIMP +RDFXULBuilderImpl::OpenContainer(nsIContent* aContainer) +{ + return NS_OK; +} + + +NS_IMETHODIMP +RDFXULBuilderImpl::CloseContainer(nsIContent* aContainer) +{ + return NS_OK; +} + + +NS_IMETHODIMP +RDFXULBuilderImpl::RebuildContainer(nsIContent* aContainer) +{ + return NS_OK; +} + + NS_IMETHODIMP RDFXULBuilderImpl::CreateElement(PRInt32 aNameSpaceID, nsIAtom* aTagName, @@ -1318,7 +1334,7 @@ RDFXULBuilderImpl::InsertChildAt(nsINameSpace* aNameSpace, } #endif - rv = aElement->InsertChildAt(child, aIndex, PR_TRUE); + rv = aElement->InsertChildAt(child, aIndex, PR_FALSE); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to add element to content model"); if (NS_FAILED(rv)) return rv; } @@ -1398,7 +1414,7 @@ RDFXULBuilderImpl::ReplaceChildAt(nsINameSpace* aNameSpace, } #endif - rv = aElement->ReplaceChildAt(child, aIndex, PR_TRUE); + rv = aElement->ReplaceChildAt(child, aIndex, PR_FALSE); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to add element to content model"); if (NS_FAILED(rv)) return rv; } diff --git a/mozilla/rdf/content/src/nsXULDocument.cpp b/mozilla/rdf/content/src/nsXULDocument.cpp index 3295fb4839c..204122f41c9 100644 --- a/mozilla/rdf/content/src/nsXULDocument.cpp +++ b/mozilla/rdf/content/src/nsXULDocument.cpp @@ -802,7 +802,7 @@ public: nsresult OpenWidgetItem(nsIContent* aElement); nsresult CloseWidgetItem(nsIContent* aElement); - nsresult RemoveAndRebuildGeneratedChildren(nsIContent* aElement); + nsresult RebuildWidgetItem(nsIContent* aElement); nsresult AddElementToMap(nsIContent* aElement, PRBool aDeep); @@ -2101,14 +2101,14 @@ XULDocumentImpl::ContentStatesChanged(nsIContent* aContent1, nsIContent* aConten } NS_IMETHODIMP -XULDocumentImpl::AttributeChanged(nsIContent* aChild, +XULDocumentImpl::AttributeChanged(nsIContent* aElement, nsIAtom* aAttribute, PRInt32 aHint) { nsresult rv; PRInt32 nameSpaceID; - rv = aChild->GetNameSpaceID(nameSpaceID); + rv = aElement->GetNameSpaceID(nameSpaceID); if (NS_FAILED(rv)) return rv; // First see if we need to update our element map. @@ -2120,27 +2120,7 @@ XULDocumentImpl::AttributeChanged(nsIContent* aChild, // That'll have removed _both_ the 'ref' and 'id' entries from // the map. So add 'em back now. - rv = AddElementToMap(aChild, PR_FALSE); - if (NS_FAILED(rv)) return rv; - } - } - - // Handle "special" cases. - if (nameSpaceID == kNameSpaceID_XUL) { - if (aAttribute == kOpenAtom) { - nsAutoString open; - rv = aChild->GetAttribute(kNameSpaceID_None, kOpenAtom, open); - if (NS_FAILED(rv)) return rv; - - if ((rv == NS_CONTENT_ATTR_HAS_VALUE) && (open.Equals("true"))) { - OpenWidgetItem(aChild); - } - else { - CloseWidgetItem(aChild); - } - } - else if (aAttribute == kRefAtom) { - rv = RemoveAndRebuildGeneratedChildren(aChild); + rv = AddElementToMap(aElement, PR_FALSE); if (NS_FAILED(rv)) return rv; } } @@ -2148,11 +2128,34 @@ XULDocumentImpl::AttributeChanged(nsIContent* aChild, // Now notify external observers for (PRInt32 i = 0; i < mObservers.Count(); i++) { nsIDocumentObserver* observer = (nsIDocumentObserver*)mObservers[i]; - observer->AttributeChanged(this, aChild, aAttribute, aHint); + observer->AttributeChanged(this, aElement, aAttribute, aHint); if (observer != (nsIDocumentObserver*)mObservers.ElementAt(i)) { i--; } } + + // Handle "special" cases. We do this handling _after_ we've + // notified the observer to ensure that any frames that are + // caching information (e.g., the tree widget and the 'open' + // attribute) will notice things properly. + if (nameSpaceID == kNameSpaceID_XUL) { + if (aAttribute == kOpenAtom) { + nsAutoString open; + rv = aElement->GetAttribute(kNameSpaceID_None, kOpenAtom, open); + if (NS_FAILED(rv)) return rv; + + if ((rv == NS_CONTENT_ATTR_HAS_VALUE) && (open.Equals("true"))) { + OpenWidgetItem(aElement); + } + else { + CloseWidgetItem(aElement); + } + } + else if (aAttribute == kRefAtom) { + RebuildWidgetItem(aElement); + } + } + return NS_OK; } @@ -4483,10 +4486,17 @@ XULDocumentImpl::ReleaseEvent(const nsString& aType) nsresult XULDocumentImpl::OpenWidgetItem(nsIContent* aElement) { + NS_PRECONDITION(aElement != nsnull, "null ptr"); + if (! aElement) + return NS_ERROR_NULL_POINTER; + + if (! mBuilders) + return NS_ERROR_NOT_INITIALIZED; + + nsresult rv; + #ifdef PR_LOGGING if (PR_LOG_TEST(gXULLog, PR_LOG_DEBUG)) { - nsresult rv; - nsCOMPtr tag; rv = aElement->GetTag(*getter_AddRefs(tag)); if (NS_FAILED(rv)) return rv; @@ -4499,12 +4509,39 @@ XULDocumentImpl::OpenWidgetItem(nsIContent* aElement) (const char*) nsCAutoString(tagStr))); } #endif - return CreateContents(aElement); + + PRUint32 cnt = 0; + rv = mBuilders->Count(&cnt); + NS_ASSERTION(NS_SUCCEEDED(rv), "Count failed"); + for (PRUint32 i = 0; i < cnt; ++i) { + // XXX we should QueryInterface() here + nsIRDFContentModelBuilder* builder + = (nsIRDFContentModelBuilder*) mBuilders->ElementAt(i); + + NS_ASSERTION(builder != nsnull, "null ptr"); + if (! builder) + continue; + + rv = builder->OpenContainer(aElement); + NS_ASSERTION(NS_SUCCEEDED(rv), "error opening container"); + // XXX ignore error code? + + NS_RELEASE(builder); + } + + return NS_OK; } nsresult XULDocumentImpl::CloseWidgetItem(nsIContent* aElement) { + NS_PRECONDITION(aElement != nsnull, "null ptr"); + if (! aElement) + return NS_ERROR_NULL_POINTER; + + if (! mBuilders) + return NS_ERROR_NOT_INITIALIZED; + nsresult rv; #ifdef PR_LOGGING @@ -4522,163 +4559,78 @@ XULDocumentImpl::CloseWidgetItem(nsIContent* aElement) } #endif - // Find the tag that contains the children so that we can remove - // all of the children. - // - // XXX We make a bit of a leap here and assume that the same - // template that was used to generate _us_ was used to generate - // our _kids_. I'm sure this'll break when we do toolbars or - // something. - nsAutoString tmplID; - rv = aElement->GetAttribute(kNameSpaceID_None, kTemplateAtom, tmplID); - if (NS_FAILED(rv)) return rv; + PRUint32 cnt = 0; + rv = mBuilders->Count(&cnt); + NS_ASSERTION(NS_SUCCEEDED(rv), "Count failed"); + for (PRUint32 i = 0; i < cnt; ++i) { + // XXX we should QueryInterface() here + nsIRDFContentModelBuilder* builder + = (nsIRDFContentModelBuilder*) mBuilders->ElementAt(i); - if (rv != NS_CONTENT_ATTR_HAS_VALUE) - return NS_OK; + NS_ASSERTION(builder != nsnull, "null ptr"); + if (! builder) + continue; - nsCOMPtr tmplDOMEle; - rv = GetElementById(tmplID, getter_AddRefs(tmplDOMEle)); - if (NS_FAILED(rv)) return rv; + rv = builder->CloseContainer(aElement); + NS_ASSERTION(NS_SUCCEEDED(rv), "error closing container"); + // XXX ignore error code? - nsCOMPtr tmpl = do_QueryInterface(tmplDOMEle); - if (! tmpl) - return NS_ERROR_UNEXPECTED; - - nsCOMPtr tmplParent; - rv = tmpl->GetParent(*getter_AddRefs(tmplParent)); - if (NS_FAILED(rv)) return rv; - - NS_ASSERTION(tmplParent != nsnull, "template node has no parent"); - if (! tmplParent) - return NS_ERROR_UNEXPECTED; - - nsCOMPtr tmplParentTag; - rv = tmplParent->GetTag(*getter_AddRefs(tmplParentTag)); - if (NS_FAILED(rv)) return rv; - - nsCOMPtr childcontainer; - if ((tmplParentTag.get() == kRuleAtom) || (tmplParentTag.get() == kTemplateAtom)) { - childcontainer = dont_QueryInterface(aElement); + NS_RELEASE(builder); } - else { - rv = nsRDFContentUtils::FindChildByTag(aElement, - kNameSpaceID_XUL, - tmplParentTag, - getter_AddRefs(childcontainer)); - - if (NS_FAILED(rv)) return rv; - - if (rv == NS_RDF_NO_VALUE) { - // No tag; must've already been closed - return NS_OK; - } - } - - PRInt32 count; - rv = childcontainer->ChildCount(count); - NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get count of the parent's children"); - if (NS_FAILED(rv)) return rv; - - while (--count >= 0) { - nsCOMPtr child; - rv = childcontainer->ChildAt(count, *getter_AddRefs(child)); - if (NS_FAILED(rv)) return rv; - - rv = childcontainer->RemoveChildAt(count, PR_TRUE); - NS_ASSERTION(NS_SUCCEEDED(rv), "error removing child"); - - do { - // If it's _not_ a XUL element, then we want to blow it and - // all of its kids out of the XUL document's - // resource-to-element map. - nsCOMPtr resource; - rv = nsRDFContentUtils::GetElementResource(child, getter_AddRefs(resource)); - if (NS_FAILED(rv)) break; - - PRBool isXULElement; - rv = mDocumentDataSource->HasAssertion(resource, kRDF_instanceOf, kXUL_element, PR_TRUE, &isXULElement); - if (NS_FAILED(rv)) break; - - if (! isXULElement) - break; - - rv = child->SetDocument(nsnull, PR_TRUE); - if (NS_FAILED(rv)) return rv; - } while (0); - } - - // Clear the container-contents-generated attribute so that the next time we - // come back, we'll regenerate the kids we just killed. - rv = aElement->UnsetAttribute(kNameSpaceID_None, - kContainerContentsGeneratedAtom, - PR_FALSE); - if (NS_FAILED(rv)) return rv; - - // This is a _total_ hack to make sure that any XUL we blow away - // gets rebuilt. - rv = childcontainer->UnsetAttribute(kNameSpaceID_None, - kXULContentsGeneratedAtom, - PR_FALSE); - if (NS_FAILED(rv)) return rv; - - rv = childcontainer->SetAttribute(kNameSpaceID_None, - kLazyContentAtom, - "true", - PR_FALSE); - if (NS_FAILED(rv)) return rv; return NS_OK; } nsresult -XULDocumentImpl::RemoveAndRebuildGeneratedChildren(nsIContent* aElement) +XULDocumentImpl::RebuildWidgetItem(nsIContent* aElement) { + NS_PRECONDITION(aElement != nsnull, "null ptr"); + if (! aElement) + return NS_ERROR_NULL_POINTER; + + if (! mBuilders) + return NS_ERROR_NOT_INITIALIZED; + nsresult rv; - PRInt32 count; - rv = aElement->ChildCount(count); - if (NS_FAILED(rv)) return rv; - - while (--count >= 0) { - nsCOMPtr child; - rv = aElement->ChildAt(count, *getter_AddRefs(child)); +#ifdef PR_LOGGING + if (PR_LOG_TEST(gXULLog, PR_LOG_DEBUG)) { + nsCOMPtr tag; + rv = aElement->GetTag(*getter_AddRefs(tag)); if (NS_FAILED(rv)) return rv; - nsAutoString tmplID; - rv = child->GetAttribute(kNameSpaceID_None, kTemplateAtom, tmplID); - if (NS_FAILED(rv)) return rv; + nsAutoString tagStr; + tag->ToString(tagStr); - if (rv != NS_CONTENT_ATTR_HAS_VALUE) + PR_LOG(gXULLog, PR_LOG_DEBUG, + ("xuldoc close-widget-item %s", + (const char*) nsCAutoString(tagStr))); + } +#endif + + PRUint32 cnt = 0; + rv = mBuilders->Count(&cnt); + NS_ASSERTION(NS_SUCCEEDED(rv), "Count failed"); + for (PRUint32 i = 0; i < cnt; ++i) { + // XXX we should QueryInterface() here + nsIRDFContentModelBuilder* builder + = (nsIRDFContentModelBuilder*) mBuilders->ElementAt(i); + + NS_ASSERTION(builder != nsnull, "null ptr"); + if (! builder) continue; - // It's a generated element. Remove it, and set its document - // to null so that it'll get knocked out of the XUL doc's - // resource-to-element map. - rv = aElement->RemoveChildAt(count, PR_TRUE); - NS_ASSERTION(NS_SUCCEEDED(rv), "error removing child"); + rv = builder->RebuildContainer(aElement); + NS_ASSERTION(NS_SUCCEEDED(rv), "error rebuilding container"); + // XXX ignore error code? - rv = child->SetDocument(nsnull, PR_TRUE); - if (NS_FAILED(rv)) return rv; + NS_RELEASE(builder); } - // Clear the contents-generated attribute so that the next time we - // come back, we'll regenerate the kids we just killed. - rv = aElement->UnsetAttribute(kNameSpaceID_None, - kTemplateContentsGeneratedAtom, - PR_FALSE); - if (NS_FAILED(rv)) return rv; - - rv = aElement->UnsetAttribute(kNameSpaceID_None, - kContainerContentsGeneratedAtom, - PR_FALSE); - if (NS_FAILED(rv)) return rv; - - rv = CreateContents(aElement); - if (NS_FAILED(rv)) return rv; - return NS_OK; } + diff --git a/mozilla/rdf/content/src/nsXULTemplateBuilder.cpp b/mozilla/rdf/content/src/nsXULTemplateBuilder.cpp index 0b22cc91838..cffb60dd5f7 100644 --- a/mozilla/rdf/content/src/nsXULTemplateBuilder.cpp +++ b/mozilla/rdf/content/src/nsXULTemplateBuilder.cpp @@ -41,38 +41,36 @@ #include "nsIDOMElement.h" #include "nsIDOMNode.h" #include "nsIDOMXULDocument.h" +#include "nsIDOMXULElement.h" #include "nsIDocument.h" +#include "nsIHTMLContent.h" +#include "nsIHTMLElementFactory.h" #include "nsINameSpaceManager.h" -#include "nsIRDFContentModelBuilder.h" #include "nsIRDFCompositeDataSource.h" +#include "nsIRDFContainerUtils.h" +#include "nsIRDFContentModelBuilder.h" #include "nsIRDFDocument.h" #include "nsIRDFNode.h" #include "nsIRDFObserver.h" +#include "nsIRDFRemoteDataSource.h" #include "nsIRDFService.h" #include "nsIServiceManager.h" -#include "nsINameSpaceManager.h" -#include "nsIServiceManager.h" #include "nsISupportsArray.h" #include "nsITextContent.h" +#include "nsITimer.h" #include "nsIURL.h" +#include "nsIXULSortService.h" #include "nsLayoutCID.h" #include "nsRDFCID.h" #include "nsRDFContentUtils.h" #include "nsString.h" +#include "nsVoidArray.h" #include "nsXPIDLString.h" +#include "prlog.h" #include "rdf.h" #include "rdfutil.h" -#include "nsITimer.h" -#include "nsIDOMXULElement.h" -#include "nsVoidArray.h" -#include "nsIXULSortService.h" -#include "nsIHTMLElementFactory.h" -#include "nsIHTMLContent.h" -#include "nsRDFGenericBuilder.h" -#include "prlog.h" -#include "nsIRDFRemoteDataSource.h" - +// Return values for EnsureElementHasGenericChild() #define NS_RDF_ELEMENT_WAS_CREATED NS_RDF_NO_VALUE #define NS_RDF_ELEMENT_WAS_THERE NS_OK @@ -107,8 +105,252 @@ static NS_DEFINE_CID(kIHTMLElementFactoryIID, NS_IHTML_ELEMENT_FACTORY_IID); //////////////////////////////////////////////////////////////////////// +class RDFGenericBuilderImpl : public nsIRDFContentModelBuilder, + public nsIRDFObserver +{ +public: + RDFGenericBuilderImpl(); + virtual ~RDFGenericBuilderImpl(); + + nsresult Init(); + + // nsISupports interface + NS_DECL_ISUPPORTS + + // nsIRDFContentModelBuilder interface + NS_IMETHOD SetDocument(nsIRDFDocument* aDocument); + NS_IMETHOD SetDataBase(nsIRDFCompositeDataSource* aDataBase); + NS_IMETHOD GetDataBase(nsIRDFCompositeDataSource** aDataBase); + NS_IMETHOD CreateRootContent(nsIRDFResource* aResource); + NS_IMETHOD SetRootContent(nsIContent* aElement); + NS_IMETHOD CreateContents(nsIContent* aElement); + NS_IMETHOD OpenContainer(nsIContent* aContainer); + NS_IMETHOD CloseContainer(nsIContent* aContainer); + NS_IMETHOD RebuildContainer(nsIContent* aContainer); + NS_IMETHOD CreateElement(PRInt32 aNameSpaceID, + nsIAtom* aTag, + nsIRDFResource* aResource, + nsIContent** aResult); + + // nsIRDFObserver interface + NS_IMETHOD OnAssert(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget); + + NS_IMETHOD OnUnassert(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget); + + NS_IMETHOD OnChange(nsIRDFResource* aSource, + nsIRDFResource* aProperty, + nsIRDFNode* aOldTarget, + nsIRDFNode* aNewTarget); + + NS_IMETHOD OnMove(nsIRDFResource* aOldSource, + nsIRDFResource* aNewSource, + nsIRDFResource* aProperty, + nsIRDFNode* aTarget); + + // Implementation methods + nsresult + FindTemplate(nsIContent* aElement, + nsIRDFResource* aProperty, + nsIRDFResource* aChild, + nsIContent **theTemplate); + + nsresult + IsTemplateRuleMatch(nsIContent* aElement, + nsIRDFResource* aProperty, + nsIRDFResource* aChild, + nsIContent *aRule, + PRBool *isMatch); + + PRBool + IsIgnoreableAttribute(PRInt32 aNameSpaceID, nsIAtom* aAtom); + + nsresult + GetSubstitutionText(nsIRDFResource* aResource, + const nsString& aSubstitution, + nsString& aResult); + + nsresult + BuildContentFromTemplate(nsIContent *aTemplateNode, + nsIContent *aRealNode, + PRBool aIsUnique, + nsIRDFResource* aChild, + PRInt32 aNaturalOrderPos, + PRBool aNotify); + + nsresult + CreateWidgetItem(nsIContent* aElement, + nsIRDFResource* aProperty, + nsIRDFResource* aChild, + PRInt32 aNaturalOrderPos, + PRBool aNotify); + + enum eUpdateAction { eSet, eClear }; + +#if 0 + PRBool + IsAttributePersisent(nsIContent *element, PRInt32 aNameSpaceID, nsIAtom* aAtom); + + void + GetPersistentAttributes(nsIContent *realKid); + + void + PersistAttribute(nsIContent *element, + PRInt32 aNameSpaceID, + nsIAtom* aAtom, + nsString aValue, + eUpdateAction action); + + void + PersistProperty(nsIContent *element, + nsIRDFResource *aProperty, + nsIRDFNode *aTarget, + eUpdateAction action); +#endif + + nsresult + SynchronizeUsingTemplate(nsIContent *aTemplateNode, + nsIContent* aRealNode, + eUpdateAction aAction, + nsIRDFResource* aProperty, + nsIRDFNode* aValue); + + nsresult + RemoveWidgetItem(nsIContent* aElement, + nsIRDFResource* aProperty, + nsIRDFResource* aValue, + PRBool aNotify); + + nsresult + CreateContainerContents(nsIContent* aElement, nsIRDFResource* aResource, PRBool aNotify); + + nsresult + CreateTemplateContents(nsIContent* aElement, const nsString& aTemplateID); + + nsresult + EnsureElementHasGenericChild(nsIContent* aParent, + PRInt32 aNameSpaceID, + nsIAtom* aTag, + PRBool aNotify, + nsIContent** aResult); + + PRBool + IsContainmentProperty(nsIContent* aElement, nsIRDFResource* aProperty); + + PRBool + IsIgnoredProperty(nsIContent* aElement, nsIRDFResource* aProperty); + + PRBool + IsContainer(nsIContent* aParentElement, nsIRDFResource* aTargetResource); + + PRBool + IsEmpty(nsIContent* aParentElement, nsIRDFResource* aContainer); + + PRBool + IsOpen(nsIContent* aElement); + + PRBool + IsElementInWidget(nsIContent* aElement); + + PRBool + IsResourceElement(nsIContent* aElement); + + nsresult + GetDOMNodeResource(nsIDOMNode* aNode, nsIRDFResource** aResource); + + nsresult + GetResource(PRInt32 aNameSpaceID, + nsIAtom* aNameAtom, + nsIRDFResource** aResource); + + nsresult FindInsertionPoint(nsIContent* aElement, nsIContent** aResult); + nsresult RemoveGeneratedContent(nsIContent* aElement); + nsresult FindFirstGeneratedChild(nsIContent* aElement, PRInt32* aIndex); + + // XXX. Urg. Hack until layout can batch reflows. See bug 10818. + PRBool + IsTreeWidgetItem(nsIContent* aElement); + + PRBool + IsReflowScheduled(); + + nsresult + ScheduleReflow(); + + static void + ForceTreeReflow(nsITimer* aTimer, void* aClosure); + +protected: + nsIRDFDocument* mDocument; // [WEAK] + + // We are an observer of the composite datasource. The cycle is + // broken by out-of-band SetDataBase(nsnull) call when document is + // destroyed. + nsCOMPtr mDB; + nsCOMPtr mRoot; + + nsCOMPtr mTimer; + + static nsIRDFDataSource *mLocalstore; + static PRBool persistLock; + + // pseudo-constants + static nsrefcnt gRefCnt; + static nsIRDFService* gRDFService; + static nsIRDFContainerUtils* gRDFContainerUtils; + static nsINameSpaceManager* gNameSpaceManager; + static nsIHTMLElementFactory* gHTMLElementFactory; + + static nsIAtom* kContainerAtom; + static nsIAtom* kLazyContentAtom; + static nsIAtom* kIsContainerAtom; + static nsIAtom* kIsEmptyAtom; + static nsIAtom* kXULContentsGeneratedAtom; + static nsIAtom* kTemplateContentsGeneratedAtom; + static nsIAtom* kContainerContentsGeneratedAtom; + static nsIAtom* kNaturalOrderPosAtom; + static nsIAtom* kIdAtom; + static nsIAtom* kPersistAtom; + static nsIAtom* kOpenAtom; + static nsIAtom* kEmptyAtom; + static nsIAtom* kResourceAtom; + static nsIAtom* kURIAtom; + static nsIAtom* kContainmentAtom; + static nsIAtom* kIgnoreAtom; + static nsIAtom* kRefAtom; + static nsIAtom* kValueAtom; + + static nsIAtom* kTemplateAtom; + static nsIAtom* kRuleAtom; + static nsIAtom* kTextAtom; + static nsIAtom* kPropertyAtom; + static nsIAtom* kInstanceOfAtom; + + static nsIAtom* kTreeAtom; + static nsIAtom* kTreeChildrenAtom; + static nsIAtom* kTreeItemAtom; + + static PRInt32 kNameSpaceID_RDF; + static PRInt32 kNameSpaceID_XUL; + + static nsIRDFResource* kNC_Title; + static nsIRDFResource* kNC_child; + static nsIRDFResource* kNC_Column; + static nsIRDFResource* kNC_Folder; + static nsIRDFResource* kRDF_child; + static nsIRDFResource* kRDF_instanceOf; + static nsIRDFResource* kXUL_element; + + static nsIXULSortService* gXULSortService; +}; + +//////////////////////////////////////////////////////////////////////// + nsrefcnt RDFGenericBuilderImpl::gRefCnt = 0; -nsIXULSortService* RDFGenericBuilderImpl::XULSortService = nsnull; +nsIXULSortService* RDFGenericBuilderImpl::gXULSortService = nsnull; nsIAtom* RDFGenericBuilderImpl::kContainerAtom; nsIAtom* RDFGenericBuilderImpl::kLazyContentAtom; @@ -252,7 +494,7 @@ RDFGenericBuilderImpl::~RDFGenericBuilderImpl(void) nsServiceManager::ReleaseService(kRDFServiceCID, gRDFService); nsServiceManager::ReleaseService(kRDFContainerUtilsCID, gRDFContainerUtils); - nsServiceManager::ReleaseService(kXULSortServiceCID, XULSortService); + nsServiceManager::ReleaseService(kXULSortServiceCID, gXULSortService); NS_RELEASE(gNameSpaceManager); NS_IF_RELEASE(gHTMLElementFactory); } @@ -351,7 +593,7 @@ RDFGenericBuilderImpl::Init() rv = nsServiceManager::GetService(kXULSortServiceCID, kIXULSortServiceIID, - (nsISupports**) &XULSortService); + (nsISupports**) &gXULSortService); if (NS_FAILED(rv)) return rv; rv = nsComponentManager::CreateInstance(kHTMLElementFactoryCID, @@ -475,19 +717,23 @@ RDFGenericBuilderImpl::SetRootContent(nsIContent* aElement) NS_IMETHODIMP RDFGenericBuilderImpl::CreateContents(nsIContent* aElement) { - nsresult rv; + NS_PRECONDITION(aElement != nsnull, "null ptr"); + if (! aElement) + return NS_ERROR_NULL_POINTER; // First, make sure that the element is in the right widget -- ours. if (!IsElementInWidget(aElement)) return NS_OK; + nsresult rv; + nsCOMPtr resource; rv = nsRDFContentUtils::GetElementRefResource(aElement, getter_AddRefs(resource)); if (NS_SUCCEEDED(rv)) { // The element has a resource; that means that it corresponds // to something in the graph, so we need to go to the graph to // create its contents. - rv = CreateContainerContents(aElement, resource); + rv = CreateContainerContents(aElement, resource, PR_FALSE); if (NS_FAILED(rv)) return rv; } @@ -505,6 +751,192 @@ RDFGenericBuilderImpl::CreateContents(nsIContent* aElement) } +NS_IMETHODIMP +RDFGenericBuilderImpl::OpenContainer(nsIContent* aElement) +{ + nsresult rv; + + // First, make sure that the element is in the right widget -- ours. + if (!IsElementInWidget(aElement)) + return NS_OK; + + nsCOMPtr resource; + rv = nsRDFContentUtils::GetElementRefResource(aElement, getter_AddRefs(resource)); + + // If it has no resource, there's nothing that we need to be + // concerned about here. + if (NS_FAILED(rv)) + return NS_OK; + + // The element has a resource; that means that it corresponds + // to something in the graph, so we need to go to the graph to + // create its contents. + rv = CreateContainerContents(aElement, resource, PR_TRUE); + if (NS_FAILED(rv)) return rv; + + if (rv == NS_RDF_ELEMENT_WAS_CREATED) { + nsCOMPtr tag; + rv = aElement->GetTag(*getter_AddRefs(tag)); + if (NS_FAILED(rv)) return rv; + + if (tag.get() == kTreeItemAtom) { + nsCOMPtr insertionpoint; + rv = FindInsertionPoint(aElement, getter_AddRefs(insertionpoint)); + if (NS_FAILED(rv)) return rv; + + if (! insertionpoint) { + // No content got built. Bail! + return NS_OK; + } + + // Find the first element beneath the insertion point that + // is generated from a template. + PRInt32 indx; + rv = FindFirstGeneratedChild(insertionpoint, &indx); + if (NS_FAILED(rv)) return rv; + + if (indx != -1) { + nsCOMPtr doc = do_QueryInterface(mDocument); + if (! doc) + return NS_ERROR_UNEXPECTED; + + rv = doc->ContentAppended(insertionpoint, indx); + if (NS_FAILED(rv)) return rv; + } + } + } + + return NS_OK; +} + +NS_IMETHODIMP +RDFGenericBuilderImpl::CloseContainer(nsIContent* aElement) +{ + NS_PRECONDITION(aElement != nsnull, "null ptr"); + if (! aElement) + return NS_ERROR_NULL_POINTER; + + // First, make sure that the element is in the right widget -- ours. + if (!IsElementInWidget(aElement)) + return NS_OK; + + nsresult rv; + + nsCOMPtr tag; + rv = aElement->GetTag(*getter_AddRefs(tag)); + if (NS_FAILED(rv)) return rv; + + // If it's not a tree, just bail. Keep the content around until + // the cows come home. + if (tag.get() != kTreeItemAtom) + return NS_OK; + + // Find the tag that contains the children so that we can remove + // all of the children. + // + // XXX We do this as a (premature?) optimization so that nodes + // which are not being displayed don't hang around taking up + // space. Unfortunately, the tree widget currently _relies_ on + // this behavior and will break if we don't do it :-(. + nsCOMPtr insertionpoint; + rv = FindInsertionPoint(aElement, getter_AddRefs(insertionpoint)); + if (NS_FAILED(rv)) return rv; + + if (insertionpoint) { + PRInt32 count; + rv = insertionpoint->ChildCount(count); + if (NS_FAILED(rv)) return rv; + + rv = RemoveGeneratedContent(insertionpoint); + if (NS_FAILED(rv)) return rv; + } + + // Clear the contents-generated attribute so that the next time we + // come back, we'll regenerate the kids we just killed. + rv = aElement->UnsetAttribute(kNameSpaceID_None, + kContainerContentsGeneratedAtom, + PR_FALSE); + if (NS_FAILED(rv)) return rv; + + // XXX Hack. Setting this attribute forces the RDF element to + // remember that it needs to be re-generated next time around. + rv = aElement->SetAttribute(kNameSpaceID_None, + kLazyContentAtom, + "true", + PR_FALSE); + if (NS_FAILED(rv)) return rv; + + return NS_OK; +} + + +NS_IMETHODIMP +RDFGenericBuilderImpl::RebuildContainer(nsIContent* aElement) +{ + nsresult rv; + + // Remove any generated children from this node + rv = RemoveGeneratedContent(aElement); + if (NS_FAILED(rv)) return rv; + + // Clear the contents-generated attribute so that the next time we + // come back, we'll regenerate the kids we just killed. + rv = aElement->UnsetAttribute(kNameSpaceID_None, + kContainerContentsGeneratedAtom, + PR_FALSE); + if (NS_FAILED(rv)) return rv; + + // XXX Hack. Setting this attribute forces the RDF element to + // remember that it needs to be re-generated next time around. + rv = aElement->SetAttribute(kNameSpaceID_None, + kLazyContentAtom, + "true", + PR_FALSE); + if (NS_FAILED(rv)) return rv; + + // Since we're changing the 'ref' attribute, we'll need to + // rebuild content for _this_ resource template, too. + rv = aElement->UnsetAttribute(kNameSpaceID_None, + kTemplateContentsGeneratedAtom, + PR_FALSE); + if (NS_FAILED(rv)) return rv; + + // Do it!!! + rv = CreateContents(aElement); + if (NS_FAILED(rv)) return rv; + + // Now see where on earth the content was _really_ appended so we + // can tell the frames to go reflow themselves. Start with _this_ + // element. + nsCOMPtr insertionpoint = dont_QueryInterface(aElement); + + PRInt32 indx; + rv = FindFirstGeneratedChild(insertionpoint, &indx); + if (NS_FAILED(rv)) return rv; + + if (indx == -1) { + // Okay, nothing got inserted directly beneath this node; see + // if the it was inserted somewhere _below_ us... + rv = FindInsertionPoint(aElement, getter_AddRefs(insertionpoint)); + if (NS_FAILED(rv)) return rv; + + if ((insertionpoint != nsnull) && (insertionpoint.get() != aElement)) { + rv = FindFirstGeneratedChild(insertionpoint, &indx); + if (NS_FAILED(rv)) return rv; + } + } + + if (indx != -1) { + nsCOMPtr doc = do_QueryInterface(mDocument); + + rv = doc->ContentAppended(insertionpoint, indx); + if (NS_FAILED(rv)) return rv; + } + + return NS_OK; +} + + NS_IMETHODIMP RDFGenericBuilderImpl::CreateElement(PRInt32 aNameSpaceID, nsIAtom* aTag, @@ -634,27 +1066,19 @@ RDFGenericBuilderImpl::OnAssert(nsIRDFResource* aSource, // Okay, it's a "live" element, so go ahead and append the new // child to this node. - - // XXX Bug 10818. - PRBool notify = PR_TRUE; - if (IsTreeWidgetItem(element)) { - if (!IsReflowScheduled()) { - rv = ScheduleReflow(); - if (NS_FAILED(rv)) return rv; - } - else { - // a reflow has been scheduled. we'll add the - // element but won't notify right now. - notify = PR_FALSE; - } - } - - rv = CreateWidgetItem(element, aProperty, resource, 0, notify); + rv = CreateWidgetItem(element, aProperty, resource, 0, PR_TRUE); NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create widget item"); if (NS_FAILED(rv)) return rv; - - rv = element->SetAttribute(kNameSpaceID_None, kEmptyAtom, "false", PR_TRUE); + + // Update the "empty" attribute + nsAutoString empty; + rv = element->GetAttribute(kNameSpaceID_None, kEmptyAtom, empty); if (NS_FAILED(rv)) return rv; + + if ((rv != NS_CONTENT_ATTR_HAS_VALUE) || (! empty.Equals("false"))) { + rv = element->SetAttribute(kNameSpaceID_None, kEmptyAtom, "false", PR_TRUE); + if (NS_FAILED(rv)) return rv; + } } else { // Either the target of the assertion is not a resource, @@ -762,8 +1186,14 @@ RDFGenericBuilderImpl::OnUnassert(nsIRDFResource* aSource, if (NS_FAILED(rv)) return rv; if (numKids == 0) { - rv = element->SetAttribute(kNameSpaceID_None, kEmptyAtom, "true", PR_TRUE); + nsAutoString empty; + rv = element->GetAttribute(kNameSpaceID_None, kEmptyAtom, empty); if (NS_FAILED(rv)) return rv; + + if ((rv != NS_CONTENT_ATTR_HAS_VALUE) && (! empty.Equals("true"))) { + rv = element->SetAttribute(kNameSpaceID_None, kEmptyAtom, "true", PR_TRUE); + if (NS_FAILED(rv)) return rv; + } } } @@ -1020,7 +1450,7 @@ RDFGenericBuilderImpl::GetPersistentAttributes(nsIContent *element) literal->GetValueConst(&uniLiteral); if (uniLiteral) { - rv = element->SetAttribute(nameSpaceID, tag, uniLiteral, PR_TRUE); + rv = element->SetAttribute(nameSpaceID, tag, uniLiteral, PR_FALSE); } } } @@ -1430,7 +1860,7 @@ RDFGenericBuilderImpl::BuildContentFromTemplate(nsIContent *aTemplateNode, nsCOMPtr realKid; if (aIsUnique) { - rv = EnsureElementHasGenericChild(aRealNode, nameSpaceID, tag, getter_AddRefs(realKid)); + rv = EnsureElementHasGenericChild(aRealNode, nameSpaceID, tag, aNotify, getter_AddRefs(realKid)); if (NS_FAILED(rv)) return rv; if (rv == NS_RDF_ELEMENT_WAS_THERE) { @@ -1489,7 +1919,7 @@ RDFGenericBuilderImpl::BuildContentFromTemplate(nsIContent *aTemplateNode, rv = content->SetText(text.GetUnicode(), text.Length(), PR_FALSE); if (NS_FAILED(rv)) return rv; - rv = aRealNode->AppendChildTo(nsCOMPtr( do_QueryInterface(content) ), PR_FALSE); + rv = aRealNode->AppendChildTo(nsCOMPtr( do_QueryInterface(content) ), aNotify); if (NS_FAILED(rv)) return rv; } } @@ -1564,15 +1994,14 @@ RDFGenericBuilderImpl::BuildContentFromTemplate(nsIContent *aTemplateNode, // We'll _already_ have added the unique elements. if (! aIsUnique) { - // Note: add into tree, but only sort if its a resource element! - if ((nsnull != XULSortService) && (isResourceElement)) { - rv = XULSortService->InsertContainerNode(aRealNode, realKid, aNotify); - if (NS_FAILED(rv)) { - aRealNode->AppendChildTo(realKid, aNotify); - } + // Add into content model, special casing treeitems. + if ((nsnull != gXULSortService) && (isResourceElement) && (tag.get() == kTreeItemAtom)) { + rv = gXULSortService->InsertContainerNode(aRealNode, realKid, aNotify); + NS_ASSERTION(NS_SUCCEEDED(rv), "unable to insert element via sort service"); } else { - aRealNode->AppendChildTo(realKid, aNotify); + rv = aRealNode->AppendChildTo(realKid, aNotify); + NS_ASSERTION(NS_SUCCEEDED(rv), "unable to insert element"); } } @@ -1605,12 +2034,8 @@ RDFGenericBuilderImpl::CreateWidgetItem(nsIContent *aElement, return NS_OK; } - rv = BuildContentFromTemplate(tmpl, - aElement, - PR_TRUE, - aChild, - aNaturalOrderPos, - aNotify); + rv = BuildContentFromTemplate(tmpl, aElement, PR_TRUE, aChild, aNaturalOrderPos, aNotify); + NS_ASSERTION(NS_SUCCEEDED(rv), "unable to build content from template"); return rv; } @@ -1777,7 +2202,7 @@ RDFGenericBuilderImpl::RemoveWidgetItem(nsIContent* aElement, nsresult -RDFGenericBuilderImpl::CreateContainerContents(nsIContent* aElement, nsIRDFResource* aResource) +RDFGenericBuilderImpl::CreateContainerContents(nsIContent* aElement, nsIRDFResource* aResource, PRBool aNotify) { // Create the contents of a container by iterating over all of the // "containment" arcs out of the element's resource. @@ -1801,7 +2226,7 @@ RDFGenericBuilderImpl::CreateContainerContents(nsIContent* aElement, nsIRDFResou if (NS_FAILED(rv)) return rv; if ((rv == NS_CONTENT_ATTR_HAS_VALUE) && (attrValue.EqualsIgnoreCase("true"))) - return NS_OK; + return NS_RDF_ELEMENT_WAS_CREATED; // Now mark the element's contents as being generated so that // any re-entrant calls don't trigger an infinite recursion. @@ -1821,11 +2246,6 @@ RDFGenericBuilderImpl::CreateContainerContents(nsIContent* aElement, nsIRDFResou rv = mDB->ArcLabelsOut(aResource, getter_AddRefs(properties)); if (NS_FAILED(rv)) return rv; - // rjc - sort - nsCOMPtr tempArray; - rv = NS_NewISupportsArray(getter_AddRefs(tempArray)); - if NS_FAILED(rv) return rv; - while (1) { PRBool hasMore; rv = properties->HasMoreElements(&hasMore); @@ -1865,74 +2285,13 @@ RDFGenericBuilderImpl::CreateContainerContents(nsIContent* aElement, nsIRDFResou rv = targets->GetNext(getter_AddRefs(isupportsNext)); if (NS_FAILED(rv)) return rv; - nsCOMPtr valueResource = do_QueryInterface(isupportsNext); - NS_ASSERTION(valueResource != nsnull, "not a resource"); - if (! valueResource) + nsCOMPtr target = do_QueryInterface(isupportsNext); + NS_ASSERTION(target != nsnull, "not a resource"); + if (! target) continue; - // XXX hack: always append value resource 1st due to sort - // callback implementation - tempArray->AppendElement(valueResource); - tempArray->AppendElement(property); - } - } - - PRUint32 numElements; - rv = tempArray->Count(&numElements); - if (NS_FAILED(rv)) return rv; - - if (numElements == 0) - return NS_OK; - - // Tree widget hackery. To be removed if and when the - // reflow lock is exposed. See bug 10818. - PRBool istree = IsTreeWidgetItem(aElement); - - { - // XXX change to use nsVoidArray? - nsIRDFResource** flatArray = new nsIRDFResource*[numElements]; - if (! flatArray) return NS_ERROR_OUT_OF_MEMORY; - - // flatten array of resources, sort them, then add as item elements - PRUint32 loop; - for (loop=0; loopElementAt(loop); - - if (XULSortService) { - XULSortService->OpenContainer(mDB, aElement, flatArray, numElements/2, 2*sizeof(nsIRDFResource *)); - } - - // This will insert all of the elements into the - // container, but _won't_ bother layout about it. - for (loop=0; loop=0; i--) { - NS_IF_RELEASE(flatArray[i]); - } - - delete [] flatArray; - if (NS_FAILED(rv)) return rv; - } - - if (istree) { - nsCOMPtr doc = do_QueryInterface(mDocument); - if (! doc) - return NS_ERROR_UNEXPECTED; - - nsCOMPtr treechildren; - rv = nsRDFContentUtils::FindChildByTag(aElement, - kNameSpaceID_XUL, - kTreeChildrenAtom, - getter_AddRefs(treechildren)); - - if (NS_SUCCEEDED(rv)) { - // _Now_ tell layout that we've mucked with the container. This'll - // force a reflow and get the content displayed. - rv = doc->ContentAppended(treechildren, 0); + rv = CreateWidgetItem(aElement, property, target, -1, aNotify); + NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create item"); if (NS_FAILED(rv)) return rv; } } @@ -2000,7 +2359,7 @@ RDFGenericBuilderImpl::CreateTemplateContents(nsIContent* aElement, const nsStri element = parent; } - rv = BuildContentFromTemplate(tmpl, aElement, PR_FALSE, resource, -1, PR_TRUE); + rv = BuildContentFromTemplate(tmpl, aElement, PR_FALSE, resource, -1, PR_FALSE); if (NS_FAILED(rv)) return rv; return NS_OK; @@ -2010,6 +2369,7 @@ nsresult RDFGenericBuilderImpl::EnsureElementHasGenericChild(nsIContent* parent, PRInt32 nameSpaceID, nsIAtom* tag, + PRBool aNotify, nsIContent** result) { nsresult rv; @@ -2026,7 +2386,7 @@ RDFGenericBuilderImpl::EnsureElementHasGenericChild(nsIContent* parent, if (NS_FAILED(rv)) return rv; // XXX Note that the notification ensures we won't batch insertions! This could be bad! - Dave - rv = parent->AppendChildTo(element, PR_TRUE); + rv = parent->AppendChildTo(element, aNotify); if (NS_FAILED(rv)) return rv; *result = element; @@ -2381,6 +2741,109 @@ RDFGenericBuilderImpl::GetResource(PRInt32 aNameSpaceID, } +nsresult +RDFGenericBuilderImpl::FindInsertionPoint(nsIContent* aElement, nsIContent** aResult) +{ + nsresult rv; + + // XXX Hack-o-rama. This needs to be fixed to actually grovel over + // the template n' stuff. + nsCOMPtr tag; + rv = aElement->GetTag(*getter_AddRefs(tag)); + if (NS_FAILED(rv)) return rv; + + nsAutoString tagName; + rv = tag->ToString(tagName); + if (NS_FAILED(rv)) return rv; + + if (tagName.Equals("tree") || tagName.Equals("treeitem")) { + rv = nsRDFContentUtils::FindChildByTag(aElement, kNameSpaceID_XUL, NS_NewAtom("treechildren"), aResult); + if (NS_FAILED(rv)) return rv; + } + else if (tagName.Equals("menu")) { + rv = nsRDFContentUtils::FindChildByTag(aElement, kNameSpaceID_XUL, NS_NewAtom("menupopup"), aResult); + if (NS_FAILED(rv)) return rv; + } + else { + *aResult = aElement; + NS_ADDREF(*aResult); + } + + return NS_OK; +} + + +nsresult +RDFGenericBuilderImpl::RemoveGeneratedContent(nsIContent* aElement) +{ + // Remove all the content beneath aElement that has been generated + // from a template. + + nsresult rv; + + PRInt32 count; + rv = aElement->ChildCount(count); + if (NS_FAILED(rv)) return rv; + + while (--count >= 0) { + nsCOMPtr child; + rv = aElement->ChildAt(count, *getter_AddRefs(child)); + if (NS_FAILED(rv)) return rv; + + nsAutoString tmplID; + rv = child->GetAttribute(kNameSpaceID_None, kTemplateAtom, tmplID); + if (NS_FAILED(rv)) return rv; + + if (rv != NS_CONTENT_ATTR_HAS_VALUE) + continue; + + // It's a generated element. Remove it, and set its document + // to null so that it'll get knocked out of the XUL doc's + // resource-to-element map. + rv = aElement->RemoveChildAt(count, PR_TRUE); + NS_ASSERTION(NS_SUCCEEDED(rv), "error removing child"); + + rv = child->SetDocument(nsnull, PR_TRUE); + if (NS_FAILED(rv)) return rv; + } + + return NS_OK; +} + + +nsresult +RDFGenericBuilderImpl::FindFirstGeneratedChild(nsIContent* aElement, PRInt32* aIndex) +{ + // Find the first kid of aElement that has been generated from a + // template. + + nsresult rv; + + PRInt32 count; + rv = aElement->ChildCount(count); + if (NS_FAILED(rv)) return rv; + + PRInt32 i = 0; + while (i < count) { + nsCOMPtr child; + rv = aElement->ChildAt(i, *getter_AddRefs(child)); + if (NS_FAILED(rv)) return rv; + + nsAutoString tmplID; + rv = child->GetAttribute(kNameSpaceID_None, kTemplateAtom, tmplID); + if (NS_FAILED(rv)) return rv; + + if (rv == NS_CONTENT_ATTR_HAS_VALUE) + break; + + ++i; + } + + *aIndex = (i < count) ? i : -1; + return NS_OK; +} + + PRBool RDFGenericBuilderImpl::IsTreeWidgetItem(nsIContent* aElement) {