Fix for 42320. r=danm

This commit is contained in:
hyatt%netscape.com 2000-08-06 04:57:55 +00:00
parent ed96fc9dc0
commit 1e75026c61
16 changed files with 544 additions and 148 deletions

View File

@ -32,6 +32,7 @@ EXPORTS = \
nsIBindingManager.h \
nsIXBLBinding.h \
nsIXBLService.h \
nsIXBLStreamListener.h \
$(NULL)
EXPORTS := $(addprefix $(srcdir)/, $(EXPORTS))

View File

@ -1,4 +1,6 @@
nsIBindingManager.h
nsIXBLBinding.h
nsIXBLService.h
nsIXBLStreamListener.h

View File

@ -25,6 +25,7 @@ EXPORTS = \
nsIBindingManager.h \
nsIXBLBinding.h \
nsIXBLService.h \
nsIXBLStreamListener.h \
$(NULL)
MODULE=raptor

View File

@ -37,6 +37,7 @@
class nsIContent;
class nsIXBLBinding;
class nsIAtom;
class nsIXBLStreamListener;
// {55D70FE0-C8E5-11d3-97FB-00400553EEF0}
#define NS_IBINDING_MANAGER_IID \
@ -66,6 +67,9 @@ public:
NS_IMETHOD PutXBLDocument(nsIDocument* aDocument) = 0;
NS_IMETHOD GetXBLDocument(const nsCString& aURL, nsIDocument** aResult) = 0;
NS_IMETHOD PutLoadingDocListener(const nsCString& aURL, nsIXBLStreamListener* aListener) = 0;
NS_IMETHOD GetLoadingDocListener(const nsCString& aURL, nsIXBLStreamListener** aResult) = 0;
NS_IMETHOD RemoveLoadingDocListener(const nsCString& aURL)=0;
};
#endif // nsIBinding_Manager_h__

View File

@ -55,6 +55,9 @@ public:
// This function clears out the bindings on a given content node.
NS_IMETHOD FlushStyleBindings(nsIContent* aContent) = 0;
// This method loads a binding doc and then builds the specific binding required.
NS_IMETHOD GetBinding(nsIContent* aBoundElement, const nsCString& aURLStr, nsIXBLBinding** aResult) = 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,

View File

@ -46,6 +46,7 @@
#include "nsITextContent.h"
#include "nsIXBLBinding.h"
#include "nsIXBLStreamListener.h"
// Static IIDs/CIDs. Try to minimize these.
static NS_DEFINE_CID(kNameSpaceManagerCID, NS_NAMESPACEMANAGER_CID);
@ -79,10 +80,16 @@ public:
NS_IMETHOD PutXBLDocument(nsIDocument* aDocument);
NS_IMETHOD GetXBLDocument(const nsCString& aURL, nsIDocument** aResult);
NS_IMETHOD PutLoadingDocListener(const nsCString& aURL, nsIXBLStreamListener* aListener);
NS_IMETHOD GetLoadingDocListener(const nsCString& aURL, nsIXBLStreamListener** aResult);
NS_IMETHOD RemoveLoadingDocListener(const nsCString& aURL);
// MEMBER VARIABLES
protected:
nsSupportsHashtable* mBindingTable;
nsSupportsHashtable* mDocumentTable;
nsSupportsHashtable* mLoadingDocTable;
nsCOMPtr<nsISupportsArray> mAttachedQueue;
};
@ -99,7 +106,10 @@ nsBindingManager::nsBindingManager(void)
NS_INIT_REFCNT();
mBindingTable = nsnull;
mDocumentTable = nsnull;
mLoadingDocTable = nsnull;
mAttachedQueue = nsnull;
}
@ -107,6 +117,7 @@ nsBindingManager::~nsBindingManager(void)
{
delete mBindingTable;
delete mDocumentTable;
delete mLoadingDocTable;
}
NS_IMETHODIMP
@ -310,6 +321,41 @@ nsBindingManager::GetXBLDocument(const nsCString& aURL, nsIDocument** aResult)
return NS_OK;
}
NS_IMETHODIMP
nsBindingManager::PutLoadingDocListener(const nsCString& aURL, nsIXBLStreamListener* aListener)
{
if (!mLoadingDocTable)
mLoadingDocTable = new nsSupportsHashtable();
nsStringKey key(aURL);
mLoadingDocTable->Put(&key, aListener);
return NS_OK;
}
NS_IMETHODIMP
nsBindingManager::GetLoadingDocListener(const nsCString& aURL, nsIXBLStreamListener** aResult)
{
*aResult = nsnull;
if (!mLoadingDocTable)
return NS_OK;
nsStringKey key(aURL);
*aResult = NS_STATIC_CAST(nsIXBLStreamListener*, mLoadingDocTable->Get(&key)); // Addref happens here.
return NS_OK;
}
NS_IMETHODIMP
nsBindingManager::RemoveLoadingDocListener(const nsCString& aURL)
{
if (!mLoadingDocTable)
return NS_OK;
nsStringKey key(aURL);
mLoadingDocTable->Remove(&key);
return NS_OK;
}
// Creation Routine ///////////////////////////////////////////////////////////////////////

