Work on 48150 and XBL performance (async and arenas).

This commit is contained in:
hyatt%netscape.com 2000-08-14 04:04:18 +00:00
parent 75afe2361d
commit a489c05fae
20 changed files with 500 additions and 180 deletions

View File

@ -74,6 +74,7 @@
#include "nsLayoutCID.h" #include "nsLayoutCID.h"
#include "nsGfxCIID.h" #include "nsGfxCIID.h"
#include "nsIImageManager.h" #include "nsIImageManager.h"
#include "nsIBindingManager.h"
#include "prio.h" #include "prio.h"
static char kChromePrefix[] = "chrome://"; static char kChromePrefix[] = "chrome://";
@ -1044,6 +1045,11 @@ NS_IMETHODIMP nsChromeRegistry::RefreshWindow(nsIDOMWindow* aWindow)
if (!document) if (!document)
return NS_OK; return NS_OK;
// Annihilate all XBL bindings.
nsCOMPtr<nsIBindingManager> bindingManager;
document->GetBindingManager(getter_AddRefs(bindingManager));
bindingManager->FlushChromeBindings();
nsCOMPtr<nsIXULDocument> xulDoc = do_QueryInterface(domDocument); nsCOMPtr<nsIXULDocument> xulDoc = do_QueryInterface(domDocument);
if (xulDoc) { if (xulDoc) {
// Deal with the backstop sheets first. // Deal with the backstop sheets first.

View File

@ -74,6 +74,7 @@ public:
NS_IMETHOD RemoveLoadingDocListener(const nsCString& aURL)=0; NS_IMETHOD RemoveLoadingDocListener(const nsCString& aURL)=0;
NS_IMETHOD InheritsStyle(nsIContent* aContent, PRBool* aResult) = 0; NS_IMETHOD InheritsStyle(nsIContent* aContent, PRBool* aResult) = 0;
NS_IMETHOD FlushChromeBindings() = 0;
}; };
#endif // nsIBinding_Manager_h__ #endif // nsIBinding_Manager_h__

View File

@ -91,6 +91,9 @@ public:
NS_IMETHOD WalkRules(nsISupportsArrayEnumFunc aFunc, void* aData)=0; NS_IMETHOD WalkRules(nsISupportsArrayEnumFunc aFunc, void* aData)=0;
NS_IMETHOD SetAllowScripts(PRBool aFlag)=0; NS_IMETHOD SetAllowScripts(PRBool aFlag)=0;
NS_IMETHOD MarkForDeath()=0;
NS_IMETHOD MarkedForDeath(PRBool* aResult)=0;
}; };
extern nsresult extern nsresult

View File

