diff --git a/chrome/src/nsChromeRegistry.cpp b/chrome/src/nsChromeRegistry.cpp index 0f07d5bf0bd6..ff1a1f01c756 100644 --- a/chrome/src/nsChromeRegistry.cpp +++ b/chrome/src/nsChromeRegistry.cpp @@ -74,6 +74,7 @@ #include "nsLayoutCID.h" #include "nsGfxCIID.h" #include "nsIImageManager.h" +#include "nsIBindingManager.h" #include "prio.h" static char kChromePrefix[] = "chrome://"; @@ -1044,6 +1045,11 @@ NS_IMETHODIMP nsChromeRegistry::RefreshWindow(nsIDOMWindow* aWindow) if (!document) return NS_OK; + // Annihilate all XBL bindings. + nsCOMPtr bindingManager; + document->GetBindingManager(getter_AddRefs(bindingManager)); + bindingManager->FlushChromeBindings(); + nsCOMPtr xulDoc = do_QueryInterface(domDocument); if (xulDoc) { // Deal with the backstop sheets first. diff --git a/content/xbl/public/nsIBindingManager.h b/content/xbl/public/nsIBindingManager.h index 5328a80eb4fb..49e682526e27 100644 --- a/content/xbl/public/nsIBindingManager.h +++ b/content/xbl/public/nsIBindingManager.h @@ -74,6 +74,7 @@ public: NS_IMETHOD RemoveLoadingDocListener(const nsCString& aURL)=0; NS_IMETHOD InheritsStyle(nsIContent* aContent, PRBool* aResult) = 0; + NS_IMETHOD FlushChromeBindings() = 0; }; #endif // nsIBinding_Manager_h__ diff --git a/content/xbl/public/nsIXBLBinding.h b/content/xbl/public/nsIXBLBinding.h index bc5d9f7411c4..a0a4d66ab617 100644 --- a/content/xbl/public/nsIXBLBinding.h +++ b/content/xbl/public/nsIXBLBinding.h @@ -91,6 +91,9 @@ public: NS_IMETHOD WalkRules(nsISupportsArrayEnumFunc aFunc, void* aData)=0; NS_IMETHOD SetAllowScripts(PRBool aFlag)=0; + + NS_IMETHOD MarkForDeath()=0; + NS_IMETHOD MarkedForDeath(PRBool* aResult)=0; }; extern nsresult diff --git a/content/xbl/public/nsIXBLService.h b/content/xbl/public/nsIXBLService.h index c641983c88e7..5846e4f23bb4 100644 --- a/content/xbl/public/nsIXBLService.h +++ b/content/xbl/public/nsIXBLService.h @@ -59,6 +59,9 @@ public: // This method loads a binding doc and then builds the specific binding required. NS_IMETHOD GetBinding(nsIContent* aBoundElement, const nsCString& aURLStr, nsIXBLBinding** aResult) = 0; + // Indicates whether or not a binding is fully loaded. + NS_IMETHOD BindingReady(nsIContent* aBoundElement, const nsCString& aURLStr, PRBool* aIsReady) = 0; + // For a given element, returns a flat list of all the anonymous children that need // frames built. NS_IMETHOD GetContentList(nsIContent* aContent, nsISupportsArray** aResult, nsIContent** aChildElement, diff --git a/content/xbl/src/nsBindingManager.cpp b/content/xbl/src/nsBindingManager.cpp index 055977d9e28a..5908f366fcd1 100644 --- a/content/xbl/src/nsBindingManager.cpp +++ b/content/xbl/src/nsBindingManager.cpp @@ -178,7 +178,8 @@ public: NS_IMETHOD RemoveLoadingDocListener(const nsCString& aURL); NS_IMETHOD InheritsStyle(nsIContent* aContent, PRBool* aResult); - + NS_IMETHOD FlushChromeBindings(); + // nsIStyleRuleSupplier NS_IMETHOD UseDocumentRules(nsIContent* aContent, PRBool* aResult); NS_IMETHOD WalkRules(nsIStyleSet* aStyleSet, @@ -252,8 +253,13 @@ nsBindingManager::SetBinding(nsIContent* aContent, nsIXBLBinding* aBinding ) mBindingTable = new nsSupportsHashtable; nsISupportsKey key(aContent); + + nsCOMPtr old = getter_AddRefs(mBindingTable->Get(&key)); + if (old && aBinding) + NS_ERROR("Binding already installed!"); + if (aBinding) { - mBindingTable->Put (&key, aBinding); + mBindingTable->Put(&key, aBinding); } else mBindingTable->Remove(&key); @@ -393,7 +399,7 @@ nsBindingManager::ProcessAttachedQueue() for (PRUint32 i = 0; i < count; i++) { nsCOMPtr binding; mAttachedQueue->GetElementAt(0, getter_AddRefs(binding)); - mAttachedQueue->RemoveElementAt(0); + mAttachedQueue->RemoveElementAt(0); binding->ExecuteAttachedHandler(); } @@ -468,6 +474,23 @@ nsBindingManager::RemoveLoadingDocListener(const nsCString& aURL) return NS_OK; } +PRBool PR_CALLBACK MarkForDeath(nsHashKey* aKey, void* aData, void* aClosure) +{ + nsIXBLBinding* binding = (nsIXBLBinding*)aData; + nsCAutoString docURI; + binding->GetDocURI(docURI); + if (!docURI.CompareWithConversion("chrome", PR_FALSE, 6)) + binding->MarkForDeath(); + return PR_TRUE; +} + +NS_IMETHODIMP +nsBindingManager::FlushChromeBindings() +{ + mBindingTable->Enumerate(MarkForDeath); + return NS_OK; +} + NS_IMETHODIMP nsBindingManager::InheritsStyle(nsIContent* aContent, PRBool* aResult) { diff --git a/content/xbl/src/nsXBLBinding.cpp b/content/xbl/src/nsXBLBinding.cpp index a33729efc0aa..92db35737b3a 100644 --- a/content/xbl/src/nsXBLBinding.cpp +++ b/content/xbl/src/nsXBLBinding.cpp @@ -252,11 +252,14 @@ nsXBLBinding::nsXBLBinding(const nsCString& aDocURI, const nsCString& aID) mIsStyleBinding(PR_TRUE), mAllowScripts(PR_TRUE), mInheritStyle(PR_TRUE), + mMarkedForDeath(PR_FALSE), mAttributeTable(nsnull), mInsertionPointTable(nsnull) { NS_INIT_REFCNT(); gRefCnt++; + // printf("REF COUNT UP: %d %s\n", gRefCnt, (const char*)mID); + if (gRefCnt == 1) { kContentAtom = NS_NewAtom("content"); kInterfaceAtom = NS_NewAtom("interface"); @@ -303,6 +306,8 @@ nsXBLBinding::~nsXBLBinding(void) delete mInsertionPointTable; gRefCnt--; + // printf("REF COUNT DOWN: %d %s\n", gRefCnt, (const char*)mID); + if (gRefCnt == 0) { NS_RELEASE(kContentAtom); NS_RELEASE(kInterfaceAtom); @@ -354,6 +359,11 @@ nsXBLBinding::GetBaseBinding(nsIXBLBinding** aResult) NS_IMETHODIMP nsXBLBinding::SetBaseBinding(nsIXBLBinding* aBinding) { + if (mNextBinding) { + NS_ERROR("Base XBL binding is already defined!"); + return NS_OK; + } + mNextBinding = aBinding; // Comptr handles rel/add return NS_OK; } @@ -1454,8 +1464,6 @@ nsXBLBinding::IsInExcludesList(nsIAtom* aTag, const nsString& aList) NS_IMETHODIMP nsXBLBinding::ConstructAttributeTable(nsIContent* aElement) { - // XXX This function still needs to deal with the - // ability to map one attribute to another. nsAutoString inherits; aElement->GetAttribute(kNameSpaceID_None, kInheritsAtom, inherits); if (!inherits.IsEmpty()) { @@ -1745,7 +1753,19 @@ nsXBLBinding::GetFirstStyleBinding(nsIXBLBinding** aResult) return NS_OK; } +NS_IMETHODIMP +nsXBLBinding::MarkForDeath() +{ + mMarkedForDeath = PR_TRUE; + return NS_OK; +} +NS_IMETHODIMP +nsXBLBinding::MarkedForDeath(PRBool* aResult) +{ + *aResult = mMarkedForDeath; + return NS_OK; +} // Creation Routine /////////////////////////////////////////////////////////////////////// diff --git a/content/xbl/src/nsXBLBinding.h b/content/xbl/src/nsXBLBinding.h index 0baa738db636..dee3f2755ba5 100644 --- a/content/xbl/src/nsXBLBinding.h +++ b/content/xbl/src/nsXBLBinding.h @@ -85,6 +85,9 @@ class nsXBLBinding: public nsIXBLBinding NS_IMETHOD SetAllowScripts(PRBool aFlag) { mAllowScripts = aFlag; return NS_OK; }; + NS_IMETHOD MarkForDeath(); + NS_IMETHOD MarkedForDeath(PRBool* aResult); + public: nsXBLBinding(const nsCString& aDocURI, const nsCString& aRef); virtual ~nsXBLBinding(); @@ -172,9 +175,10 @@ protected: nsIContent* mBoundElement; // [WEAK] We have a reference, but we don't own it. - PRBool mIsStyleBinding; - PRBool mAllowScripts; - PRBool mInheritStyle; + PRPackedBool mIsStyleBinding; + PRPackedBool mAllowScripts; + PRPackedBool mInheritStyle; + PRPackedBool mMarkedForDeath; nsSupportsHashtable* mAttributeTable; // A table for attribute entries. nsSupportsHashtable* mInsertionPointTable; // A table of insertion points. diff --git a/content/xbl/src/nsXBLService.cpp b/content/xbl/src/nsXBLService.cpp index 6e70e13ea90b..fbf8de1f3048 100644 --- a/content/xbl/src/nsXBLService.cpp +++ b/content/xbl/src/nsXBLService.cpp @@ -25,6 +25,7 @@ #include "nsCOMPtr.h" #include "nsXBLService.h" #include "nsIInputStream.h" +#include "nsINameSpace.h" #include "nsINameSpaceManager.h" #include "nsHashtable.h" #include "nsIURI.h" @@ -37,6 +38,7 @@ #include "nsNetUtil.h" #include "plstr.h" #include "nsIContent.h" +#include "nsIXMLContent.h" #include "nsIDOMElement.h" #include "nsIDocument.h" #include "nsIXMLContentSink.h" @@ -52,6 +54,9 @@ #include "nsIChromeRegistry.h" #include "nsIPref.h" +#include "nsIPresShell.h" +#include "nsIDocumentObserver.h" + #include "nsIXULContentUtils.h" #include "nsIXULPrototypeCache.h" #include "nsIDOMLoadListener.h" @@ -104,55 +109,56 @@ struct nsXBLBindingRequest } } + static void* operator new(size_t aSize, nsFixedSizeAllocator& aAllocator) { + return aAllocator.Alloc(aSize); + } + + static void operator delete(void* aPtr, size_t aSize) { + nsFixedSizeAllocator::Free(aPtr, aSize); + } + void DocumentLoaded(nsIDocument* aBindingDoc) { - // Get the binding. - nsCOMPtr newBinding; - gXBLService->GetBinding(mBoundElement, mBindingURL, getter_AddRefs(newBinding)); - - // XXX Deal with layered bindings. - // XXX Deal with cross-site inheritance (e.g., http://a/a.xml inheriting from http://b/b.xml) - // Install the binding on the content node. nsCOMPtr doc; mBoundElement->GetDocument(*getter_AddRefs(doc)); if (!doc) return; - nsCOMPtr bindingManager; - doc->GetBindingManager(getter_AddRefs(bindingManager)); - bindingManager->SetBinding(mBoundElement, newBinding); - // Set the binding's bound element. - newBinding->SetBoundElement(mBoundElement); + // Get the binding. + PRBool ready = PR_FALSE; + gXBLService->BindingReady(mBoundElement, mBindingURL, &ready); - // Tell the binding to build the anonymous content. - newBinding->GenerateAnonymousContent(mBoundElement); - - // Tell the binding to install event handlers - nsCOMPtr attachReq; - newBinding->InstallEventHandlers(mBoundElement, getter_AddRefs(attachReq)); - - // Set up our properties - newBinding->InstallProperties(mBoundElement); - - if (attachReq) { - attachReq->ExecuteAttachedHandler(); - } + if (!ready) + return; + // XXX Deal with layered bindings. // Now do a ContentInserted notification to cause the frames to get installed finally, nsCOMPtr parent; mBoundElement->GetParent(*getter_AddRefs(parent)); PRInt32 index = 0; if (parent) parent->IndexOf(mBoundElement, index); - if (index == -1) - return; - doc->ContentInserted(parent, mBoundElement, index); + + nsCOMPtr shell = getter_AddRefs(doc->GetShellAt(0)); + if (shell) { + nsCOMPtr obs(do_QueryInterface(shell)); + obs->ContentRemoved(doc, parent, mBoundElement, index); + obs->ContentInserted(doc, parent, mBoundElement, index); + } } static nsIXBLService* gXBLService; static int gRefCnt; }; +static const size_t kBucketSizes[] = { + sizeof(nsXBLBindingRequest) +}; + +static const PRInt32 kNumBuckets = sizeof(kBucketSizes)/sizeof(size_t); +static const PRInt32 kNumElements = 64; +static const PRInt32 kInitialSize = (NS_SIZE_IN_HEAP(sizeof(nsXBLBindingRequest))) * kNumElements; + nsIXBLService* nsXBLBindingRequest::gXBLService = nsnull; int nsXBLBindingRequest::gRefCnt = 0; @@ -180,12 +186,13 @@ public: virtual ~nsXBLStreamListener(); void AddRequest(nsXBLBindingRequest* aRequest) { mBindingRequests.AppendElement(aRequest); }; + PRBool HasRequest(const nsCString& aURI, nsIContent* aBoundElement); private: nsCOMPtr mInner; nsVoidArray mBindingRequests; - nsCOMPtr mDocument; + nsIDocument* mDocument; nsCOMPtr mBindingDocument; }; @@ -264,17 +271,34 @@ nsXBLStreamListener::OnStopRequest(nsIChannel* aChannel, nsISupports* aCtxt, nsr rv = mInner->OnStopRequest(aChannel, aCtxt, aStatus, aStatusArg); } - if (NS_FAILED(rv)) { + if (NS_FAILED(rv) || NS_FAILED(aStatus)) { PRUint32 count = mBindingRequests.Count(); for (PRUint32 i = 0; i < count; i++) { nsXBLBindingRequest* req = (nsXBLBindingRequest*)mBindingRequests.ElementAt(i); delete req; } + + mDocument = nsnull; + mBindingDocument = nsnull; } return rv; } +PRBool +nsXBLStreamListener::HasRequest(const nsCString& aURI, nsIContent* aElt) +{ + // XXX Could be more efficient. + PRUint32 count = mBindingRequests.Count(); + for (PRUint32 i = 0; i < count; i++) { + nsXBLBindingRequest* req = (nsXBLBindingRequest*)mBindingRequests.ElementAt(i); + if (req->mBindingURL.Equals(aURI) && req->mBoundElement.get() == aElt) + return PR_TRUE; + } + + return PR_FALSE; +} + nsresult nsXBLStreamListener::Load(nsIDOMEvent* aEvent) { @@ -312,8 +336,9 @@ nsXBLStreamListener::Load(nsIDOMEvent* aEvent) info->SetScriptAccess(allow); } } - - bindingManager->PutXBLDocumentInfo(info); + + if (!cached) + bindingManager->PutXBLDocumentInfo(info); // Notify all pending requests that their bindings are // ready and can be installed. @@ -328,9 +353,12 @@ nsXBLStreamListener::Load(nsIDOMEvent* aEvent) delete req; } - nsCOMPtr rec(do_QueryInterface(mDocument)); + nsCOMPtr rec(do_QueryInterface(mBindingDocument)); rec->RemoveEventListener(NS_ConvertASCIItoUCS2("load"), (nsIDOMLoadListener*)this, PR_FALSE); + mDocument = nsnull; + mBindingDocument = nsnull; + return rv; } @@ -406,7 +434,7 @@ PRUint32 nsXBLService::gClassLRUListLength = 0; PRUint32 nsXBLService::gClassLRUListQuota = 64; nsIAtom* nsXBLService::kExtendsAtom = nsnull; -nsIAtom* nsXBLService::kHasChildrenAtom = nsnull; +nsIAtom* nsXBLService::kScrollbarAtom = nsnull; // Enabled by default. Must be over-ridden to disable PRBool nsXBLService::gDisableChromeCache = PR_FALSE; @@ -421,6 +449,8 @@ NS_IMPL_ISUPPORTS2(nsXBLService, nsIXBLService, nsIMemoryPressureObserver) nsXBLService::nsXBLService(void) { NS_INIT_REFCNT(); + mPool.Init("XBL Binding Requests", kBucketSizes, kNumBuckets, kInitialSize); + gRefCnt++; if (gRefCnt == 1) { @@ -442,8 +472,8 @@ nsXBLService::nsXBLService(void) // Create our atoms kExtendsAtom = NS_NewAtom("extends"); - kHasChildrenAtom = NS_NewAtom("haschildren"); - + kScrollbarAtom = NS_NewAtom("scrollbar"); + // Find out if the XUL cache is on or off NS_WITH_SERVICE(nsIPref, prefs, NS_PREF_PROGID, &rv); if (NS_SUCCEEDED(rv)) @@ -476,8 +506,8 @@ nsXBLService::~nsXBLService(void) // Release our atoms NS_RELEASE(kExtendsAtom); - NS_RELEASE(kHasChildrenAtom); - + NS_RELEASE(kScrollbarAtom); + // Walk the LRU list removing and deleting the nsXBLJSClasses. FlushMemory(REASON_HEAP_MINIMIZE, 0); @@ -526,15 +556,23 @@ nsXBLService::LoadBindings(nsIContent* aContent, const nsString& aURL, PRBool aA nsCOMPtr styleBinding; binding->GetFirstStyleBinding(getter_AddRefs(styleBinding)); if (styleBinding) { - // See if the URIs match. - nsCAutoString uri; - styleBinding->GetBindingURI(uri); - if (uri.EqualsWithConversion(aURL)) - return NS_OK; - else { + PRBool marked = PR_FALSE; + binding->MarkedForDeath(&marked); + if (marked) { FlushStyleBindings(aContent); binding = nsnull; } + else { + // See if the URIs match. + nsCAutoString uri; + styleBinding->GetBindingURI(uri); + if (uri.EqualsWithConversion(aURL)) + return NS_OK; + else { + FlushStyleBindings(aContent); + binding = nsnull; + } + } } } @@ -736,9 +774,29 @@ nsXBLService::FlushMemory(PRUint32 reason, size_t requestedAmount) // Internal helper methods //////////////////////////////////////////////////////////////// -NS_IMETHODIMP nsXBLService::GetBinding(nsIContent* aBoundElement, const nsCString& aURLStr, nsIXBLBinding** aResult) +NS_IMETHODIMP nsXBLService::GetBinding(nsIContent* aBoundElement, + const nsCString& aURLStr, + nsIXBLBinding** aResult) { - *aResult = nsnull; + PRBool dummy; + return GetBindingInternal(aBoundElement, aURLStr, PR_FALSE, &dummy, aResult); +} + +NS_IMETHODIMP nsXBLService::BindingReady(nsIContent* aBoundElement, + const nsCString& aURLStr, + PRBool* aIsReady) +{ + return GetBindingInternal(aBoundElement, aURLStr, PR_TRUE, aIsReady, nsnull); +} + +NS_IMETHODIMP nsXBLService::GetBindingInternal(nsIContent* aBoundElement, + const nsCString& aURLStr, + PRBool aPeekOnly, + PRBool* aIsReady, + nsIXBLBinding** aResult) +{ + if (aResult) + *aResult = nsnull; if (aURLStr.IsEmpty()) return NS_ERROR_FAILURE; @@ -781,32 +839,53 @@ NS_IMETHODIMP nsXBLService::GetBinding(nsIContent* aBoundElement, const nsCStrin child->GetAttribute(kNameSpaceID_None, nsHTMLAtoms::id, value); // If no ref is specified just use this. - if ((bindingName.IsEmpty()) || (bindingName == value)) { - // Make a new binding - NS_NewXBLBinding(uri, ref, aResult); - - // Initialize its bound element. - (*aResult)->SetBindingElement(child); - (*aResult)->SetAllowScripts(allowScripts); - + if ((bindingName.IsEmpty()) || (bindingName == value)) { // Check for the presence of an extends attribute - child->GetAttribute(kNameSpaceID_None, kExtendsAtom, value); - if (!value.IsEmpty()) { - // See if we are extending a builtin tag. - nsCOMPtr tag; - PRInt32 dummy; - (*aResult)->GetBaseTag(&dummy, getter_AddRefs(tag)); - if (!tag) { - // We have a base class binding. Load it right now. - nsCOMPtr baseBinding; - nsCAutoString urlCString; urlCString.AssignWithConversion(value); - GetBinding(aBoundElement, urlCString, getter_AddRefs(baseBinding)); - if (!baseBinding) - return NS_OK; // At least we got the derived class binding loaded. - (*aResult)->SetBaseBinding(baseBinding); + nsAutoString extends; + nsCOMPtr baseBinding; + child->GetAttribute(kNameSpaceID_None, kExtendsAtom, extends); + value = extends; + if (!extends.IsEmpty()) { + nsAutoString prefix; + PRInt32 offset = extends.FindChar(':'); + if (-1 != offset) { + extends.Left(prefix, offset); + extends.Cut(0, offset+1); + } + if (prefix.Length() > 0) { + // Look up the prefix. + nsCOMPtr prefixAtom = getter_AddRefs(NS_NewAtom(prefix)); + nsCOMPtr nameSpace; + nsCOMPtr xmlContent(do_QueryInterface(child)); + if (xmlContent) { + xmlContent->GetContainingNameSpace(*getter_AddRefs(nameSpace)); + if (nameSpace) { + nsCOMPtr tagSpace; + nameSpace->FindNameSpace(prefixAtom, *getter_AddRefs(tagSpace)); + if (!tagSpace) { + // We have a base class binding. Load it right now. + nsCAutoString urlCString; urlCString.AssignWithConversion(value); + GetBindingInternal(aBoundElement, urlCString, aPeekOnly, aIsReady, getter_AddRefs(baseBinding)); + if (!*aIsReady) + return NS_ERROR_FAILURE; // Binding not yet ready or an error occurred. + } + } + } } } + *aIsReady = PR_TRUE; + if (!aPeekOnly) { + // Make a new binding + NS_NewXBLBinding(uri, ref, aResult); + + // Initialize its bound element. + (*aResult)->SetBindingElement(child); + (*aResult)->SetAllowScripts(allowScripts); + + if (baseBinding) + (*aResult)->SetBaseBinding(baseBinding); + } break; } } @@ -839,7 +918,9 @@ nsXBLService::GetBindingDocumentInfo(nsIContent* aBoundElement, const nsCString& boundDocument->GetBindingManager(getter_AddRefs(bindingManager)); bindingManager->GetXBLDocumentInfo(aURLStr, getter_AddRefs(info)); - if (!info) { + nsCOMPtr tagName; + aBoundElement->GetTag(*getter_AddRefs(tagName)); + if (!info && (tagName.get() != kScrollbarAtom)) { // The third line of defense is to investigate whether or not the // document is currently being loaded asynchronously. If so, there's no // document yet, but we need to glom on our request so that it will be @@ -847,14 +928,16 @@ nsXBLService::GetBindingDocumentInfo(nsIContent* aBoundElement, const nsCString& nsCOMPtr listener; bindingManager->GetLoadingDocListener(aURLStr, getter_AddRefs(listener)); if (listener) { + nsIStreamListener* ilist = listener.get(); + nsXBLStreamListener* xblListener = NS_STATIC_CAST(nsXBLStreamListener*, ilist); // Create a new load observer. nsCAutoString bindingURI(aURLStr); bindingURI += "#"; bindingURI += aRef; - nsXBLBindingRequest* req = new nsXBLBindingRequest(aRef, aBoundElement); - nsIStreamListener* ilist = listener.get(); - nsXBLStreamListener* xblListener = NS_STATIC_CAST(nsXBLStreamListener*, ilist); - xblListener->AddRequest(req); + if (!xblListener->HasRequest(bindingURI, aBoundElement)) { + nsXBLBindingRequest* req = new (mPool) nsXBLBindingRequest(bindingURI, aBoundElement); + xblListener->AddRequest(req); + } return NS_OK; } } @@ -947,11 +1030,12 @@ nsXBLService::FetchBindingDocument(nsIContent* aBoundElement, nsIURI* aURI, cons return rv; } - if (!IsChromeURI(aURI)) { + nsCOMPtr tagName; + aBoundElement->GetTag(*getter_AddRefs(tagName)); + if (tagName != kScrollbarAtom) { // We can be asynchronous nsXBLStreamListener* xblListener = new nsXBLStreamListener(listener, boundDoc, doc); - NS_ADDREF(xblListener); - + nsCOMPtr rec(do_QueryInterface(doc)); rec->AddEventListener(NS_ConvertASCIItoUCS2("load"), (nsIDOMLoadListener*)xblListener, PR_FALSE); @@ -968,7 +1052,7 @@ nsXBLService::FetchBindingDocument(nsIContent* aBoundElement, nsIURI* aURI, cons nsCAutoString bindingURI(uri); bindingURI += "#"; bindingURI += aRef; - nsXBLBindingRequest* req = new nsXBLBindingRequest(bindingURI, aBoundElement); + nsXBLBindingRequest* req = new (mPool) nsXBLBindingRequest(bindingURI, aBoundElement); xblListener->AddRequest(req); // Now kick off the async read. diff --git a/content/xbl/src/nsXBLService.h b/content/xbl/src/nsXBLService.h index ee84f43973f6..22209f3703d0 100644 --- a/content/xbl/src/nsXBLService.h +++ b/content/xbl/src/nsXBLService.h @@ -28,6 +28,7 @@ #include "nsIMemory.h" #include "jsapi.h" // nsXBLJSClass derives from JSClass #include "jsclist.h" // nsXBLJSClass derives from JSCList +#include "nsFixedSizeAllocator.h" class nsIXBLBinding; class nsIXBLDocumentInfo; @@ -54,6 +55,9 @@ class nsXBLService : public nsIXBLService, public nsIMemoryPressureObserver // This method loads a binding doc and then builds the specific binding required. NS_IMETHOD GetBinding(nsIContent* aBoundElement, const nsCString& aURLStr, nsIXBLBinding** aResult); + // Indicates whether or not a binding is fully loaded. + NS_IMETHOD BindingReady(nsIContent* aBoundElement, const nsCString& aURLStr, PRBool* aIsReady); + // This function clears out the bindings on a given content node. NS_IMETHOD FlushStyleBindings(nsIContent* aContent); @@ -80,6 +84,11 @@ public: // This method synchronously loads and parses an XBL file. NS_IMETHOD FetchBindingDocument(nsIContent* aBoundElement, nsIURI* aURI, const nsCString& aRef, nsIDocument** aResult); + // This method loads a binding doc and then builds the specific binding required. It + // can also peek without building. + NS_IMETHOD GetBindingInternal(nsIContent* aBoundElement, const nsCString& aURLStr, + PRBool aPeekFlag, PRBool* aIsReady, nsIXBLBinding** aResult); + // This method walks a binding document and removes any text nodes // that contain only whitespace. static nsresult StripWhitespaceNodes(nsIContent* aContent); @@ -104,7 +113,10 @@ public: // XBL Atoms static nsIAtom* kExtendsAtom; - static nsIAtom* kHasChildrenAtom; + + static nsIAtom* kScrollbarAtom; + + nsFixedSizeAllocator mPool; }; class nsXBLJSClass : public JSCList, public JSClass diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 38345f457fa1..7e26ee937483 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -5235,8 +5235,10 @@ nsCSSFrameConstructor::CreateAnonymousFrames(nsIPresShell* aPresShell, // Load the bindings. nsCOMPtr binding; - xblService->LoadBindings(aParent, ui->mBehavior, PR_FALSE, getter_AddRefs(binding)); - + rv = xblService->LoadBindings(aParent, ui->mBehavior, PR_FALSE, getter_AddRefs(binding)); + if (NS_FAILED(rv)) + return NS_OK; + // Retrieve the anonymous content that we should build. nsCOMPtr anonymousItems; nsCOMPtr childElement; @@ -5467,8 +5469,10 @@ nsCSSFrameConstructor::CreateAnonymousTableCellFrames(nsIPresShell* aPres // Load the bindings. nsCOMPtr binding; - xblService->LoadBindings(aParent, ui->mBehavior, PR_FALSE, getter_AddRefs(binding)); - + rv = xblService->LoadBindings(aParent, ui->mBehavior, PR_FALSE, getter_AddRefs(binding)); + if (NS_FAILED(rv)) + return NS_OK; + // Retrieve the anonymous content that we should build. nsCOMPtr childElement; nsCOMPtr anonymousItems; diff --git a/layout/html/style/src/nsCSSFrameConstructor.cpp b/layout/html/style/src/nsCSSFrameConstructor.cpp index 38345f457fa1..7e26ee937483 100644 --- a/layout/html/style/src/nsCSSFrameConstructor.cpp +++ b/layout/html/style/src/nsCSSFrameConstructor.cpp @@ -5235,8 +5235,10 @@ nsCSSFrameConstructor::CreateAnonymousFrames(nsIPresShell* aPresShell, // Load the bindings. nsCOMPtr binding; - xblService->LoadBindings(aParent, ui->mBehavior, PR_FALSE, getter_AddRefs(binding)); - + rv = xblService->LoadBindings(aParent, ui->mBehavior, PR_FALSE, getter_AddRefs(binding)); + if (NS_FAILED(rv)) + return NS_OK; + // Retrieve the anonymous content that we should build. nsCOMPtr anonymousItems; nsCOMPtr childElement; @@ -5467,8 +5469,10 @@ nsCSSFrameConstructor::CreateAnonymousTableCellFrames(nsIPresShell* aPres // Load the bindings. nsCOMPtr binding; - xblService->LoadBindings(aParent, ui->mBehavior, PR_FALSE, getter_AddRefs(binding)); - + rv = xblService->LoadBindings(aParent, ui->mBehavior, PR_FALSE, getter_AddRefs(binding)); + if (NS_FAILED(rv)) + return NS_OK; + // Retrieve the anonymous content that we should build. nsCOMPtr childElement; nsCOMPtr anonymousItems; diff --git a/layout/xbl/public/nsIBindingManager.h b/layout/xbl/public/nsIBindingManager.h index 5328a80eb4fb..49e682526e27 100644 --- a/layout/xbl/public/nsIBindingManager.h +++ b/layout/xbl/public/nsIBindingManager.h @@ -74,6 +74,7 @@ public: NS_IMETHOD RemoveLoadingDocListener(const nsCString& aURL)=0; NS_IMETHOD InheritsStyle(nsIContent* aContent, PRBool* aResult) = 0; + NS_IMETHOD FlushChromeBindings() = 0; }; #endif // nsIBinding_Manager_h__ diff --git a/layout/xbl/public/nsIXBLBinding.h b/layout/xbl/public/nsIXBLBinding.h index bc5d9f7411c4..a0a4d66ab617 100644 --- a/layout/xbl/public/nsIXBLBinding.h +++ b/layout/xbl/public/nsIXBLBinding.h @@ -91,6 +91,9 @@ public: NS_IMETHOD WalkRules(nsISupportsArrayEnumFunc aFunc, void* aData)=0; NS_IMETHOD SetAllowScripts(PRBool aFlag)=0; + + NS_IMETHOD MarkForDeath()=0; + NS_IMETHOD MarkedForDeath(PRBool* aResult)=0; }; extern nsresult diff --git a/layout/xbl/public/nsIXBLService.h b/layout/xbl/public/nsIXBLService.h index c641983c88e7..5846e4f23bb4 100644 --- a/layout/xbl/public/nsIXBLService.h +++ b/layout/xbl/public/nsIXBLService.h @@ -59,6 +59,9 @@ public: // This method loads a binding doc and then builds the specific binding required. NS_IMETHOD GetBinding(nsIContent* aBoundElement, const nsCString& aURLStr, nsIXBLBinding** aResult) = 0; + // Indicates whether or not a binding is fully loaded. + NS_IMETHOD BindingReady(nsIContent* aBoundElement, const nsCString& aURLStr, PRBool* aIsReady) = 0; + // For a given element, returns a flat list of all the anonymous children that need // frames built. NS_IMETHOD GetContentList(nsIContent* aContent, nsISupportsArray** aResult, nsIContent** aChildElement, diff --git a/layout/xbl/src/nsBindingManager.cpp b/layout/xbl/src/nsBindingManager.cpp index 055977d9e28a..5908f366fcd1 100644 --- a/layout/xbl/src/nsBindingManager.cpp +++ b/layout/xbl/src/nsBindingManager.cpp @@ -178,7 +178,8 @@ public: NS_IMETHOD RemoveLoadingDocListener(const nsCString& aURL); NS_IMETHOD InheritsStyle(nsIContent* aContent, PRBool* aResult); - + NS_IMETHOD FlushChromeBindings(); + // nsIStyleRuleSupplier NS_IMETHOD UseDocumentRules(nsIContent* aContent, PRBool* aResult); NS_IMETHOD WalkRules(nsIStyleSet* aStyleSet, @@ -252,8 +253,13 @@ nsBindingManager::SetBinding(nsIContent* aContent, nsIXBLBinding* aBinding ) mBindingTable = new nsSupportsHashtable; nsISupportsKey key(aContent); + + nsCOMPtr old = getter_AddRefs(mBindingTable->Get(&key)); + if (old && aBinding) + NS_ERROR("Binding already installed!"); + if (aBinding) { - mBindingTable->Put (&key, aBinding); + mBindingTable->Put(&key, aBinding); } else mBindingTable->Remove(&key); @@ -393,7 +399,7 @@ nsBindingManager::ProcessAttachedQueue() for (PRUint32 i = 0; i < count; i++) { nsCOMPtr binding; mAttachedQueue->GetElementAt(0, getter_AddRefs(binding)); - mAttachedQueue->RemoveElementAt(0); + mAttachedQueue->RemoveElementAt(0); binding->ExecuteAttachedHandler(); } @@ -468,6 +474,23 @@ nsBindingManager::RemoveLoadingDocListener(const nsCString& aURL) return NS_OK; } +PRBool PR_CALLBACK MarkForDeath(nsHashKey* aKey, void* aData, void* aClosure) +{ + nsIXBLBinding* binding = (nsIXBLBinding*)aData; + nsCAutoString docURI; + binding->GetDocURI(docURI); + if (!docURI.CompareWithConversion("chrome", PR_FALSE, 6)) + binding->MarkForDeath(); + return PR_TRUE; +} + +NS_IMETHODIMP +nsBindingManager::FlushChromeBindings() +{ + mBindingTable->Enumerate(MarkForDeath); + return NS_OK; +} + NS_IMETHODIMP nsBindingManager::InheritsStyle(nsIContent* aContent, PRBool* aResult) { diff --git a/layout/xbl/src/nsXBLBinding.cpp b/layout/xbl/src/nsXBLBinding.cpp index a33729efc0aa..92db35737b3a 100644 --- a/layout/xbl/src/nsXBLBinding.cpp +++ b/layout/xbl/src/nsXBLBinding.cpp @@ -252,11 +252,14 @@ nsXBLBinding::nsXBLBinding(const nsCString& aDocURI, const nsCString& aID) mIsStyleBinding(PR_TRUE), mAllowScripts(PR_TRUE), mInheritStyle(PR_TRUE), + mMarkedForDeath(PR_FALSE), mAttributeTable(nsnull), mInsertionPointTable(nsnull) { NS_INIT_REFCNT(); gRefCnt++; + // printf("REF COUNT UP: %d %s\n", gRefCnt, (const char*)mID); + if (gRefCnt == 1) { kContentAtom = NS_NewAtom("content"); kInterfaceAtom = NS_NewAtom("interface"); @@ -303,6 +306,8 @@ nsXBLBinding::~nsXBLBinding(void) delete mInsertionPointTable; gRefCnt--; + // printf("REF COUNT DOWN: %d %s\n", gRefCnt, (const char*)mID); + if (gRefCnt == 0) { NS_RELEASE(kContentAtom); NS_RELEASE(kInterfaceAtom); @@ -354,6 +359,11 @@ nsXBLBinding::GetBaseBinding(nsIXBLBinding** aResult) NS_IMETHODIMP nsXBLBinding::SetBaseBinding(nsIXBLBinding* aBinding) { + if (mNextBinding) { + NS_ERROR("Base XBL binding is already defined!"); + return NS_OK; + } + mNextBinding = aBinding; // Comptr handles rel/add return NS_OK; } @@ -1454,8 +1464,6 @@ nsXBLBinding::IsInExcludesList(nsIAtom* aTag, const nsString& aList) NS_IMETHODIMP nsXBLBinding::ConstructAttributeTable(nsIContent* aElement) { - // XXX This function still needs to deal with the - // ability to map one attribute to another. nsAutoString inherits; aElement->GetAttribute(kNameSpaceID_None, kInheritsAtom, inherits); if (!inherits.IsEmpty()) { @@ -1745,7 +1753,19 @@ nsXBLBinding::GetFirstStyleBinding(nsIXBLBinding** aResult) return NS_OK; } +NS_IMETHODIMP +nsXBLBinding::MarkForDeath() +{ + mMarkedForDeath = PR_TRUE; + return NS_OK; +} +NS_IMETHODIMP +nsXBLBinding::MarkedForDeath(PRBool* aResult) +{ + *aResult = mMarkedForDeath; + return NS_OK; +} // Creation Routine /////////////////////////////////////////////////////////////////////// diff --git a/layout/xbl/src/nsXBLBinding.h b/layout/xbl/src/nsXBLBinding.h index 0baa738db636..dee3f2755ba5 100644 --- a/layout/xbl/src/nsXBLBinding.h +++ b/layout/xbl/src/nsXBLBinding.h @@ -85,6 +85,9 @@ class nsXBLBinding: public nsIXBLBinding NS_IMETHOD SetAllowScripts(PRBool aFlag) { mAllowScripts = aFlag; return NS_OK; }; + NS_IMETHOD MarkForDeath(); + NS_IMETHOD MarkedForDeath(PRBool* aResult); + public: nsXBLBinding(const nsCString& aDocURI, const nsCString& aRef); virtual ~nsXBLBinding(); @@ -172,9 +175,10 @@ protected: nsIContent* mBoundElement; // [WEAK] We have a reference, but we don't own it. - PRBool mIsStyleBinding; - PRBool mAllowScripts; - PRBool mInheritStyle; + PRPackedBool mIsStyleBinding; + PRPackedBool mAllowScripts; + PRPackedBool mInheritStyle; + PRPackedBool mMarkedForDeath; nsSupportsHashtable* mAttributeTable; // A table for attribute entries. nsSupportsHashtable* mInsertionPointTable; // A table of insertion points. diff --git a/layout/xbl/src/nsXBLService.cpp b/layout/xbl/src/nsXBLService.cpp index 6e70e13ea90b..fbf8de1f3048 100644 --- a/layout/xbl/src/nsXBLService.cpp +++ b/layout/xbl/src/nsXBLService.cpp @@ -25,6 +25,7 @@ #include "nsCOMPtr.h" #include "nsXBLService.h" #include "nsIInputStream.h" +#include "nsINameSpace.h" #include "nsINameSpaceManager.h" #include "nsHashtable.h" #include "nsIURI.h" @@ -37,6 +38,7 @@ #include "nsNetUtil.h" #include "plstr.h" #include "nsIContent.h" +#include "nsIXMLContent.h" #include "nsIDOMElement.h" #include "nsIDocument.h" #include "nsIXMLContentSink.h" @@ -52,6 +54,9 @@ #include "nsIChromeRegistry.h" #include "nsIPref.h" +#include "nsIPresShell.h" +#include "nsIDocumentObserver.h" + #include "nsIXULContentUtils.h" #include "nsIXULPrototypeCache.h" #include "nsIDOMLoadListener.h" @@ -104,55 +109,56 @@ struct nsXBLBindingRequest } } + static void* operator new(size_t aSize, nsFixedSizeAllocator& aAllocator) { + return aAllocator.Alloc(aSize); + } + + static void operator delete(void* aPtr, size_t aSize) { + nsFixedSizeAllocator::Free(aPtr, aSize); + } + void DocumentLoaded(nsIDocument* aBindingDoc) { - // Get the binding. - nsCOMPtr newBinding; - gXBLService->GetBinding(mBoundElement, mBindingURL, getter_AddRefs(newBinding)); - - // XXX Deal with layered bindings. - // XXX Deal with cross-site inheritance (e.g., http://a/a.xml inheriting from http://b/b.xml) - // Install the binding on the content node. nsCOMPtr doc; mBoundElement->GetDocument(*getter_AddRefs(doc)); if (!doc) return; - nsCOMPtr bindingManager; - doc->GetBindingManager(getter_AddRefs(bindingManager)); - bindingManager->SetBinding(mBoundElement, newBinding); - // Set the binding's bound element. - newBinding->SetBoundElement(mBoundElement); + // Get the binding. + PRBool ready = PR_FALSE; + gXBLService->BindingReady(mBoundElement, mBindingURL, &ready); - // Tell the binding to build the anonymous content. - newBinding->GenerateAnonymousContent(mBoundElement); - - // Tell the binding to install event handlers - nsCOMPtr attachReq; - newBinding->InstallEventHandlers(mBoundElement, getter_AddRefs(attachReq)); - - // Set up our properties - newBinding->InstallProperties(mBoundElement); - - if (attachReq) { - attachReq->ExecuteAttachedHandler(); - } + if (!ready) + return; + // XXX Deal with layered bindings. // Now do a ContentInserted notification to cause the frames to get installed finally, nsCOMPtr parent; mBoundElement->GetParent(*getter_AddRefs(parent)); PRInt32 index = 0; if (parent) parent->IndexOf(mBoundElement, index); - if (index == -1) - return; - doc->ContentInserted(parent, mBoundElement, index); + + nsCOMPtr shell = getter_AddRefs(doc->GetShellAt(0)); + if (shell) { + nsCOMPtr obs(do_QueryInterface(shell)); + obs->ContentRemoved(doc, parent, mBoundElement, index); + obs->ContentInserted(doc, parent, mBoundElement, index); + } } static nsIXBLService* gXBLService; static int gRefCnt; }; +static const size_t kBucketSizes[] = { + sizeof(nsXBLBindingRequest) +}; + +static const PRInt32 kNumBuckets = sizeof(kBucketSizes)/sizeof(size_t); +static const PRInt32 kNumElements = 64; +static const PRInt32 kInitialSize = (NS_SIZE_IN_HEAP(sizeof(nsXBLBindingRequest))) * kNumElements; + nsIXBLService* nsXBLBindingRequest::gXBLService = nsnull; int nsXBLBindingRequest::gRefCnt = 0; @@ -180,12 +186,13 @@ public: virtual ~nsXBLStreamListener(); void AddRequest(nsXBLBindingRequest* aRequest) { mBindingRequests.AppendElement(aRequest); }; + PRBool HasRequest(const nsCString& aURI, nsIContent* aBoundElement); private: nsCOMPtr mInner; nsVoidArray mBindingRequests; - nsCOMPtr mDocument; + nsIDocument* mDocument; nsCOMPtr mBindingDocument; }; @@ -264,17 +271,34 @@ nsXBLStreamListener::OnStopRequest(nsIChannel* aChannel, nsISupports* aCtxt, nsr rv = mInner->OnStopRequest(aChannel, aCtxt, aStatus, aStatusArg); } - if (NS_FAILED(rv)) { + if (NS_FAILED(rv) || NS_FAILED(aStatus)) { PRUint32 count = mBindingRequests.Count(); for (PRUint32 i = 0; i < count; i++) { nsXBLBindingRequest* req = (nsXBLBindingRequest*)mBindingRequests.ElementAt(i); delete req; } + + mDocument = nsnull; + mBindingDocument = nsnull; } return rv; } +PRBool +nsXBLStreamListener::HasRequest(const nsCString& aURI, nsIContent* aElt) +{ + // XXX Could be more efficient. + PRUint32 count = mBindingRequests.Count(); + for (PRUint32 i = 0; i < count; i++) { + nsXBLBindingRequest* req = (nsXBLBindingRequest*)mBindingRequests.ElementAt(i); + if (req->mBindingURL.Equals(aURI) && req->mBoundElement.get() == aElt) + return PR_TRUE; + } + + return PR_FALSE; +} + nsresult nsXBLStreamListener::Load(nsIDOMEvent* aEvent) { @@ -312,8 +336,9 @@ nsXBLStreamListener::Load(nsIDOMEvent* aEvent) info->SetScriptAccess(allow); } } - - bindingManager->PutXBLDocumentInfo(info); + + if (!cached) + bindingManager->PutXBLDocumentInfo(info); // Notify all pending requests that their bindings are // ready and can be installed. @@ -328,9 +353,12 @@ nsXBLStreamListener::Load(nsIDOMEvent* aEvent) delete req; } - nsCOMPtr rec(do_QueryInterface(mDocument)); + nsCOMPtr rec(do_QueryInterface(mBindingDocument)); rec->RemoveEventListener(NS_ConvertASCIItoUCS2("load"), (nsIDOMLoadListener*)this, PR_FALSE); + mDocument = nsnull; + mBindingDocument = nsnull; + return rv; } @@ -406,7 +434,7 @@ PRUint32 nsXBLService::gClassLRUListLength = 0; PRUint32 nsXBLService::gClassLRUListQuota = 64; nsIAtom* nsXBLService::kExtendsAtom = nsnull; -nsIAtom* nsXBLService::kHasChildrenAtom = nsnull; +nsIAtom* nsXBLService::kScrollbarAtom = nsnull; // Enabled by default. Must be over-ridden to disable PRBool nsXBLService::gDisableChromeCache = PR_FALSE; @@ -421,6 +449,8 @@ NS_IMPL_ISUPPORTS2(nsXBLService, nsIXBLService, nsIMemoryPressureObserver) nsXBLService::nsXBLService(void) { NS_INIT_REFCNT(); + mPool.Init("XBL Binding Requests", kBucketSizes, kNumBuckets, kInitialSize); + gRefCnt++; if (gRefCnt == 1) { @@ -442,8 +472,8 @@ nsXBLService::nsXBLService(void) // Create our atoms kExtendsAtom = NS_NewAtom("extends"); - kHasChildrenAtom = NS_NewAtom("haschildren"); - + kScrollbarAtom = NS_NewAtom("scrollbar"); + // Find out if the XUL cache is on or off NS_WITH_SERVICE(nsIPref, prefs, NS_PREF_PROGID, &rv); if (NS_SUCCEEDED(rv)) @@ -476,8 +506,8 @@ nsXBLService::~nsXBLService(void) // Release our atoms NS_RELEASE(kExtendsAtom); - NS_RELEASE(kHasChildrenAtom); - + NS_RELEASE(kScrollbarAtom); + // Walk the LRU list removing and deleting the nsXBLJSClasses. FlushMemory(REASON_HEAP_MINIMIZE, 0); @@ -526,15 +556,23 @@ nsXBLService::LoadBindings(nsIContent* aContent, const nsString& aURL, PRBool aA nsCOMPtr styleBinding; binding->GetFirstStyleBinding(getter_AddRefs(styleBinding)); if (styleBinding) { - // See if the URIs match. - nsCAutoString uri; - styleBinding->GetBindingURI(uri); - if (uri.EqualsWithConversion(aURL)) - return NS_OK; - else { + PRBool marked = PR_FALSE; + binding->MarkedForDeath(&marked); + if (marked) { FlushStyleBindings(aContent); binding = nsnull; } + else { + // See if the URIs match. + nsCAutoString uri; + styleBinding->GetBindingURI(uri); + if (uri.EqualsWithConversion(aURL)) + return NS_OK; + else { + FlushStyleBindings(aContent); + binding = nsnull; + } + } } } @@ -736,9 +774,29 @@ nsXBLService::FlushMemory(PRUint32 reason, size_t requestedAmount) // Internal helper methods //////////////////////////////////////////////////////////////// -NS_IMETHODIMP nsXBLService::GetBinding(nsIContent* aBoundElement, const nsCString& aURLStr, nsIXBLBinding** aResult) +NS_IMETHODIMP nsXBLService::GetBinding(nsIContent* aBoundElement, + const nsCString& aURLStr, + nsIXBLBinding** aResult) { - *aResult = nsnull; + PRBool dummy; + return GetBindingInternal(aBoundElement, aURLStr, PR_FALSE, &dummy, aResult); +} + +NS_IMETHODIMP nsXBLService::BindingReady(nsIContent* aBoundElement, + const nsCString& aURLStr, + PRBool* aIsReady) +{ + return GetBindingInternal(aBoundElement, aURLStr, PR_TRUE, aIsReady, nsnull); +} + +NS_IMETHODIMP nsXBLService::GetBindingInternal(nsIContent* aBoundElement, + const nsCString& aURLStr, + PRBool aPeekOnly, + PRBool* aIsReady, + nsIXBLBinding** aResult) +{ + if (aResult) + *aResult = nsnull; if (aURLStr.IsEmpty()) return NS_ERROR_FAILURE; @@ -781,32 +839,53 @@ NS_IMETHODIMP nsXBLService::GetBinding(nsIContent* aBoundElement, const nsCStrin child->GetAttribute(kNameSpaceID_None, nsHTMLAtoms::id, value); // If no ref is specified just use this. - if ((bindingName.IsEmpty()) || (bindingName == value)) { - // Make a new binding - NS_NewXBLBinding(uri, ref, aResult); - - // Initialize its bound element. - (*aResult)->SetBindingElement(child); - (*aResult)->SetAllowScripts(allowScripts); - + if ((bindingName.IsEmpty()) || (bindingName == value)) { // Check for the presence of an extends attribute - child->GetAttribute(kNameSpaceID_None, kExtendsAtom, value); - if (!value.IsEmpty()) { - // See if we are extending a builtin tag. - nsCOMPtr tag; - PRInt32 dummy; - (*aResult)->GetBaseTag(&dummy, getter_AddRefs(tag)); - if (!tag) { - // We have a base class binding. Load it right now. - nsCOMPtr baseBinding; - nsCAutoString urlCString; urlCString.AssignWithConversion(value); - GetBinding(aBoundElement, urlCString, getter_AddRefs(baseBinding)); - if (!baseBinding) - return NS_OK; // At least we got the derived class binding loaded. - (*aResult)->SetBaseBinding(baseBinding); + nsAutoString extends; + nsCOMPtr baseBinding; + child->GetAttribute(kNameSpaceID_None, kExtendsAtom, extends); + value = extends; + if (!extends.IsEmpty()) { + nsAutoString prefix; + PRInt32 offset = extends.FindChar(':'); + if (-1 != offset) { + extends.Left(prefix, offset); + extends.Cut(0, offset+1); + } + if (prefix.Length() > 0) { + // Look up the prefix. + nsCOMPtr prefixAtom = getter_AddRefs(NS_NewAtom(prefix)); + nsCOMPtr nameSpace; + nsCOMPtr xmlContent(do_QueryInterface(child)); + if (xmlContent) { + xmlContent->GetContainingNameSpace(*getter_AddRefs(nameSpace)); + if (nameSpace) { + nsCOMPtr tagSpace; + nameSpace->FindNameSpace(prefixAtom, *getter_AddRefs(tagSpace)); + if (!tagSpace) { + // We have a base class binding. Load it right now. + nsCAutoString urlCString; urlCString.AssignWithConversion(value); + GetBindingInternal(aBoundElement, urlCString, aPeekOnly, aIsReady, getter_AddRefs(baseBinding)); + if (!*aIsReady) + return NS_ERROR_FAILURE; // Binding not yet ready or an error occurred. + } + } + } } } + *aIsReady = PR_TRUE; + if (!aPeekOnly) { + // Make a new binding + NS_NewXBLBinding(uri, ref, aResult); + + // Initialize its bound element. + (*aResult)->SetBindingElement(child); + (*aResult)->SetAllowScripts(allowScripts); + + if (baseBinding) + (*aResult)->SetBaseBinding(baseBinding); + } break; } } @@ -839,7 +918,9 @@ nsXBLService::GetBindingDocumentInfo(nsIContent* aBoundElement, const nsCString& boundDocument->GetBindingManager(getter_AddRefs(bindingManager)); bindingManager->GetXBLDocumentInfo(aURLStr, getter_AddRefs(info)); - if (!info) { + nsCOMPtr tagName; + aBoundElement->GetTag(*getter_AddRefs(tagName)); + if (!info && (tagName.get() != kScrollbarAtom)) { // The third line of defense is to investigate whether or not the // document is currently being loaded asynchronously. If so, there's no // document yet, but we need to glom on our request so that it will be @@ -847,14 +928,16 @@ nsXBLService::GetBindingDocumentInfo(nsIContent* aBoundElement, const nsCString& nsCOMPtr listener; bindingManager->GetLoadingDocListener(aURLStr, getter_AddRefs(listener)); if (listener) { + nsIStreamListener* ilist = listener.get(); + nsXBLStreamListener* xblListener = NS_STATIC_CAST(nsXBLStreamListener*, ilist); // Create a new load observer. nsCAutoString bindingURI(aURLStr); bindingURI += "#"; bindingURI += aRef; - nsXBLBindingRequest* req = new nsXBLBindingRequest(aRef, aBoundElement); - nsIStreamListener* ilist = listener.get(); - nsXBLStreamListener* xblListener = NS_STATIC_CAST(nsXBLStreamListener*, ilist); - xblListener->AddRequest(req); + if (!xblListener->HasRequest(bindingURI, aBoundElement)) { + nsXBLBindingRequest* req = new (mPool) nsXBLBindingRequest(bindingURI, aBoundElement); + xblListener->AddRequest(req); + } return NS_OK; } } @@ -947,11 +1030,12 @@ nsXBLService::FetchBindingDocument(nsIContent* aBoundElement, nsIURI* aURI, cons return rv; } - if (!IsChromeURI(aURI)) { + nsCOMPtr tagName; + aBoundElement->GetTag(*getter_AddRefs(tagName)); + if (tagName != kScrollbarAtom) { // We can be asynchronous nsXBLStreamListener* xblListener = new nsXBLStreamListener(listener, boundDoc, doc); - NS_ADDREF(xblListener); - + nsCOMPtr rec(do_QueryInterface(doc)); rec->AddEventListener(NS_ConvertASCIItoUCS2("load"), (nsIDOMLoadListener*)xblListener, PR_FALSE); @@ -968,7 +1052,7 @@ nsXBLService::FetchBindingDocument(nsIContent* aBoundElement, nsIURI* aURI, cons nsCAutoString bindingURI(uri); bindingURI += "#"; bindingURI += aRef; - nsXBLBindingRequest* req = new nsXBLBindingRequest(bindingURI, aBoundElement); + nsXBLBindingRequest* req = new (mPool) nsXBLBindingRequest(bindingURI, aBoundElement); xblListener->AddRequest(req); // Now kick off the async read. diff --git a/layout/xbl/src/nsXBLService.h b/layout/xbl/src/nsXBLService.h index ee84f43973f6..22209f3703d0 100644 --- a/layout/xbl/src/nsXBLService.h +++ b/layout/xbl/src/nsXBLService.h @@ -28,6 +28,7 @@ #include "nsIMemory.h" #include "jsapi.h" // nsXBLJSClass derives from JSClass #include "jsclist.h" // nsXBLJSClass derives from JSCList +#include "nsFixedSizeAllocator.h" class nsIXBLBinding; class nsIXBLDocumentInfo; @@ -54,6 +55,9 @@ class nsXBLService : public nsIXBLService, public nsIMemoryPressureObserver // This method loads a binding doc and then builds the specific binding required. NS_IMETHOD GetBinding(nsIContent* aBoundElement, const nsCString& aURLStr, nsIXBLBinding** aResult); + // Indicates whether or not a binding is fully loaded. + NS_IMETHOD BindingReady(nsIContent* aBoundElement, const nsCString& aURLStr, PRBool* aIsReady); + // This function clears out the bindings on a given content node. NS_IMETHOD FlushStyleBindings(nsIContent* aContent); @@ -80,6 +84,11 @@ public: // This method synchronously loads and parses an XBL file. NS_IMETHOD FetchBindingDocument(nsIContent* aBoundElement, nsIURI* aURI, const nsCString& aRef, nsIDocument** aResult); + // This method loads a binding doc and then builds the specific binding required. It + // can also peek without building. + NS_IMETHOD GetBindingInternal(nsIContent* aBoundElement, const nsCString& aURLStr, + PRBool aPeekFlag, PRBool* aIsReady, nsIXBLBinding** aResult); + // This method walks a binding document and removes any text nodes // that contain only whitespace. static nsresult StripWhitespaceNodes(nsIContent* aContent); @@ -104,7 +113,10 @@ public: // XBL Atoms static nsIAtom* kExtendsAtom; - static nsIAtom* kHasChildrenAtom; + + static nsIAtom* kScrollbarAtom; + + nsFixedSizeAllocator mPool; }; class nsXBLJSClass : public JSCList, public JSClass diff --git a/rdf/chrome/src/nsChromeRegistry.cpp b/rdf/chrome/src/nsChromeRegistry.cpp index 0f07d5bf0bd6..ff1a1f01c756 100644 --- a/rdf/chrome/src/nsChromeRegistry.cpp +++ b/rdf/chrome/src/nsChromeRegistry.cpp @@ -74,6 +74,7 @@ #include "nsLayoutCID.h" #include "nsGfxCIID.h" #include "nsIImageManager.h" +#include "nsIBindingManager.h" #include "prio.h" static char kChromePrefix[] = "chrome://"; @@ -1044,6 +1045,11 @@ NS_IMETHODIMP nsChromeRegistry::RefreshWindow(nsIDOMWindow* aWindow) if (!document) return NS_OK; + // Annihilate all XBL bindings. + nsCOMPtr bindingManager; + document->GetBindingManager(getter_AddRefs(bindingManager)); + bindingManager->FlushChromeBindings(); + nsCOMPtr xulDoc = do_QueryInterface(domDocument); if (xulDoc) { // Deal with the backstop sheets first.