View File

@ -52,6 +52,7 @@
#include "nsIXULContentUtils.h"
#include "nsIXULPrototypeCache.h"
#include "nsIXBLStreamListener.h"
// Static IIDs/CIDs. Try to minimize these.
static NS_DEFINE_CID(kNameSpaceManagerCID, NS_NAMESPACEMANAGER_CID);
@ -62,40 +63,118 @@ static NS_DEFINE_CID(kChromeRegistryCID, NS_CHROMEREGISTRY_CID);
// Individual binding requests.
struct nsXBLBindingRequest
{
nsCOMPtr<nsIURL> mURL;
nsCString mBindingURL;
nsCOMPtr<nsIContent> mBoundElement;
nsXBLBindingRequest(const nsCString& aURL, nsIContent* aBoundElement)
{
mBindingURL = aURL;
mBoundElement = aBoundElement;
gRefCnt++;
if (gRefCnt == 1) {
nsServiceManager::GetService("component://netscape/xbl",
NS_GET_IID(nsIXBLService),
(nsISupports**) &gXBLService);
}
}
~nsXBLBindingRequest()
{
gRefCnt--;
if (gRefCnt == 0) {
nsServiceManager::ReleaseService("component://netscape/xbl", gXBLService);
gXBLService = nsnull;
}
}
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;
mBoundElement->GetDocument(*getter_AddRefs(doc));
if (!doc)
return;
nsCOMPtr<nsIBindingManager> bindingManager;
doc->GetBindingManager(getter_AddRefs(bindingManager));
bindingManager->SetBinding(mBoundElement, newBinding);
// Set the binding's bound element.
newBinding->SetBoundElement(mBoundElement);
// Tell the binding to build the anonymous content.
newBinding->GenerateAnonymousContent(mBoundElement);
// 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();
}
// Now do a ContentInserted notification to cause the frames to get installed finally,
nsCOMPtr<nsIContent> parent;
mBoundElement->GetParent(*getter_AddRefs(parent));
PRInt32 index = 0;
if (parent)
parent->IndexOf(mBoundElement, index);
if (index == -1)
return;
doc->ContentInserted(parent, mBoundElement, index);
}
static nsIXBLService* gXBLService;
static int gRefCnt;
};
nsIXBLService* nsXBLBindingRequest::gXBLService = nsnull;
int nsXBLBindingRequest::gRefCnt = 0;
// nsXBLStreamListener, a helper class used for
// asynchronous parsing of URLs
/* Header file */
class nsXBLStreamListener : public nsIStreamListener
class nsXBLStreamListener : public nsIStreamListener, public nsIXBLStreamListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSISTREAMOBSERVER
nsXBLStreamListener(nsIStreamListener* aInner);
nsXBLStreamListener(nsIStreamListener* aInner, nsIDocument* aDocument, nsIDocument* aBindingDocument);
virtual ~nsXBLStreamListener();
/* additional members */
void AddRequest(nsXBLBindingRequest* aRequest) { mBindingRequests.AppendElement(aRequest); };
private:
nsCOMPtr<nsIStreamListener> mInner;
nsVoidArray mBindingRequests;
nsCOMPtr<nsIDocument> mDocument;
static nsIXBLService* gXBLService;
nsCOMPtr<nsIDocument> mBindingDocument;
};
nsIXBLService* nsXBLStreamListener::gXBLService = nsnull;
/* Implementation file */
NS_IMPL_ISUPPORTS2(nsXBLStreamListener, nsIStreamListener, nsIStreamObserver)
NS_IMPL_ISUPPORTS3(nsXBLStreamListener, nsIStreamListener, nsIStreamObserver, nsIXBLStreamListener)
nsXBLStreamListener::nsXBLStreamListener(nsIStreamListener* aInner)
nsXBLStreamListener::nsXBLStreamListener(nsIStreamListener* aInner, nsIDocument* aDocument,
nsIDocument* aBindingDocument)
{
NS_INIT_ISUPPORTS();
/* member initializers and constructor code */
mInner = aInner;
mDocument = aDocument;
mBindingDocument = aBindingDocument;
}
nsXBLStreamListener::~nsXBLStreamListener()
@ -117,16 +196,9 @@ nsXBLStreamListener::OnDataAvailable(nsIChannel* aChannel, nsISupports* aCtxt, n
NS_IMETHODIMP
nsXBLStreamListener::OnStartRequest(nsIChannel* aChannel, nsISupports* aCtxt)
{
nsresult rv;
if (mInner) {
rv = mInner->OnStartRequest(aChannel, aCtxt);
if (NS_FAILED(rv))
return rv;
//
}
if (mInner)
return mInner->OnStartRequest(aChannel, aCtxt);
return NS_ERROR_FAILURE;
}
@ -134,8 +206,39 @@ nsXBLStreamListener::OnStartRequest(nsIChannel* aChannel, nsISupports* aCtxt)
NS_IMETHODIMP
nsXBLStreamListener::OnStopRequest(nsIChannel* aChannel, nsISupports* aCtxt, nsresult aStatus, const PRUnichar* aStatusArg)
{
if (mInner)
return mInner->OnStopRequest(aChannel, aCtxt, aStatus, aStatusArg);
if (mInner) {
nsresult rv = mInner->OnStopRequest(aChannel, aCtxt, aStatus, aStatusArg);
if (NS_FAILED(rv))
return NS_ERROR_FAILURE;
// Remove ourselves from the set of pending docs.
nsCOMPtr<nsIBindingManager> bindingManager;
mDocument->GetBindingManager(getter_AddRefs(bindingManager));
nsCOMPtr<nsIURI> uri(mBindingDocument->GetDocumentURL());
nsXPIDLCString str;
uri->GetSpec(getter_Copies(str));
bindingManager->RemoveLoadingDocListener((const char*)str);
PRUint32 count = mBindingRequests.Count();
if (NS_SUCCEEDED(aStatus)) {
// Put our doc in the doc table.
bindingManager->PutXBLDocument(mBindingDocument);
// Notify all pending requests that their bindings are
// ready and can be installed.
for (PRUint32 i = 0; i < count; i++) {
nsXBLBindingRequest* req = (nsXBLBindingRequest*)mBindingRequests.ElementAt(i);
req->DocumentLoaded(mBindingDocument);
}
}
for (PRUint32 i = 0; i < count; i++) {
nsXBLBindingRequest* req = (nsXBLBindingRequest*)mBindingRequests.ElementAt(i);
delete req;
}
}
return NS_ERROR_FAILURE;
}
@ -560,7 +663,7 @@ NS_IMETHODIMP nsXBLService::GetBinding(nsIContent* aBoundElement, const nsCStrin
nsCOMPtr<nsIDocument> doc;
GetBindingDocument(aBoundElement, uri, getter_AddRefs(doc));
GetBindingDocument(aBoundElement, uri, ref, getter_AddRefs(doc));
if (!doc)
return NS_ERROR_FAILURE;
@ -637,7 +740,8 @@ static PRBool IsChromeOrResourceURI(nsIURI* aURI)
}
NS_IMETHODIMP
nsXBLService::GetBindingDocument(nsIContent* aBoundElement, const nsCString& aURLStr, nsIDocument** aResult)
nsXBLService::GetBindingDocument(nsIContent* aBoundElement, const nsCString& aURLStr, const nsCString& aRef,
nsIDocument** aResult)
{
nsresult rv;
@ -660,53 +764,69 @@ nsXBLService::GetBindingDocument(nsIContent* aBoundElement, const nsCString& aUR
nsCOMPtr<nsIBindingManager> bindingManager;
boundDocument->GetBindingManager(getter_AddRefs(bindingManager));
bindingManager->GetXBLDocument(aURLStr, getter_AddRefs(document));
}
if (!document) {
// XXX 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
// processed whenever the doc does finish loading.
// Finally, if all lines of defense fail, we go and fetch the binding
// document.
nsCOMPtr<nsIURL> uri;
nsComponentManager::CreateInstance("component://netscape/network/standard-url",
nsnull,
NS_GET_IID(nsIURL),
getter_AddRefs(uri));
uri->SetSpec(aURLStr);
FetchBindingDocument(uri, getter_AddRefs(document));
if (document) {
// If the doc is a chrome URI, then we put it into the XUL cache.
PRBool cached = PR_FALSE;
if (IsChromeOrResourceURI(uri) && gXULUtils->UseXULCache()) {
cached = PR_TRUE;
gXULCache->PutXBLDocument(document);
// Cache whether or not this chrome XBL can execute scripts.
nsCOMPtr<nsIChromeRegistry> reg(do_GetService(kChromeRegistryCID, &rv));
if (NS_SUCCEEDED(rv) && reg) {
PRBool allow;
reg->AllowScriptsForSkin(uri, &allow);
if (!allow)
gXULCache->PutXBLDocScriptAccess(document);
}
}
if (!cached) {
// Otherwise we put it in our binding manager's document table.
nsCOMPtr<nsIDocument> boundDocument;
aBoundElement->GetDocument(*getter_AddRefs(boundDocument));
nsCOMPtr<nsIBindingManager> bindingManager;
boundDocument->GetBindingManager(getter_AddRefs(bindingManager));
bindingManager->PutXBLDocument(document);
if (!document) {
// 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
// processed whenever the doc does finish loading.
nsCOMPtr<nsIXBLStreamListener> listener;
bindingManager->GetLoadingDocListener(aURLStr, getter_AddRefs(listener));
if (listener) {
// Create a new load observer.
nsCAutoString bindingURI(aURLStr);
bindingURI += "#";
bindingURI += aRef;
nsXBLBindingRequest* req = new nsXBLBindingRequest(aRef, aBoundElement);
nsIXBLStreamListener* ilist = listener.get();
nsXBLStreamListener* xblListener = NS_STATIC_CAST(nsXBLStreamListener*, ilist);
xblListener->AddRequest(req);
}
}
if (!document) {
// Finally, if all lines of defense fail, we go and fetch the binding
// document.
nsCOMPtr<nsIURL> uri;
nsComponentManager::CreateInstance("component://netscape/network/standard-url",
nsnull,
NS_GET_IID(nsIURL),
getter_AddRefs(uri));
uri->SetSpec(aURLStr);
FetchBindingDocument(aBoundElement, uri, aRef, getter_AddRefs(document));
if (document) {
// If the doc is a chrome URI, then we put it into the XUL cache.
PRBool cached = PR_FALSE;
if (IsChromeOrResourceURI(uri) && gXULUtils->UseXULCache()) {
cached = PR_TRUE;
gXULCache->PutXBLDocument(document);
// Cache whether or not this chrome XBL can execute scripts.
nsCOMPtr<nsIChromeRegistry> reg(do_GetService(kChromeRegistryCID, &rv));
if (NS_SUCCEEDED(rv) && reg) {
PRBool allow;
reg->AllowScriptsForSkin(uri, &allow);
if (!allow)
gXULCache->PutXBLDocScriptAccess(document);
}
}
if (!cached) {
// Otherwise we put it in our binding manager's document table.
nsCOMPtr<nsIDocument> boundDocument;
aBoundElement->GetDocument(*getter_AddRefs(boundDocument));
nsCOMPtr<nsIBindingManager> bindingManager;
boundDocument->GetBindingManager(getter_AddRefs(bindingManager));
bindingManager->PutXBLDocument(document);
}
}
}
else return NS_OK;
}
if (!document)
return NS_OK;
*aResult = document;
NS_IF_ADDREF(*aResult);
@ -714,7 +834,7 @@ nsXBLService::GetBindingDocument(nsIContent* aBoundElement, const nsCString& aUR
}
NS_IMETHODIMP
nsXBLService::FetchBindingDocument(nsIURI* aURI, nsIDocument** aResult)
nsXBLService::FetchBindingDocument(nsIContent* aBoundElement, nsIURI* aURI, const nsCString& aRef, nsIDocument** aResult)
{
// Initialize our out pointer to nsnull
*aResult = nsnull;
@ -749,8 +869,28 @@ nsXBLService::FetchBindingDocument(nsIURI* aURI, nsIDocument** aResult)
nsCOMPtr<nsIHTTPChannel> http(do_QueryInterface(channel));
if (http) {
// Need to be asynchronous
nsXBLStreamListener* xblListener = new nsXBLStreamListener(listener);
nsCOMPtr<nsIDocument> boundDoc;
aBoundElement->GetDocument(*getter_AddRefs(boundDoc));
nsXBLStreamListener* xblListener = new nsXBLStreamListener(listener, boundDoc, doc);
NS_ADDREF(xblListener);
// Add ourselves to the list of loading docs.
nsCOMPtr<nsIDocument> boundDocument;
aBoundElement->GetDocument(*getter_AddRefs(boundDocument));
nsCOMPtr<nsIBindingManager> bindingManager;
boundDocument->GetBindingManager(getter_AddRefs(bindingManager));
nsXPIDLCString uri;
aURI->GetSpec(getter_Copies(uri));
bindingManager->PutLoadingDocListener((const char*)uri, xblListener);
// Add our request.
nsCAutoString bindingURI(uri);
bindingURI += "#";
bindingURI += aRef;
nsXBLBindingRequest* req = new nsXBLBindingRequest(bindingURI, aBoundElement);
xblListener->AddRequest(req);
// Now kick off the async read.
http->AsyncRead(xblListener, nsnull);
return NS_OK;
}

View File

@ -50,6 +50,9 @@ class nsXBLService : public nsIXBLService, public nsIMemoryPressureObserver
NS_IMETHOD LoadBindings(nsIContent* aContent, const nsString& aURL, PRBool aAugmentFlag,
nsIXBLBinding** aBinding);
// This method loads a binding doc and then builds the specific binding required.
NS_IMETHOD GetBinding(nsIContent* aBoundElement, const nsCString& aURLStr, nsIXBLBinding** aResult);
// This function clears out the bindings on a given content node.
NS_IMETHOD FlushStyleBindings(nsIContent* aContent);
@ -69,14 +72,12 @@ public:
nsXBLService();
virtual ~nsXBLService();
// This method loads a binding doc and then builds the specific binding required.
NS_IMETHOD GetBinding(nsIContent* aBoundElement, const nsCString& aURLStr, nsIXBLBinding** aResult);
// This method checks the hashtable and then calls FetchBindingDocument on a miss.
NS_IMETHOD GetBindingDocument(nsIContent* aBoundElement, const nsCString& aURI, nsIDocument** aResult);
NS_IMETHOD GetBindingDocument(nsIContent* aBoundElement, const nsCString& aURI, const nsCString& aRef,
nsIDocument** aResult);
// This method synchronously loads and parses an XBL file.
NS_IMETHOD FetchBindingDocument(nsIURI* aURI, nsIDocument** aResult);
NS_IMETHOD FetchBindingDocument(nsIContent* aBoundElement, nsIURI* aURI, const nsCString& aRef, nsIDocument** aResult);
// This method walks a binding document and removes any text nodes
// that contain only whitespace.

View File

@ -32,6 +32,7 @@ EXPORTS = \
nsIBindingManager.h \
nsIXBLBinding.h \
nsIXBLService.h \
nsIXBLStreamListener.h \
$(NULL)
EXPORTS := $(addprefix $(srcdir)/, $(EXPORTS))

View File

@ -1,4 +1,6 @@
nsIBindingManager.h
nsIXBLBinding.h
nsIXBLService.h
nsIXBLStreamListener.h

View File

@ -25,6 +25,7 @@ EXPORTS = \
nsIBindingManager.h \
nsIXBLBinding.h \
nsIXBLService.h \
nsIXBLStreamListener.h \
$(NULL)
MODULE=raptor

View File

@ -37,6 +37,7 @@
class nsIContent;
class nsIXBLBinding;
class nsIAtom;
class nsIXBLStreamListener;
// {55D70FE0-C8E5-11d3-97FB-00400553EEF0}
#define NS_IBINDING_MANAGER_IID \
@ -66,6 +67,9 @@ public:
NS_IMETHOD PutXBLDocument(nsIDocument* aDocument) = 0;
NS_IMETHOD GetXBLDocument(const nsCString& aURL, nsIDocument** aResult) = 0;
NS_IMETHOD PutLoadingDocListener(const nsCString& aURL, nsIXBLStreamListener* aListener) = 0;
NS_IMETHOD GetLoadingDocListener(const nsCString& aURL, nsIXBLStreamListener** aResult) = 0;
NS_IMETHOD RemoveLoadingDocListener(const nsCString& aURL)=0;
};
#endif // nsIBinding_Manager_h__

View File

@ -55,6 +55,9 @@ public:
// This function clears out the bindings on a given content node.
NS_IMETHOD FlushStyleBindings(nsIContent* aContent) = 0;
// This method loads a binding doc and then builds the specific binding required.
NS_IMETHOD GetBinding(nsIContent* aBoundElement, const nsCString& aURLStr, nsIXBLBinding** aResult) = 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,

View File

@ -46,6 +46,7 @@
#include "nsITextContent.h"
#include "nsIXBLBinding.h"
#include "nsIXBLStreamListener.h"
// Static IIDs/CIDs. Try to minimize these.
static NS_DEFINE_CID(kNameSpaceManagerCID, NS_NAMESPACEMANAGER_CID);
@ -79,10 +80,16 @@ public:
NS_IMETHOD PutXBLDocument(nsIDocument* aDocument);
NS_IMETHOD GetXBLDocument(const nsCString& aURL, nsIDocument** aResult);
NS_IMETHOD PutLoadingDocListener(const nsCString& aURL, nsIXBLStreamListener* aListener);
NS_IMETHOD GetLoadingDocListener(const nsCString& aURL, nsIXBLStreamListener** aResult);
NS_IMETHOD RemoveLoadingDocListener(const nsCString& aURL);
// MEMBER VARIABLES
protected:
nsSupportsHashtable* mBindingTable;
nsSupportsHashtable* mDocumentTable;
nsSupportsHashtable* mLoadingDocTable;
nsCOMPtr<nsISupportsArray> mAttachedQueue;
};
@ -99,7 +106,10 @@ nsBindingManager::nsBindingManager(void)
NS_INIT_REFCNT();
mBindingTable = nsnull;
mDocumentTable = nsnull;
mLoadingDocTable = nsnull;
mAttachedQueue = nsnull;
}
@ -107,6 +117,7 @@ nsBindingManager::~nsBindingManager(void)
{
delete mBindingTable;
delete mDocumentTable;
delete mLoadingDocTable;
}
NS_IMETHODIMP
@ -310,6 +321,41 @@ nsBindingManager::GetXBLDocument(const nsCString& aURL, nsIDocument** aResult)
return NS_OK;
}
NS_IMETHODIMP
nsBindingManager::PutLoadingDocListener(const nsCString& aURL, nsIXBLStreamListener* aListener)
{
if (!mLoadingDocTable)
mLoadingDocTable = new nsSupportsHashtable();
nsStringKey key(aURL);
mLoadingDocTable->Put(&key, aListener);
return NS_OK;
}
NS_IMETHODIMP
nsBindingManager::GetLoadingDocListener(const nsCString& aURL, nsIXBLStreamListener** aResult)
{
*aResult = nsnull;
if (!mLoadingDocTable)
return NS_OK;
nsStringKey key(aURL);
*aResult = NS_STATIC_CAST(nsIXBLStreamListener*, mLoadingDocTable->Get(&key)); // Addref happens here.
return NS_OK;
}
NS_IMETHODIMP
nsBindingManager::RemoveLoadingDocListener(const nsCString& aURL)
{
if (!mLoadingDocTable)
return NS_OK;
nsStringKey key(aURL);
mLoadingDocTable->Remove(&key);
return NS_OK;
}
// Creation Routine ///////////////////////////////////////////////////////////////////////

View File

@ -52,6 +52,7 @@
#include "nsIXULContentUtils.h"
#include "nsIXULPrototypeCache.h"
#include "nsIXBLStreamListener.h"
// Static IIDs/CIDs. Try to minimize these.
static NS_DEFINE_CID(kNameSpaceManagerCID, NS_NAMESPACEMANAGER_CID);
@ -62,40 +63,118 @@ static NS_DEFINE_CID(kChromeRegistryCID, NS_CHROMEREGISTRY_CID);
// Individual binding requests.
struct nsXBLBindingRequest
{
nsCOMPtr<nsIURL> mURL;
nsCString mBindingURL;
nsCOMPtr<nsIContent> mBoundElement;
nsXBLBindingRequest(const nsCString& aURL, nsIContent* aBoundElement)
{
mBindingURL = aURL;
mBoundElement = aBoundElement;
gRefCnt++;
if (gRefCnt == 1) {
nsServiceManager::GetService("component://netscape/xbl",
NS_GET_IID(nsIXBLService),
(nsISupports**) &gXBLService);
}
}
~nsXBLBindingRequest()
{
gRefCnt--;
if (gRefCnt == 0) {
nsServiceManager::ReleaseService("component://netscape/xbl", gXBLService);
gXBLService = nsnull;
}
}
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;
mBoundElement->GetDocument(*getter_AddRefs(doc));
if (!doc)
return;
nsCOMPtr<nsIBindingManager> bindingManager;
doc->GetBindingManager(getter_AddRefs(bindingManager));
bindingManager->SetBinding(mBoundElement, newBinding);
// Set the binding's bound element.
newBinding->SetBoundElement(mBoundElement);
// Tell the binding to build the anonymous content.
newBinding->GenerateAnonymousContent(mBoundElement);
// 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();
}
// Now do a ContentInserted notification to cause the frames to get installed finally,
nsCOMPtr<nsIContent> parent;
mBoundElement->GetParent(*getter_AddRefs(parent));
PRInt32 index = 0;
if (parent)
parent->IndexOf(mBoundElement, index);
if (index == -1)
return;
doc->ContentInserted(parent, mBoundElement, index);
}
static nsIXBLService* gXBLService;
static int gRefCnt;
};
nsIXBLService* nsXBLBindingRequest::gXBLService = nsnull;
int nsXBLBindingRequest::gRefCnt = 0;
// nsXBLStreamListener, a helper class used for
// asynchronous parsing of URLs
/* Header file */
class nsXBLStreamListener : public nsIStreamListener
class nsXBLStreamListener : public nsIStreamListener, public nsIXBLStreamListener
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSISTREAMOBSERVER
nsXBLStreamListener(nsIStreamListener* aInner);
nsXBLStreamListener(nsIStreamListener* aInner, nsIDocument* aDocument, nsIDocument* aBindingDocument);
virtual ~nsXBLStreamListener();
/* additional members */
void AddRequest(nsXBLBindingRequest* aRequest) { mBindingRequests.AppendElement(aRequest); };
private:
nsCOMPtr<nsIStreamListener> mInner;
nsVoidArray mBindingRequests;
nsCOMPtr<nsIDocument> mDocument;
static nsIXBLService* gXBLService;
nsCOMPtr<nsIDocument> mBindingDocument;
};
nsIXBLService* nsXBLStreamListener::gXBLService = nsnull;
/* Implementation file */
NS_IMPL_ISUPPORTS2(nsXBLStreamListener, nsIStreamListener, nsIStreamObserver)
NS_IMPL_ISUPPORTS3(nsXBLStreamListener, nsIStreamListener, nsIStreamObserver, nsIXBLStreamListener)
nsXBLStreamListener::nsXBLStreamListener(nsIStreamListener* aInner)
nsXBLStreamListener::nsXBLStreamListener(nsIStreamListener* aInner, nsIDocument* aDocument,
nsIDocument* aBindingDocument)
{
NS_INIT_ISUPPORTS();
/* member initializers and constructor code */
mInner = aInner;
mDocument = aDocument;
mBindingDocument = aBindingDocument;
}
nsXBLStreamListener::~nsXBLStreamListener()
@ -117,16 +196,9 @@ nsXBLStreamListener::OnDataAvailable(nsIChannel* aChannel, nsISupports* aCtxt, n
NS_IMETHODIMP
nsXBLStreamListener::OnStartRequest(nsIChannel* aChannel, nsISupports* aCtxt)
{
nsresult rv;
if (mInner) {
rv = mInner->OnStartRequest(aChannel, aCtxt);
if (NS_FAILED(rv))
return rv;
//
}
if (mInner)
return mInner->OnStartRequest(aChannel, aCtxt);
return NS_ERROR_FAILURE;
}
@ -134,8 +206,39 @@ nsXBLStreamListener::OnStartRequest(nsIChannel* aChannel, nsISupports* aCtxt)
NS_IMETHODIMP
nsXBLStreamListener::OnStopRequest(nsIChannel* aChannel, nsISupports* aCtxt, nsresult aStatus, const PRUnichar* aStatusArg)
{
if (mInner)
return mInner->OnStopRequest(aChannel, aCtxt, aStatus, aStatusArg);
if (mInner) {
nsresult rv = mInner->OnStopRequest(aChannel, aCtxt, aStatus, aStatusArg);
if (NS_FAILED(rv))
return NS_ERROR_FAILURE;
// Remove ourselves from the set of pending docs.
nsCOMPtr<nsIBindingManager> bindingManager;
mDocument->GetBindingManager(getter_AddRefs(bindingManager));
nsCOMPtr<nsIURI> uri(mBindingDocument->GetDocumentURL());
nsXPIDLCString str;
uri->GetSpec(getter_Copies(str));
bindingManager->RemoveLoadingDocListener((const char*)str);
PRUint32 count = mBindingRequests.Count();
if (NS_SUCCEEDED(aStatus)) {
// Put our doc in the doc table.
bindingManager->PutXBLDocument(mBindingDocument);
// Notify all pending requests that their bindings are
// ready and can be installed.
for (PRUint32 i = 0; i < count; i++) {
nsXBLBindingRequest* req = (nsXBLBindingRequest*)mBindingRequests.ElementAt(i);
req->DocumentLoaded(mBindingDocument);
}
}
for (PRUint32 i = 0; i < count; i++) {
nsXBLBindingRequest* req = (nsXBLBindingRequest*)mBindingRequests.ElementAt(i);
delete req;
}
}
return NS_ERROR_FAILURE;
}
@ -560,7 +663,7 @@ NS_IMETHODIMP nsXBLService::GetBinding(nsIContent* aBoundElement, const nsCStrin
nsCOMPtr<nsIDocument> doc;
GetBindingDocument(aBoundElement, uri, getter_AddRefs(doc));
GetBindingDocument(aBoundElement, uri, ref, getter_AddRefs(doc));
if (!doc)
return NS_ERROR_FAILURE;
@ -637,7 +740,8 @@ static PRBool IsChromeOrResourceURI(nsIURI* aURI)
}
NS_IMETHODIMP
nsXBLService::GetBindingDocument(nsIContent* aBoundElement, const nsCString& aURLStr, nsIDocument** aResult)
nsXBLService::GetBindingDocument(nsIContent* aBoundElement, const nsCString& aURLStr, const nsCString& aRef,
nsIDocument** aResult)
{
nsresult rv;
@ -660,53 +764,69 @@ nsXBLService::GetBindingDocument(nsIContent* aBoundElement, const nsCString& aUR
nsCOMPtr<nsIBindingManager> bindingManager;
boundDocument->GetBindingManager(getter_AddRefs(bindingManager));
bindingManager->GetXBLDocument(aURLStr, getter_AddRefs(document));
}
if (!document) {
// XXX 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
// processed whenever the doc does finish loading.
// Finally, if all lines of defense fail, we go and fetch the binding
// document.
nsCOMPtr<nsIURL> uri;
nsComponentManager::CreateInstance("component://netscape/network/standard-url",
nsnull,
NS_GET_IID(nsIURL),
getter_AddRefs(uri));
uri->SetSpec(aURLStr);
FetchBindingDocument(uri, getter_AddRefs(document));
if (document) {
// If the doc is a chrome URI, then we put it into the XUL cache.
PRBool cached = PR_FALSE;
if (IsChromeOrResourceURI(uri) && gXULUtils->UseXULCache()) {
cached = PR_TRUE;
gXULCache->PutXBLDocument(document);
// Cache whether or not this chrome XBL can execute scripts.
nsCOMPtr<nsIChromeRegistry> reg(do_GetService(kChromeRegistryCID, &rv));
if (NS_SUCCEEDED(rv) && reg) {
PRBool allow;
reg->AllowScriptsForSkin(uri, &allow);
if (!allow)
gXULCache->PutXBLDocScriptAccess(document);
}
}
if (!cached) {
// Otherwise we put it in our binding manager's document table.
nsCOMPtr<nsIDocument> boundDocument;
aBoundElement->GetDocument(*getter_AddRefs(boundDocument));
nsCOMPtr<nsIBindingManager> bindingManager;
boundDocument->GetBindingManager(getter_AddRefs(bindingManager));
bindingManager->PutXBLDocument(document);
if (!document) {
// 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
// processed whenever the doc does finish loading.
nsCOMPtr<nsIXBLStreamListener> listener;
bindingManager->GetLoadingDocListener(aURLStr, getter_AddRefs(listener));
if (listener) {
// Create a new load observer.
nsCAutoString bindingURI(aURLStr);
bindingURI += "#";
bindingURI += aRef;
nsXBLBindingRequest* req = new nsXBLBindingRequest(aRef, aBoundElement);
nsIXBLStreamListener* ilist = listener.get();
nsXBLStreamListener* xblListener = NS_STATIC_CAST(nsXBLStreamListener*, ilist);
xblListener->AddRequest(req);
}
}
if (!document) {
// Finally, if all lines of defense fail, we go and fetch the binding
// document.
nsCOMPtr<nsIURL> uri;
nsComponentManager::CreateInstance("component://netscape/network/standard-url",
nsnull,
NS_GET_IID(nsIURL),
getter_AddRefs(uri));
uri->SetSpec(aURLStr);
FetchBindingDocument(aBoundElement, uri, aRef, getter_AddRefs(document));
if (document) {
// If the doc is a chrome URI, then we put it into the XUL cache.
PRBool cached = PR_FALSE;
if (IsChromeOrResourceURI(uri) && gXULUtils->UseXULCache()) {
cached = PR_TRUE;
gXULCache->PutXBLDocument(document);
// Cache whether or not this chrome XBL can execute scripts.
nsCOMPtr<nsIChromeRegistry> reg(do_GetService(kChromeRegistryCID, &rv));
if (NS_SUCCEEDED(rv) && reg) {
PRBool allow;
reg->AllowScriptsForSkin(uri, &allow);
if (!allow)
gXULCache->PutXBLDocScriptAccess(document);
}
}
if (!cached) {
// Otherwise we put it in our binding manager's document table.
nsCOMPtr<nsIDocument> boundDocument;
aBoundElement->GetDocument(*getter_AddRefs(boundDocument));
nsCOMPtr<nsIBindingManager> bindingManager;
boundDocument->GetBindingManager(getter_AddRefs(bindingManager));
bindingManager->PutXBLDocument(document);
}
}
}
else return NS_OK;
}
if (!document)
return NS_OK;
*aResult = document;
NS_IF_ADDREF(*aResult);
@ -714,7 +834,7 @@ nsXBLService::GetBindingDocument(nsIContent* aBoundElement, const nsCString& aUR
}
NS_IMETHODIMP
nsXBLService::FetchBindingDocument(nsIURI* aURI, nsIDocument** aResult)
nsXBLService::FetchBindingDocument(nsIContent* aBoundElement, nsIURI* aURI, const nsCString& aRef, nsIDocument** aResult)
{
// Initialize our out pointer to nsnull
*aResult = nsnull;
@ -749,8 +869,28 @@ nsXBLService::FetchBindingDocument(nsIURI* aURI, nsIDocument** aResult)
nsCOMPtr<nsIHTTPChannel> http(do_QueryInterface(channel));
if (http) {
// Need to be asynchronous
nsXBLStreamListener* xblListener = new nsXBLStreamListener(listener);
nsCOMPtr<nsIDocument> boundDoc;
aBoundElement->GetDocument(*getter_AddRefs(boundDoc));
nsXBLStreamListener* xblListener = new nsXBLStreamListener(listener, boundDoc, doc);
NS_ADDREF(xblListener);
// Add ourselves to the list of loading docs.
nsCOMPtr<nsIDocument> boundDocument;
aBoundElement->GetDocument(*getter_AddRefs(boundDocument));
nsCOMPtr<nsIBindingManager> bindingManager;
boundDocument->GetBindingManager(getter_AddRefs(bindingManager));
nsXPIDLCString uri;
aURI->GetSpec(getter_Copies(uri));
bindingManager->PutLoadingDocListener((const char*)uri, xblListener);
// Add our request.
nsCAutoString bindingURI(uri);
bindingURI += "#";
bindingURI += aRef;
nsXBLBindingRequest* req = new nsXBLBindingRequest(bindingURI, aBoundElement);
xblListener->AddRequest(req);
// Now kick off the async read.
http->AsyncRead(xblListener, nsnull);
return NS_OK;
}

View File

@ -50,6 +50,9 @@ class nsXBLService : public nsIXBLService, public nsIMemoryPressureObserver
NS_IMETHOD LoadBindings(nsIContent* aContent, const nsString& aURL, PRBool aAugmentFlag,
nsIXBLBinding** aBinding);
// This method loads a binding doc and then builds the specific binding required.
NS_IMETHOD GetBinding(nsIContent* aBoundElement, const nsCString& aURLStr, nsIXBLBinding** aResult);
// This function clears out the bindings on a given content node.
NS_IMETHOD FlushStyleBindings(nsIContent* aContent);
@ -69,14 +72,12 @@ public:
nsXBLService();
virtual ~nsXBLService();
// This method loads a binding doc and then builds the specific binding required.
NS_IMETHOD GetBinding(nsIContent* aBoundElement, const nsCString& aURLStr, nsIXBLBinding** aResult);
// This method checks the hashtable and then calls FetchBindingDocument on a miss.
NS_IMETHOD GetBindingDocument(nsIContent* aBoundElement, const nsCString& aURI, nsIDocument** aResult);
NS_IMETHOD GetBindingDocument(nsIContent* aBoundElement, const nsCString& aURI, const nsCString& aRef,
nsIDocument** aResult);
// This method synchronously loads and parses an XBL file.
NS_IMETHOD FetchBindingDocument(nsIURI* aURI, nsIDocument** aResult);
NS_IMETHOD FetchBindingDocument(nsIContent* aBoundElement, nsIURI* aURI, const nsCString& aRef, nsIDocument** aResult);
// This method walks a binding document and removes any text nodes
// that contain only whitespace.