@ -59,6 +59,9 @@ public:
// This method loads a binding doc and then builds the specific binding required. // This method loads a binding doc and then builds the specific binding required.
NS_IMETHOD GetBinding(nsIContent* aBoundElement, const nsCString& aURLStr, nsIXBLBinding** aResult) = 0; 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 // For a given element, returns a flat list of all the anonymous children that need
// frames built. // frames built.
NS_IMETHOD GetContentList(nsIContent* aContent, nsISupportsArray** aResult, nsIContent** aChildElement, NS_IMETHOD GetContentList(nsIContent* aContent, nsISupportsArray** aResult, nsIContent** aChildElement,

View File

@ -178,6 +178,7 @@ public:
NS_IMETHOD RemoveLoadingDocListener(const nsCString& aURL); NS_IMETHOD RemoveLoadingDocListener(const nsCString& aURL);
NS_IMETHOD InheritsStyle(nsIContent* aContent, PRBool* aResult); NS_IMETHOD InheritsStyle(nsIContent* aContent, PRBool* aResult);
NS_IMETHOD FlushChromeBindings();
// nsIStyleRuleSupplier // nsIStyleRuleSupplier
NS_IMETHOD UseDocumentRules(nsIContent* aContent, PRBool* aResult); NS_IMETHOD UseDocumentRules(nsIContent* aContent, PRBool* aResult);
@ -252,8 +253,13 @@ nsBindingManager::SetBinding(nsIContent* aContent, nsIXBLBinding* aBinding )
mBindingTable = new nsSupportsHashtable; mBindingTable = new nsSupportsHashtable;
nsISupportsKey key(aContent); nsISupportsKey key(aContent);
nsCOMPtr<nsISupports> old = getter_AddRefs(mBindingTable->Get(&key));
if (old && aBinding)
NS_ERROR("Binding already installed!");
if (aBinding) { if (aBinding) {
mBindingTable->Put (&key, aBinding); mBindingTable->Put(&key, aBinding);
} }
else else
mBindingTable->Remove(&key); mBindingTable->Remove(&key);
@ -468,6 +474,23 @@ nsBindingManager::RemoveLoadingDocListener(const nsCString& aURL)
return NS_OK; 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 NS_IMETHODIMP
nsBindingManager::InheritsStyle(nsIContent* aContent, PRBool* aResult) nsBindingManager::InheritsStyle(nsIContent* aContent, PRBool* aResult)
{ {

View File

@ -252,11 +252,14 @@ nsXBLBinding::nsXBLBinding(const nsCString& aDocURI, const nsCString& aID)
mIsStyleBinding(PR_TRUE), mIsStyleBinding(PR_TRUE),
mAllowScripts(PR_TRUE), mAllowScripts(PR_TRUE),
mInheritStyle(PR_TRUE), mInheritStyle(PR_TRUE),
mMarkedForDeath(PR_FALSE),
mAttributeTable(nsnull), mAttributeTable(nsnull),
mInsertionPointTable(nsnull) mInsertionPointTable(nsnull)
{ {
NS_INIT_REFCNT(); NS_INIT_REFCNT();
gRefCnt++; gRefCnt++;
// printf("REF COUNT UP: %d %s\n", gRefCnt, (const char*)mID);
if (gRefCnt == 1) { if (gRefCnt == 1) {
kContentAtom = NS_NewAtom("content"); kContentAtom = NS_NewAtom("content");
kInterfaceAtom = NS_NewAtom("interface"); kInterfaceAtom = NS_NewAtom("interface");
@ -303,6 +306,8 @@ nsXBLBinding::~nsXBLBinding(void)
delete mInsertionPointTable; delete mInsertionPointTable;
gRefCnt--; gRefCnt--;
// printf("REF COUNT DOWN: %d %s\n", gRefCnt, (const char*)mID);
if (gRefCnt == 0) { if (gRefCnt == 0) {
NS_RELEASE(kContentAtom); NS_RELEASE(kContentAtom);
NS_RELEASE(kInterfaceAtom); NS_RELEASE(kInterfaceAtom);
@ -354,6 +359,11 @@ nsXBLBinding::GetBaseBinding(nsIXBLBinding** aResult)
NS_IMETHODIMP NS_IMETHODIMP
nsXBLBinding::SetBaseBinding(nsIXBLBinding* aBinding) nsXBLBinding::SetBaseBinding(nsIXBLBinding* aBinding)
{ {
if (mNextBinding) {
NS_ERROR("Base XBL binding is already defined!");
return NS_OK;
}
mNextBinding = aBinding; // Comptr handles rel/add mNextBinding = aBinding; // Comptr handles rel/add
return NS_OK; return NS_OK;
} }
@ -1454,8 +1464,6 @@ nsXBLBinding::IsInExcludesList(nsIAtom* aTag, const nsString& aList)
NS_IMETHODIMP NS_IMETHODIMP
nsXBLBinding::ConstructAttributeTable(nsIContent* aElement) nsXBLBinding::ConstructAttributeTable(nsIContent* aElement)
{ {
// XXX This function still needs to deal with the
// ability to map one attribute to another.
nsAutoString inherits; nsAutoString inherits;
aElement->GetAttribute(kNameSpaceID_None, kInheritsAtom, inherits); aElement->GetAttribute(kNameSpaceID_None, kInheritsAtom, inherits);
if (!inherits.IsEmpty()) { if (!inherits.IsEmpty()) {
@ -1745,7 +1753,19 @@ nsXBLBinding::GetFirstStyleBinding(nsIXBLBinding** aResult)
return NS_OK; 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 /////////////////////////////////////////////////////////////////////// // Creation Routine ///////////////////////////////////////////////////////////////////////

View File

@ -85,6 +85,9 @@ class nsXBLBinding: public nsIXBLBinding
NS_IMETHOD SetAllowScripts(PRBool aFlag) { mAllowScripts = aFlag; return NS_OK; }; NS_IMETHOD SetAllowScripts(PRBool aFlag) { mAllowScripts = aFlag; return NS_OK; };
NS_IMETHOD MarkForDeath();
NS_IMETHOD MarkedForDeath(PRBool* aResult);
public: public:
nsXBLBinding(const nsCString& aDocURI, const nsCString& aRef); nsXBLBinding(const nsCString& aDocURI, const nsCString& aRef);
virtual ~nsXBLBinding(); virtual ~nsXBLBinding();
@ -172,9 +175,10 @@ protected:
nsIContent* mBoundElement; // [WEAK] We have a reference, but we don't own it. nsIContent* mBoundElement; // [WEAK] We have a reference, but we don't own it.
PRBool mIsStyleBinding; PRPackedBool mIsStyleBinding;
PRBool mAllowScripts; PRPackedBool mAllowScripts;
PRBool mInheritStyle; PRPackedBool mInheritStyle;
PRPackedBool mMarkedForDeath;
nsSupportsHashtable* mAttributeTable; // A table for attribute entries. nsSupportsHashtable* mAttributeTable; // A table for attribute entries.
nsSupportsHashtable* mInsertionPointTable; // A table of insertion points. nsSupportsHashtable* mInsertionPointTable; // A table of insertion points.

View File

@ -25,6 +25,7 @@
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "nsXBLService.h" #include "nsXBLService.h"
#include "nsIInputStream.h" #include "nsIInputStream.h"
#include "nsINameSpace.h"
#include "nsINameSpaceManager.h" #include "nsINameSpaceManager.h"
#include "nsHashtable.h" #include "nsHashtable.h"
#include "nsIURI.h" #include "nsIURI.h"
@ -37,6 +38,7 @@
#include "nsNetUtil.h" #include "nsNetUtil.h"
#include "plstr.h" #include "plstr.h"
#include "nsIContent.h" #include "nsIContent.h"
#include "nsIXMLContent.h"
#include "nsIDOMElement.h" #include "nsIDOMElement.h"
#include "nsIDocument.h" #include "nsIDocument.h"
#include "nsIXMLContentSink.h" #include "nsIXMLContentSink.h"
@ -52,6 +54,9 @@
#include "nsIChromeRegistry.h" #include "nsIChromeRegistry.h"
#include "nsIPref.h" #include "nsIPref.h"
#include "nsIPresShell.h"
#include "nsIDocumentObserver.h"
#include "nsIXULContentUtils.h" #include "nsIXULContentUtils.h"
#include "nsIXULPrototypeCache.h" #include "nsIXULPrototypeCache.h"
#include "nsIDOMLoadListener.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) void DocumentLoaded(nsIDocument* aBindingDoc)
{ {
// Get the binding.
nsCOMPtr<nsIXBLBinding> 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<nsIDocument> doc; nsCOMPtr<nsIDocument> doc;
mBoundElement->GetDocument(*getter_AddRefs(doc)); mBoundElement->GetDocument(*getter_AddRefs(doc));
if (!doc) if (!doc)
return; return;
nsCOMPtr<nsIBindingManager> bindingManager;
doc->GetBindingManager(getter_AddRefs(bindingManager));
bindingManager->SetBinding(mBoundElement, newBinding);
// Set the binding's bound element. // Get the binding.
newBinding->SetBoundElement(mBoundElement); PRBool ready = PR_FALSE;
gXBLService->BindingReady(mBoundElement, mBindingURL, &ready);
// Tell the binding to build the anonymous content. if (!ready)
newBinding->GenerateAnonymousContent(mBoundElement); return;
// Tell the binding to install event handlers
nsCOMPtr<nsIXBLBinding> attachReq;
newBinding->InstallEventHandlers(mBoundElement, getter_AddRefs(attachReq));
// Set up our properties
newBinding->InstallProperties(mBoundElement);
if (attachReq) {
attachReq->ExecuteAttachedHandler();
}
// XXX Deal with layered bindings.
// Now do a ContentInserted notification to cause the frames to get installed finally, // Now do a ContentInserted notification to cause the frames to get installed finally,
nsCOMPtr<nsIContent> parent; nsCOMPtr<nsIContent> parent;
mBoundElement->GetParent(*getter_AddRefs(parent)); mBoundElement->GetParent(*getter_AddRefs(parent));
PRInt32 index = 0; PRInt32 index = 0;
if (parent) if (parent)
parent->IndexOf(mBoundElement, index); parent->IndexOf(mBoundElement, index);
if (index == -1)
return; nsCOMPtr<nsIPresShell> shell = getter_AddRefs(doc->GetShellAt(0));
doc->ContentInserted(parent, mBoundElement, index); if (shell) {
nsCOMPtr<nsIDocumentObserver> obs(do_QueryInterface(shell));
obs->ContentRemoved(doc, parent, mBoundElement, index);
obs->ContentInserted(doc, parent, mBoundElement, index);
}
} }
static nsIXBLService* gXBLService; static nsIXBLService* gXBLService;
static int gRefCnt; 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; nsIXBLService* nsXBLBindingRequest::gXBLService = nsnull;
int nsXBLBindingRequest::gRefCnt = 0; int nsXBLBindingRequest::gRefCnt = 0;
@ -180,12 +186,13 @@ public:
virtual ~nsXBLStreamListener(); virtual ~nsXBLStreamListener();
void AddRequest(nsXBLBindingRequest* aRequest) { mBindingRequests.AppendElement(aRequest); }; void AddRequest(nsXBLBindingRequest* aRequest) { mBindingRequests.AppendElement(aRequest); };
PRBool HasRequest(const nsCString& aURI, nsIContent* aBoundElement);
private: private:
nsCOMPtr<nsIStreamListener> mInner; nsCOMPtr<nsIStreamListener> mInner;
nsVoidArray mBindingRequests; nsVoidArray mBindingRequests;
nsCOMPtr<nsIDocument> mDocument; nsIDocument* mDocument;
nsCOMPtr<nsIDocument> mBindingDocument; nsCOMPtr<nsIDocument> mBindingDocument;
}; };
@ -264,17 +271,34 @@ nsXBLStreamListener::OnStopRequest(nsIChannel* aChannel, nsISupports* aCtxt, nsr
rv = mInner->OnStopRequest(aChannel, aCtxt, aStatus, aStatusArg); rv = mInner->OnStopRequest(aChannel, aCtxt, aStatus, aStatusArg);
} }
if (NS_FAILED(rv)) { if (NS_FAILED(rv) || NS_FAILED(aStatus)) {
PRUint32 count = mBindingRequests.Count(); PRUint32 count = mBindingRequests.Count();
for (PRUint32 i = 0; i < count; i++) { for (PRUint32 i = 0; i < count; i++) {
nsXBLBindingRequest* req = (nsXBLBindingRequest*)mBindingRequests.ElementAt(i); nsXBLBindingRequest* req = (nsXBLBindingRequest*)mBindingRequests.ElementAt(i);
delete req; delete req;
} }
mDocument = nsnull;
mBindingDocument = nsnull;
} }
return rv; 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 nsresult
nsXBLStreamListener::Load(nsIDOMEvent* aEvent) nsXBLStreamListener::Load(nsIDOMEvent* aEvent)
{ {
@ -313,6 +337,7 @@ nsXBLStreamListener::Load(nsIDOMEvent* aEvent)
} }
} }
if (!cached)
bindingManager->PutXBLDocumentInfo(info); bindingManager->PutXBLDocumentInfo(info);
// Notify all pending requests that their bindings are // Notify all pending requests that their bindings are
@ -328,9 +353,12 @@ nsXBLStreamListener::Load(nsIDOMEvent* aEvent)
delete req; delete req;
} }
nsCOMPtr<nsIDOMEventReceiver> rec(do_QueryInterface(mDocument)); nsCOMPtr<nsIDOMEventReceiver> rec(do_QueryInterface(mBindingDocument));
rec->RemoveEventListener(NS_ConvertASCIItoUCS2("load"), (nsIDOMLoadListener*)this, PR_FALSE); rec->RemoveEventListener(NS_ConvertASCIItoUCS2("load"), (nsIDOMLoadListener*)this, PR_FALSE);
mDocument = nsnull;
mBindingDocument = nsnull;
return rv; return rv;
} }
@ -406,7 +434,7 @@ PRUint32 nsXBLService::gClassLRUListLength = 0;
PRUint32 nsXBLService::gClassLRUListQuota = 64; PRUint32 nsXBLService::gClassLRUListQuota = 64;
nsIAtom* nsXBLService::kExtendsAtom = nsnull; nsIAtom* nsXBLService::kExtendsAtom = nsnull;
nsIAtom* nsXBLService::kHasChildrenAtom = nsnull; nsIAtom* nsXBLService::kScrollbarAtom = nsnull;
// Enabled by default. Must be over-ridden to disable // Enabled by default. Must be over-ridden to disable
PRBool nsXBLService::gDisableChromeCache = PR_FALSE; PRBool nsXBLService::gDisableChromeCache = PR_FALSE;
@ -421,6 +449,8 @@ NS_IMPL_ISUPPORTS2(nsXBLService, nsIXBLService, nsIMemoryPressureObserver)
nsXBLService::nsXBLService(void) nsXBLService::nsXBLService(void)
{ {
NS_INIT_REFCNT(); NS_INIT_REFCNT();
mPool.Init("XBL Binding Requests", kBucketSizes, kNumBuckets, kInitialSize);
gRefCnt++; gRefCnt++;
if (gRefCnt == 1) { if (gRefCnt == 1) {
@ -442,7 +472,7 @@ nsXBLService::nsXBLService(void)
// Create our atoms // Create our atoms
kExtendsAtom = NS_NewAtom("extends"); kExtendsAtom = NS_NewAtom("extends");
kHasChildrenAtom = NS_NewAtom("haschildren"); kScrollbarAtom = NS_NewAtom("scrollbar");
// Find out if the XUL cache is on or off // Find out if the XUL cache is on or off
NS_WITH_SERVICE(nsIPref, prefs, NS_PREF_PROGID, &rv); NS_WITH_SERVICE(nsIPref, prefs, NS_PREF_PROGID, &rv);
@ -476,7 +506,7 @@ nsXBLService::~nsXBLService(void)
// Release our atoms // Release our atoms
NS_RELEASE(kExtendsAtom); NS_RELEASE(kExtendsAtom);
NS_RELEASE(kHasChildrenAtom); NS_RELEASE(kScrollbarAtom);
// Walk the LRU list removing and deleting the nsXBLJSClasses. // Walk the LRU list removing and deleting the nsXBLJSClasses.
FlushMemory(REASON_HEAP_MINIMIZE, 0); FlushMemory(REASON_HEAP_MINIMIZE, 0);
@ -526,6 +556,13 @@ nsXBLService::LoadBindings(nsIContent* aContent, const nsString& aURL, PRBool aA
nsCOMPtr<nsIXBLBinding> styleBinding; nsCOMPtr<nsIXBLBinding> styleBinding;
binding->GetFirstStyleBinding(getter_AddRefs(styleBinding)); binding->GetFirstStyleBinding(getter_AddRefs(styleBinding));
if (styleBinding) { if (styleBinding) {
PRBool marked = PR_FALSE;
binding->MarkedForDeath(&marked);
if (marked) {
FlushStyleBindings(aContent);
binding = nsnull;
}
else {
// See if the URIs match. // See if the URIs match.
nsCAutoString uri; nsCAutoString uri;
styleBinding->GetBindingURI(uri); styleBinding->GetBindingURI(uri);
@ -537,6 +574,7 @@ nsXBLService::LoadBindings(nsIContent* aContent, const nsString& aURL, PRBool aA
} }
} }
} }
}
nsCOMPtr<nsIXBLBinding> newBinding; nsCOMPtr<nsIXBLBinding> newBinding;
nsCAutoString url; url.AssignWithConversion(aURL); nsCAutoString url; url.AssignWithConversion(aURL);
@ -736,8 +774,28 @@ nsXBLService::FlushMemory(PRUint32 reason, size_t requestedAmount)
// Internal helper methods //////////////////////////////////////////////////////////////// // 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)
{ {
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; *aResult = nsnull;
if (aURLStr.IsEmpty()) if (aURLStr.IsEmpty())
@ -782,6 +840,42 @@ NS_IMETHODIMP nsXBLService::GetBinding(nsIContent* aBoundElement, const nsCStrin
// If no ref is specified just use this. // If no ref is specified just use this.
if ((bindingName.IsEmpty()) || (bindingName == value)) { if ((bindingName.IsEmpty()) || (bindingName == value)) {
// Check for the presence of an extends attribute
nsAutoString extends;
nsCOMPtr<nsIXBLBinding> 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<nsIAtom> prefixAtom = getter_AddRefs(NS_NewAtom(prefix));
nsCOMPtr<nsINameSpace> nameSpace;
nsCOMPtr<nsIXMLContent> xmlContent(do_QueryInterface(child));
if (xmlContent) {
xmlContent->GetContainingNameSpace(*getter_AddRefs(nameSpace));
if (nameSpace) {
nsCOMPtr<nsINameSpace> 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 // Make a new binding
NS_NewXBLBinding(uri, ref, aResult); NS_NewXBLBinding(uri, ref, aResult);
@ -789,24 +883,9 @@ NS_IMETHODIMP nsXBLService::GetBinding(nsIContent* aBoundElement, const nsCStrin
(*aResult)->SetBindingElement(child); (*aResult)->SetBindingElement(child);
(*aResult)->SetAllowScripts(allowScripts); (*aResult)->SetAllowScripts(allowScripts);
// Check for the presence of an extends attribute if (baseBinding)
child->GetAttribute(kNameSpaceID_None, kExtendsAtom, value);
if (!value.IsEmpty()) {
// See if we are extending a builtin tag.
nsCOMPtr<nsIAtom> tag;
PRInt32 dummy;
(*aResult)->GetBaseTag(&dummy, getter_AddRefs(tag));
if (!tag) {
// We have a base class binding. Load it right now.
nsCOMPtr<nsIXBLBinding> 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); (*aResult)->SetBaseBinding(baseBinding);
} }
}
break; break;
} }
} }
@ -839,7 +918,9 @@ nsXBLService::GetBindingDocumentInfo(nsIContent* aBoundElement, const nsCString&
boundDocument->GetBindingManager(getter_AddRefs(bindingManager)); boundDocument->GetBindingManager(getter_AddRefs(bindingManager));
bindingManager->GetXBLDocumentInfo(aURLStr, getter_AddRefs(info)); bindingManager->GetXBLDocumentInfo(aURLStr, getter_AddRefs(info));
if (!info) { nsCOMPtr<nsIAtom> tagName;
aBoundElement->GetTag(*getter_AddRefs(tagName));
if (!info && (tagName.get() != kScrollbarAtom)) {
// The third line of defense is to investigate whether or not the // The third line of defense is to investigate whether or not the
// document is currently being loaded asynchronously. If so, there's no // 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 // 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<nsIStreamListener> listener; nsCOMPtr<nsIStreamListener> listener;
bindingManager->GetLoadingDocListener(aURLStr, getter_AddRefs(listener)); bindingManager->GetLoadingDocListener(aURLStr, getter_AddRefs(listener));
if (listener) { if (listener) {
nsIStreamListener* ilist = listener.get();
nsXBLStreamListener* xblListener = NS_STATIC_CAST(nsXBLStreamListener*, ilist);
// Create a new load observer. // Create a new load observer.
nsCAutoString bindingURI(aURLStr); nsCAutoString bindingURI(aURLStr);
bindingURI += "#"; bindingURI += "#";
bindingURI += aRef; bindingURI += aRef;
nsXBLBindingRequest* req = new nsXBLBindingRequest(aRef, aBoundElement); if (!xblListener->HasRequest(bindingURI, aBoundElement)) {
nsIStreamListener* ilist = listener.get(); nsXBLBindingRequest* req = new (mPool) nsXBLBindingRequest(bindingURI, aBoundElement);
nsXBLStreamListener* xblListener = NS_STATIC_CAST(nsXBLStreamListener*, ilist);
xblListener->AddRequest(req); xblListener->AddRequest(req);
}
return NS_OK; return NS_OK;
} }
} }
@ -947,10 +1030,11 @@ nsXBLService::FetchBindingDocument(nsIContent* aBoundElement, nsIURI* aURI, cons
return rv; return rv;
} }
if (!IsChromeURI(aURI)) { nsCOMPtr<nsIAtom> tagName;
aBoundElement->GetTag(*getter_AddRefs(tagName));
if (tagName != kScrollbarAtom) {
// We can be asynchronous // We can be asynchronous
nsXBLStreamListener* xblListener = new nsXBLStreamListener(listener, boundDoc, doc); nsXBLStreamListener* xblListener = new nsXBLStreamListener(listener, boundDoc, doc);
NS_ADDREF(xblListener);
nsCOMPtr<nsIDOMEventReceiver> rec(do_QueryInterface(doc)); nsCOMPtr<nsIDOMEventReceiver> rec(do_QueryInterface(doc));
rec->AddEventListener(NS_ConvertASCIItoUCS2("load"), (nsIDOMLoadListener*)xblListener, PR_FALSE); rec->AddEventListener(NS_ConvertASCIItoUCS2("load"), (nsIDOMLoadListener*)xblListener, PR_FALSE);
@ -968,7 +1052,7 @@ nsXBLService::FetchBindingDocument(nsIContent* aBoundElement, nsIURI* aURI, cons
nsCAutoString bindingURI(uri); nsCAutoString bindingURI(uri);
bindingURI += "#"; bindingURI += "#";
bindingURI += aRef; bindingURI += aRef;
nsXBLBindingRequest* req = new nsXBLBindingRequest(bindingURI, aBoundElement); nsXBLBindingRequest* req = new (mPool) nsXBLBindingRequest(bindingURI, aBoundElement);
xblListener->AddRequest(req); xblListener->AddRequest(req);
// Now kick off the async read. // Now kick off the async read.

View File

@ -28,6 +28,7 @@
#include "nsIMemory.h" #include "nsIMemory.h"
#include "jsapi.h" // nsXBLJSClass derives from JSClass #include "jsapi.h" // nsXBLJSClass derives from JSClass
#include "jsclist.h" // nsXBLJSClass derives from JSCList #include "jsclist.h" // nsXBLJSClass derives from JSCList
#include "nsFixedSizeAllocator.h"
class nsIXBLBinding; class nsIXBLBinding;
class nsIXBLDocumentInfo; 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. // This method loads a binding doc and then builds the specific binding required.
NS_IMETHOD GetBinding(nsIContent* aBoundElement, const nsCString& aURLStr, nsIXBLBinding** aResult); 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. // This function clears out the bindings on a given content node.
NS_IMETHOD FlushStyleBindings(nsIContent* aContent); NS_IMETHOD FlushStyleBindings(nsIContent* aContent);
@ -80,6 +84,11 @@ public:
// This method synchronously loads and parses an XBL file. // This method synchronously loads and parses an XBL file.
NS_IMETHOD FetchBindingDocument(nsIContent* aBoundElement, nsIURI* aURI, const nsCString& aRef, nsIDocument** aResult); 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 // This method walks a binding document and removes any text nodes
// that contain only whitespace. // that contain only whitespace.
static nsresult StripWhitespaceNodes(nsIContent* aContent); static nsresult StripWhitespaceNodes(nsIContent* aContent);
@ -104,7 +113,10 @@ public:
// XBL Atoms // XBL Atoms
static nsIAtom* kExtendsAtom; static nsIAtom* kExtendsAtom;
static nsIAtom* kHasChildrenAtom;
static nsIAtom* kScrollbarAtom;
nsFixedSizeAllocator mPool;
}; };
class nsXBLJSClass : public JSCList, public JSClass class nsXBLJSClass : public JSCList, public JSClass

View File

@ -5235,7 +5235,9 @@ nsCSSFrameConstructor::CreateAnonymousFrames(nsIPresShell* aPresShell,
// Load the bindings. // Load the bindings.
nsCOMPtr<nsIXBLBinding> binding; nsCOMPtr<nsIXBLBinding> 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. // Retrieve the anonymous content that we should build.
nsCOMPtr<nsISupportsArray> anonymousItems; nsCOMPtr<nsISupportsArray> anonymousItems;
@ -5467,7 +5469,9 @@ nsCSSFrameConstructor::CreateAnonymousTableCellFrames(nsIPresShell* aPres
// Load the bindings. // Load the bindings.
nsCOMPtr<nsIXBLBinding> binding; nsCOMPtr<nsIXBLBinding> 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. // Retrieve the anonymous content that we should build.
nsCOMPtr<nsIContent> childElement; nsCOMPtr<nsIContent> childElement;

View File

@ -5235,7 +5235,9 @@ nsCSSFrameConstructor::CreateAnonymousFrames(nsIPresShell* aPresShell,
// Load the bindings. // Load the bindings.
nsCOMPtr<nsIXBLBinding> binding; nsCOMPtr<nsIXBLBinding> 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. // Retrieve the anonymous content that we should build.
nsCOMPtr<nsISupportsArray> anonymousItems; nsCOMPtr<nsISupportsArray> anonymousItems;
@ -5467,7 +5469,9 @@ nsCSSFrameConstructor::CreateAnonymousTableCellFrames(nsIPresShell* aPres
// Load the bindings. // Load the bindings.
nsCOMPtr<nsIXBLBinding> binding; nsCOMPtr<nsIXBLBinding> 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. // Retrieve the anonymous content that we should build.
nsCOMPtr<nsIContent> childElement; nsCOMPtr<nsIContent> childElement;

View File

@ -74,6 +74,7 @@ public:
NS_IMETHOD RemoveLoadingDocListener(const nsCString& aURL)=0; NS_IMETHOD RemoveLoadingDocListener(const nsCString& aURL)=0;
NS_IMETHOD InheritsStyle(nsIContent* aContent, PRBool* aResult) = 0; NS_IMETHOD InheritsStyle(nsIContent* aContent, PRBool* aResult) = 0;
NS_IMETHOD FlushChromeBindings() = 0;
}; };
#endif // nsIBinding_Manager_h__ #endif // nsIBinding_Manager_h__

View File

@ -91,6 +91,9 @@ public:
NS_IMETHOD WalkRules(nsISupportsArrayEnumFunc aFunc, void* aData)=0; NS_IMETHOD WalkRules(nsISupportsArrayEnumFunc aFunc, void* aData)=0;
NS_IMETHOD SetAllowScripts(PRBool aFlag)=0; NS_IMETHOD SetAllowScripts(PRBool aFlag)=0;
NS_IMETHOD MarkForDeath()=0;
NS_IMETHOD MarkedForDeath(PRBool* aResult)=0;
}; };
extern nsresult extern nsresult

View File

@ -59,6 +59,9 @@ public:
// This method loads a binding doc and then builds the specific binding required. // This method loads a binding doc and then builds the specific binding required.
NS_IMETHOD GetBinding(nsIContent* aBoundElement, const nsCString& aURLStr, nsIXBLBinding** aResult) = 0; 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 // For a given element, returns a flat list of all the anonymous children that need
// frames built. // frames built.
NS_IMETHOD GetContentList(nsIContent* aContent, nsISupportsArray** aResult, nsIContent** aChildElement, NS_IMETHOD GetContentList(nsIContent* aContent, nsISupportsArray** aResult, nsIContent** aChildElement,

View File

@ -178,6 +178,7 @@ public:
NS_IMETHOD RemoveLoadingDocListener(const nsCString& aURL); NS_IMETHOD RemoveLoadingDocListener(const nsCString& aURL);
NS_IMETHOD InheritsStyle(nsIContent* aContent, PRBool* aResult); NS_IMETHOD InheritsStyle(nsIContent* aContent, PRBool* aResult);
NS_IMETHOD FlushChromeBindings();
// nsIStyleRuleSupplier // nsIStyleRuleSupplier
NS_IMETHOD UseDocumentRules(nsIContent* aContent, PRBool* aResult); NS_IMETHOD UseDocumentRules(nsIContent* aContent, PRBool* aResult);
@ -252,8 +253,13 @@ nsBindingManager::SetBinding(nsIContent* aContent, nsIXBLBinding* aBinding )
mBindingTable = new nsSupportsHashtable; mBindingTable = new nsSupportsHashtable;
nsISupportsKey key(aContent); nsISupportsKey key(aContent);
nsCOMPtr<nsISupports> old = getter_AddRefs(mBindingTable->Get(&key));
if (old && aBinding)
NS_ERROR("Binding already installed!");
if (aBinding) { if (aBinding) {
mBindingTable->Put (&key, aBinding); mBindingTable->Put(&key, aBinding);
} }
else else
mBindingTable->Remove(&key); mBindingTable->Remove(&key);
@ -468,6 +474,23 @@ nsBindingManager::RemoveLoadingDocListener(const nsCString& aURL)
return NS_OK; 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 NS_IMETHODIMP
nsBindingManager::InheritsStyle(nsIContent* aContent, PRBool* aResult) nsBindingManager::InheritsStyle(nsIContent* aContent, PRBool* aResult)
{ {

View File

@ -252,11 +252,14 @@ nsXBLBinding::nsXBLBinding(const nsCString& aDocURI, const nsCString& aID)
mIsStyleBinding(PR_TRUE), mIsStyleBinding(PR_TRUE),
mAllowScripts(PR_TRUE), mAllowScripts(PR_TRUE),
mInheritStyle(PR_TRUE), mInheritStyle(PR_TRUE),
mMarkedForDeath(PR_FALSE),
mAttributeTable(nsnull), mAttributeTable(nsnull),
mInsertionPointTable(nsnull) mInsertionPointTable(nsnull)
{ {
NS_INIT_REFCNT(); NS_INIT_REFCNT();
gRefCnt++; gRefCnt++;
// printf("REF COUNT UP: %d %s\n", gRefCnt, (const char*)mID);
if (gRefCnt == 1) { if (gRefCnt == 1) {
kContentAtom = NS_NewAtom("content"); kContentAtom = NS_NewAtom("content");
kInterfaceAtom = NS_NewAtom("interface"); kInterfaceAtom = NS_NewAtom("interface");
@ -303,6 +306,8 @@ nsXBLBinding::~nsXBLBinding(void)
delete mInsertionPointTable; delete mInsertionPointTable;
gRefCnt--; gRefCnt--;
// printf("REF COUNT DOWN: %d %s\n", gRefCnt, (const char*)mID);
if (gRefCnt == 0) { if (gRefCnt == 0) {
NS_RELEASE(kContentAtom); NS_RELEASE(kContentAtom);
NS_RELEASE(kInterfaceAtom); NS_RELEASE(kInterfaceAtom);
@ -354,6 +359,11 @@ nsXBLBinding::GetBaseBinding(nsIXBLBinding** aResult)
NS_IMETHODIMP NS_IMETHODIMP
nsXBLBinding::SetBaseBinding(nsIXBLBinding* aBinding) nsXBLBinding::SetBaseBinding(nsIXBLBinding* aBinding)
{ {
if (mNextBinding) {
NS_ERROR("Base XBL binding is already defined!");
return NS_OK;
}
mNextBinding = aBinding; // Comptr handles rel/add mNextBinding = aBinding; // Comptr handles rel/add
return NS_OK; return NS_OK;
} }
@ -1454,8 +1464,6 @@ nsXBLBinding::IsInExcludesList(nsIAtom* aTag, const nsString& aList)
NS_IMETHODIMP NS_IMETHODIMP
nsXBLBinding::ConstructAttributeTable(nsIContent* aElement) nsXBLBinding::ConstructAttributeTable(nsIContent* aElement)
{ {
// XXX This function still needs to deal with the
// ability to map one attribute to another.
nsAutoString inherits; nsAutoString inherits;
aElement->GetAttribute(kNameSpaceID_None, kInheritsAtom, inherits); aElement->GetAttribute(kNameSpaceID_None, kInheritsAtom, inherits);
if (!inherits.IsEmpty()) { if (!inherits.IsEmpty()) {
@ -1745,7 +1753,19 @@ nsXBLBinding::GetFirstStyleBinding(nsIXBLBinding** aResult)
return NS_OK; 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 /////////////////////////////////////////////////////////////////////// // Creation Routine ///////////////////////////////////////////////////////////////////////

View File

@ -85,6 +85,9 @@ class nsXBLBinding: public nsIXBLBinding
NS_IMETHOD SetAllowScripts(PRBool aFlag) { mAllowScripts = aFlag; return NS_OK; }; NS_IMETHOD SetAllowScripts(PRBool aFlag) { mAllowScripts = aFlag; return NS_OK; };
NS_IMETHOD MarkForDeath();
NS_IMETHOD MarkedForDeath(PRBool* aResult);
public: public:
nsXBLBinding(const nsCString& aDocURI, const nsCString& aRef); nsXBLBinding(const nsCString& aDocURI, const nsCString& aRef);
virtual ~nsXBLBinding(); virtual ~nsXBLBinding();
@ -172,9 +175,10 @@ protected:
nsIContent* mBoundElement; // [WEAK] We have a reference, but we don't own it. nsIContent* mBoundElement; // [WEAK] We have a reference, but we don't own it.
PRBool mIsStyleBinding; PRPackedBool mIsStyleBinding;
PRBool mAllowScripts; PRPackedBool mAllowScripts;
PRBool mInheritStyle; PRPackedBool mInheritStyle;
PRPackedBool mMarkedForDeath;
nsSupportsHashtable* mAttributeTable; // A table for attribute entries. nsSupportsHashtable* mAttributeTable; // A table for attribute entries.
nsSupportsHashtable* mInsertionPointTable; // A table of insertion points. nsSupportsHashtable* mInsertionPointTable; // A table of insertion points.

View File

@ -25,6 +25,7 @@
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "nsXBLService.h" #include "nsXBLService.h"
#include "nsIInputStream.h" #include "nsIInputStream.h"
#include "nsINameSpace.h"
#include "nsINameSpaceManager.h" #include "nsINameSpaceManager.h"
#include "nsHashtable.h" #include "nsHashtable.h"
#include "nsIURI.h" #include "nsIURI.h"
@ -37,6 +38,7 @@
#include "nsNetUtil.h" #include "nsNetUtil.h"
#include "plstr.h" #include "plstr.h"
#include "nsIContent.h" #include "nsIContent.h"
#include "nsIXMLContent.h"
#include "nsIDOMElement.h" #include "nsIDOMElement.h"
#include "nsIDocument.h" #include "nsIDocument.h"
#include "nsIXMLContentSink.h" #include "nsIXMLContentSink.h"
@ -52,6 +54,9 @@
#include "nsIChromeRegistry.h" #include "nsIChromeRegistry.h"
#include "nsIPref.h" #include "nsIPref.h"
#include "nsIPresShell.h"
#include "nsIDocumentObserver.h"
#include "nsIXULContentUtils.h" #include "nsIXULContentUtils.h"
#include "nsIXULPrototypeCache.h" #include "nsIXULPrototypeCache.h"
#include "nsIDOMLoadListener.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) void DocumentLoaded(nsIDocument* aBindingDoc)
{ {
// Get the binding.
nsCOMPtr<nsIXBLBinding> 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<nsIDocument> doc; nsCOMPtr<nsIDocument> doc;
mBoundElement->GetDocument(*getter_AddRefs(doc)); mBoundElement->GetDocument(*getter_AddRefs(doc));
if (!doc) if (!doc)
return; return;
nsCOMPtr<nsIBindingManager> bindingManager;
doc->GetBindingManager(getter_AddRefs(bindingManager));
bindingManager->SetBinding(mBoundElement, newBinding);
// Set the binding's bound element. // Get the binding.
newBinding->SetBoundElement(mBoundElement); PRBool ready = PR_FALSE;
gXBLService->BindingReady(mBoundElement, mBindingURL, &ready);
// Tell the binding to build the anonymous content. if (!ready)
newBinding->GenerateAnonymousContent(mBoundElement); return;
// Tell the binding to install event handlers
nsCOMPtr<nsIXBLBinding> attachReq;
newBinding->InstallEventHandlers(mBoundElement, getter_AddRefs(attachReq));
// Set up our properties
newBinding->InstallProperties(mBoundElement);
if (attachReq) {
attachReq->ExecuteAttachedHandler();
}
// XXX Deal with layered bindings.
// Now do a ContentInserted notification to cause the frames to get installed finally, // Now do a ContentInserted notification to cause the frames to get installed finally,
nsCOMPtr<nsIContent> parent; nsCOMPtr<nsIContent> parent;
mBoundElement->GetParent(*getter_AddRefs(parent)); mBoundElement->GetParent(*getter_AddRefs(parent));
PRInt32 index = 0; PRInt32 index = 0;
if (parent) if (parent)
parent->IndexOf(mBoundElement, index); parent->IndexOf(mBoundElement, index);
if (index == -1)
return; nsCOMPtr<nsIPresShell> shell = getter_AddRefs(doc->GetShellAt(0));
doc->ContentInserted(parent, mBoundElement, index); if (shell) {
nsCOMPtr<nsIDocumentObserver> obs(do_QueryInterface(shell));
obs->ContentRemoved(doc, parent, mBoundElement, index);
obs->ContentInserted(doc, parent, mBoundElement, index);
}
} }
static nsIXBLService* gXBLService; static nsIXBLService* gXBLService;
static int gRefCnt; 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; nsIXBLService* nsXBLBindingRequest::gXBLService = nsnull;
int nsXBLBindingRequest::gRefCnt = 0; int nsXBLBindingRequest::gRefCnt = 0;
@ -180,12 +186,13 @@ public:
virtual ~nsXBLStreamListener(); virtual ~nsXBLStreamListener();
void AddRequest(nsXBLBindingRequest* aRequest) { mBindingRequests.AppendElement(aRequest); }; void AddRequest(nsXBLBindingRequest* aRequest) { mBindingRequests.AppendElement(aRequest); };
PRBool HasRequest(const nsCString& aURI, nsIContent* aBoundElement);
private: private:
nsCOMPtr<nsIStreamListener> mInner; nsCOMPtr<nsIStreamListener> mInner;
nsVoidArray mBindingRequests; nsVoidArray mBindingRequests;
nsCOMPtr<nsIDocument> mDocument; nsIDocument* mDocument;
nsCOMPtr<nsIDocument> mBindingDocument; nsCOMPtr<nsIDocument> mBindingDocument;
}; };
@ -264,17 +271,34 @@ nsXBLStreamListener::OnStopRequest(nsIChannel* aChannel, nsISupports* aCtxt, nsr
rv = mInner->OnStopRequest(aChannel, aCtxt, aStatus, aStatusArg); rv = mInner->OnStopRequest(aChannel, aCtxt, aStatus, aStatusArg);
} }
if (NS_FAILED(rv)) { if (NS_FAILED(rv) || NS_FAILED(aStatus)) {
PRUint32 count = mBindingRequests.Count(); PRUint32 count = mBindingRequests.Count();
for (PRUint32 i = 0; i < count; i++) { for (PRUint32 i = 0; i < count; i++) {
nsXBLBindingRequest* req = (nsXBLBindingRequest*)mBindingRequests.ElementAt(i); nsXBLBindingRequest* req = (nsXBLBindingRequest*)mBindingRequests.ElementAt(i);
delete req; delete req;
} }
mDocument = nsnull;
mBindingDocument = nsnull;
} }
return rv; 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 nsresult
nsXBLStreamListener::Load(nsIDOMEvent* aEvent) nsXBLStreamListener::Load(nsIDOMEvent* aEvent)
{ {
@ -313,6 +337,7 @@ nsXBLStreamListener::Load(nsIDOMEvent* aEvent)
} }
} }
if (!cached)
bindingManager->PutXBLDocumentInfo(info); bindingManager->PutXBLDocumentInfo(info);
// Notify all pending requests that their bindings are // Notify all pending requests that their bindings are
@ -328,9 +353,12 @@ nsXBLStreamListener::Load(nsIDOMEvent* aEvent)
delete req; delete req;
} }
nsCOMPtr<nsIDOMEventReceiver> rec(do_QueryInterface(mDocument)); nsCOMPtr<nsIDOMEventReceiver> rec(do_QueryInterface(mBindingDocument));
rec->RemoveEventListener(NS_ConvertASCIItoUCS2("load"), (nsIDOMLoadListener*)this, PR_FALSE); rec->RemoveEventListener(NS_ConvertASCIItoUCS2("load"), (nsIDOMLoadListener*)this, PR_FALSE);
mDocument = nsnull;
mBindingDocument = nsnull;
return rv; return rv;
} }
@ -406,7 +434,7 @@ PRUint32 nsXBLService::gClassLRUListLength = 0;
PRUint32 nsXBLService::gClassLRUListQuota = 64; PRUint32 nsXBLService::gClassLRUListQuota = 64;
nsIAtom* nsXBLService::kExtendsAtom = nsnull; nsIAtom* nsXBLService::kExtendsAtom = nsnull;
nsIAtom* nsXBLService::kHasChildrenAtom = nsnull; nsIAtom* nsXBLService::kScrollbarAtom = nsnull;
// Enabled by default. Must be over-ridden to disable // Enabled by default. Must be over-ridden to disable
PRBool nsXBLService::gDisableChromeCache = PR_FALSE; PRBool nsXBLService::gDisableChromeCache = PR_FALSE;
@ -421,6 +449,8 @@ NS_IMPL_ISUPPORTS2(nsXBLService, nsIXBLService, nsIMemoryPressureObserver)
nsXBLService::nsXBLService(void) nsXBLService::nsXBLService(void)
{ {
NS_INIT_REFCNT(); NS_INIT_REFCNT();
mPool.Init("XBL Binding Requests", kBucketSizes, kNumBuckets, kInitialSize);
gRefCnt++; gRefCnt++;
if (gRefCnt == 1) { if (gRefCnt == 1) {
@ -442,7 +472,7 @@ nsXBLService::nsXBLService(void)
// Create our atoms // Create our atoms
kExtendsAtom = NS_NewAtom("extends"); kExtendsAtom = NS_NewAtom("extends");
kHasChildrenAtom = NS_NewAtom("haschildren"); kScrollbarAtom = NS_NewAtom("scrollbar");
// Find out if the XUL cache is on or off // Find out if the XUL cache is on or off
NS_WITH_SERVICE(nsIPref, prefs, NS_PREF_PROGID, &rv); NS_WITH_SERVICE(nsIPref, prefs, NS_PREF_PROGID, &rv);
@ -476,7 +506,7 @@ nsXBLService::~nsXBLService(void)
// Release our atoms // Release our atoms
NS_RELEASE(kExtendsAtom); NS_RELEASE(kExtendsAtom);
NS_RELEASE(kHasChildrenAtom); NS_RELEASE(kScrollbarAtom);
// Walk the LRU list removing and deleting the nsXBLJSClasses. // Walk the LRU list removing and deleting the nsXBLJSClasses.
FlushMemory(REASON_HEAP_MINIMIZE, 0); FlushMemory(REASON_HEAP_MINIMIZE, 0);
@ -526,6 +556,13 @@ nsXBLService::LoadBindings(nsIContent* aContent, const nsString& aURL, PRBool aA
nsCOMPtr<nsIXBLBinding> styleBinding; nsCOMPtr<nsIXBLBinding> styleBinding;
binding->GetFirstStyleBinding(getter_AddRefs(styleBinding)); binding->GetFirstStyleBinding(getter_AddRefs(styleBinding));
if (styleBinding) { if (styleBinding) {
PRBool marked = PR_FALSE;
binding->MarkedForDeath(&marked);
if (marked) {
FlushStyleBindings(aContent);
binding = nsnull;
}
else {
// See if the URIs match. // See if the URIs match.
nsCAutoString uri; nsCAutoString uri;
styleBinding->GetBindingURI(uri); styleBinding->GetBindingURI(uri);
@ -537,6 +574,7 @@ nsXBLService::LoadBindings(nsIContent* aContent, const nsString& aURL, PRBool aA
} }
} }
} }
}
nsCOMPtr<nsIXBLBinding> newBinding; nsCOMPtr<nsIXBLBinding> newBinding;
nsCAutoString url; url.AssignWithConversion(aURL); nsCAutoString url; url.AssignWithConversion(aURL);
@ -736,8 +774,28 @@ nsXBLService::FlushMemory(PRUint32 reason, size_t requestedAmount)
// Internal helper methods //////////////////////////////////////////////////////////////// // 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)
{ {
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; *aResult = nsnull;
if (aURLStr.IsEmpty()) if (aURLStr.IsEmpty())
@ -782,6 +840,42 @@ NS_IMETHODIMP nsXBLService::GetBinding(nsIContent* aBoundElement, const nsCStrin
// If no ref is specified just use this. // If no ref is specified just use this.
if ((bindingName.IsEmpty()) || (bindingName == value)) { if ((bindingName.IsEmpty()) || (bindingName == value)) {
// Check for the presence of an extends attribute
nsAutoString extends;
nsCOMPtr<nsIXBLBinding> 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<nsIAtom> prefixAtom = getter_AddRefs(NS_NewAtom(prefix));
nsCOMPtr<nsINameSpace> nameSpace;
nsCOMPtr<nsIXMLContent> xmlContent(do_QueryInterface(child));
if (xmlContent) {
xmlContent->GetContainingNameSpace(*getter_AddRefs(nameSpace));
if (nameSpace) {
nsCOMPtr<nsINameSpace> 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 // Make a new binding
NS_NewXBLBinding(uri, ref, aResult); NS_NewXBLBinding(uri, ref, aResult);
@ -789,24 +883,9 @@ NS_IMETHODIMP nsXBLService::GetBinding(nsIContent* aBoundElement, const nsCStrin
(*aResult)->SetBindingElement(child); (*aResult)->SetBindingElement(child);
(*aResult)->SetAllowScripts(allowScripts); (*aResult)->SetAllowScripts(allowScripts);
// Check for the presence of an extends attribute if (baseBinding)
child->GetAttribute(kNameSpaceID_None, kExtendsAtom, value);
if (!value.IsEmpty()) {
// See if we are extending a builtin tag.
nsCOMPtr<nsIAtom> tag;
PRInt32 dummy;
(*aResult)->GetBaseTag(&dummy, getter_AddRefs(tag));
if (!tag) {
// We have a base class binding. Load it right now.
nsCOMPtr<nsIXBLBinding> 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); (*aResult)->SetBaseBinding(baseBinding);
} }
}
break; break;
} }
} }
@ -839,7 +918,9 @@ nsXBLService::GetBindingDocumentInfo(nsIContent* aBoundElement, const nsCString&
boundDocument->GetBindingManager(getter_AddRefs(bindingManager)); boundDocument->GetBindingManager(getter_AddRefs(bindingManager));
bindingManager->GetXBLDocumentInfo(aURLStr, getter_AddRefs(info)); bindingManager->GetXBLDocumentInfo(aURLStr, getter_AddRefs(info));
if (!info) { nsCOMPtr<nsIAtom> tagName;
aBoundElement->GetTag(*getter_AddRefs(tagName));
if (!info && (tagName.get() != kScrollbarAtom)) {
// The third line of defense is to investigate whether or not the // The third line of defense is to investigate whether or not the
// document is currently being loaded asynchronously. If so, there's no // 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 // 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<nsIStreamListener> listener; nsCOMPtr<nsIStreamListener> listener;
bindingManager->GetLoadingDocListener(aURLStr, getter_AddRefs(listener)); bindingManager->GetLoadingDocListener(aURLStr, getter_AddRefs(listener));
if (listener) { if (listener) {
nsIStreamListener* ilist = listener.get();
nsXBLStreamListener* xblListener = NS_STATIC_CAST(nsXBLStreamListener*, ilist);
// Create a new load observer. // Create a new load observer.
nsCAutoString bindingURI(aURLStr); nsCAutoString bindingURI(aURLStr);
bindingURI += "#"; bindingURI += "#";
bindingURI += aRef; bindingURI += aRef;
nsXBLBindingRequest* req = new nsXBLBindingRequest(aRef, aBoundElement); if (!xblListener->HasRequest(bindingURI, aBoundElement)) {
nsIStreamListener* ilist = listener.get(); nsXBLBindingRequest* req = new (mPool) nsXBLBindingRequest(bindingURI, aBoundElement);
nsXBLStreamListener* xblListener = NS_STATIC_CAST(nsXBLStreamListener*, ilist);
xblListener->AddRequest(req); xblListener->AddRequest(req);
}
return NS_OK; return NS_OK;
} }
} }
@ -947,10 +1030,11 @@ nsXBLService::FetchBindingDocument(nsIContent* aBoundElement, nsIURI* aURI, cons
return rv; return rv;
} }
if (!IsChromeURI(aURI)) { nsCOMPtr<nsIAtom> tagName;
aBoundElement->GetTag(*getter_AddRefs(tagName));
if (tagName != kScrollbarAtom) {
// We can be asynchronous // We can be asynchronous
nsXBLStreamListener* xblListener = new nsXBLStreamListener(listener, boundDoc, doc); nsXBLStreamListener* xblListener = new nsXBLStreamListener(listener, boundDoc, doc);
NS_ADDREF(xblListener);
nsCOMPtr<nsIDOMEventReceiver> rec(do_QueryInterface(doc)); nsCOMPtr<nsIDOMEventReceiver> rec(do_QueryInterface(doc));
rec->AddEventListener(NS_ConvertASCIItoUCS2("load"), (nsIDOMLoadListener*)xblListener, PR_FALSE); rec->AddEventListener(NS_ConvertASCIItoUCS2("load"), (nsIDOMLoadListener*)xblListener, PR_FALSE);
@ -968,7 +1052,7 @@ nsXBLService::FetchBindingDocument(nsIContent* aBoundElement, nsIURI* aURI, cons
nsCAutoString bindingURI(uri); nsCAutoString bindingURI(uri);
bindingURI += "#"; bindingURI += "#";
bindingURI += aRef; bindingURI += aRef;
nsXBLBindingRequest* req = new nsXBLBindingRequest(bindingURI, aBoundElement); nsXBLBindingRequest* req = new (mPool) nsXBLBindingRequest(bindingURI, aBoundElement);
xblListener->AddRequest(req); xblListener->AddRequest(req);
// Now kick off the async read. // Now kick off the async read.

View File

@ -28,6 +28,7 @@
#include "nsIMemory.h" #include "nsIMemory.h"
#include "jsapi.h" // nsXBLJSClass derives from JSClass #include "jsapi.h" // nsXBLJSClass derives from JSClass
#include "jsclist.h" // nsXBLJSClass derives from JSCList #include "jsclist.h" // nsXBLJSClass derives from JSCList
#include "nsFixedSizeAllocator.h"
class nsIXBLBinding; class nsIXBLBinding;
class nsIXBLDocumentInfo; 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. // This method loads a binding doc and then builds the specific binding required.
NS_IMETHOD GetBinding(nsIContent* aBoundElement, const nsCString& aURLStr, nsIXBLBinding** aResult); 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. // This function clears out the bindings on a given content node.
NS_IMETHOD FlushStyleBindings(nsIContent* aContent); NS_IMETHOD FlushStyleBindings(nsIContent* aContent);
@ -80,6 +84,11 @@ public:
// This method synchronously loads and parses an XBL file. // This method synchronously loads and parses an XBL file.
NS_IMETHOD FetchBindingDocument(nsIContent* aBoundElement, nsIURI* aURI, const nsCString& aRef, nsIDocument** aResult); 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 // This method walks a binding document and removes any text nodes
// that contain only whitespace. // that contain only whitespace.
static nsresult StripWhitespaceNodes(nsIContent* aContent); static nsresult StripWhitespaceNodes(nsIContent* aContent);
@ -104,7 +113,10 @@ public:
// XBL Atoms // XBL Atoms
static nsIAtom* kExtendsAtom; static nsIAtom* kExtendsAtom;
static nsIAtom* kHasChildrenAtom;
static nsIAtom* kScrollbarAtom;
nsFixedSizeAllocator mPool;
}; };
class nsXBLJSClass : public JSCList, public JSClass class nsXBLJSClass : public JSCList, public JSClass

View File

@ -74,6 +74,7 @@
#include "nsLayoutCID.h" #include "nsLayoutCID.h"
#include "nsGfxCIID.h" #include "nsGfxCIID.h"
#include "nsIImageManager.h" #include "nsIImageManager.h"
#include "nsIBindingManager.h"
#include "prio.h" #include "prio.h"
static char kChromePrefix[] = "chrome://"; static char kChromePrefix[] = "chrome://";
@ -1044,6 +1045,11 @@ NS_IMETHODIMP nsChromeRegistry::RefreshWindow(nsIDOMWindow* aWindow)
if (!document) if (!document)
return NS_OK; return NS_OK;
// Annihilate all XBL bindings.
nsCOMPtr<nsIBindingManager> bindingManager;
document->GetBindingManager(getter_AddRefs(bindingManager));
bindingManager->FlushChromeBindings();
nsCOMPtr<nsIXULDocument> xulDoc = do_QueryInterface(domDocument); nsCOMPtr<nsIXULDocument> xulDoc = do_QueryInterface(domDocument);
if (xulDoc) { if (xulDoc) {
// Deal with the backstop sheets first. // Deal with the backstop sheets first.