gecko-dev/content/xbl/src/nsXBLService.cpp

1258 lines
40 KiB
C++
Raw Normal View History

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
2000-03-21 13:14:34 +00:00
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
2000-03-21 13:14:34 +00:00
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
2000-03-21 13:14:34 +00:00
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
2000-03-21 13:14:34 +00:00
*
* Contributor(s):
* Original Author: David W. Hyatt (hyatt@netscape.com)
* - Brendan Eich (brendan@mozilla.org)
* - Mike Pinkerton (pinkerton@netscape.com)
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
2000-03-21 13:14:34 +00:00
#include "nsCOMPtr.h"
#include "nsNetUtil.h"
2000-05-28 04:10:50 +00:00
#include "nsXBLService.h"
2000-09-22 05:02:20 +00:00
#include "nsXBLWindowKeyHandler.h"
#include "nsXBLWindowDragHandler.h"
2000-01-12 09:44:18 +00:00
#include "nsIInputStream.h"
#include "nsINameSpaceManager.h"
#include "nsHashtable.h"
#include "nsIURI.h"
2000-09-22 05:02:20 +00:00
#include "nsIDOMElement.h"
2000-01-12 09:44:18 +00:00
#include "nsIURL.h"
#include "nsIChannel.h"
#include "nsXPIDLString.h"
#include "nsIParser.h"
#include "nsParserCIID.h"
#include "nsNetUtil.h"
#include "plstr.h"
#include "nsIContent.h"
2000-01-15 05:31:45 +00:00
#include "nsIDOMElement.h"
#include "nsIDocument.h"
#include "nsIXMLContentSink.h"
#include "nsContentCID.h"
2000-01-12 10:55:56 +00:00
#include "nsXMLDocument.h"
2000-01-13 02:23:54 +00:00
#include "nsHTMLAtoms.h"
#include "nsSupportsArray.h"
#include "nsITextContent.h"
#include "nsIMemory.h"
#include "nsIObserverService.h"
#include "nsIDOMNodeList.h"
#include "nsXBLContentSink.h"
#include "nsXBLBinding.h"
#include "nsXBLPrototypeBinding.h"
#include "nsIXBLDocumentInfo.h"
#include "nsXBLAtoms.h"
#include "nsXULAtoms.h"
#include "nsCRT.h"
#include "nsContentUtils.h"
#include "nsISyncLoadDOMService.h"
#include "nsIDOM3Node.h"
#include "nsContentPolicyUtils.h"
#include "nsIPresShell.h"
#include "nsIDocumentObserver.h"
#include "nsFrameManager.h"
#include "nsStyleContext.h"
#include "nsIScriptSecurityManager.h"
#ifdef MOZ_XUL
2000-08-05 22:33:29 +00:00
#include "nsIXULPrototypeCache.h"
#endif
#include "nsIDOMLoadListener.h"
#include "nsIDOMEventGroup.h"
2000-08-05 22:33:29 +00:00
// Static IIDs/CIDs. Try to minimize these.
static NS_DEFINE_CID(kXMLDocumentCID, NS_XMLDOCUMENT_CID);
2000-01-12 09:44:18 +00:00
static PRBool IsChromeOrResourceURI(nsIURI* aURI)
{
PRBool isChrome = PR_FALSE;
PRBool isResource = PR_FALSE;
if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) &&
NS_SUCCEEDED(aURI->SchemeIs("resource", &isResource)))
return (isChrome || isResource);
return PR_FALSE;
}
static PRBool IsResourceURI(nsIURI* aURI)
{
PRBool isResource = PR_FALSE;
if (NS_SUCCEEDED(aURI->SchemeIs("resource", &isResource)) && isResource)
return PR_TRUE;
return PR_FALSE;
}
2000-08-05 22:33:29 +00:00
// Individual binding requests.
class nsXBLBindingRequest
2000-08-05 22:33:29 +00:00
{
public:
nsCOMPtr<nsIURL> mBindingURL;
2000-08-06 04:57:55 +00:00
nsCOMPtr<nsIContent> mBoundElement;
static nsXBLBindingRequest*
Create(nsFixedSizeAllocator& aPool, nsIURL* aURL, nsIContent* aBoundElement) {
void* place = aPool.Alloc(sizeof(nsXBLBindingRequest));
return place ? ::new (place) nsXBLBindingRequest(aURL, aBoundElement) : nsnull;
}
static void
Destroy(nsFixedSizeAllocator& aPool, nsXBLBindingRequest* aRequest) {
aRequest->~nsXBLBindingRequest();
aPool.Free(aRequest, sizeof(*aRequest));
}
2000-08-06 04:57:55 +00:00
void DocumentLoaded(nsIDocument* aBindingDoc)
{
// We only need the document here to cause frame construction, so
// we need the current doc, not the owner doc.
nsIDocument* doc = mBoundElement->GetCurrentDoc();
2000-08-06 04:57:55 +00:00
if (!doc)
return;
// Get the binding.
PRBool ready = PR_FALSE;
gXBLService->BindingReady(mBoundElement, mBindingURL, &ready);
2000-08-06 04:57:55 +00:00
if (!ready)
return;
2000-08-06 04:57:55 +00:00
// XXX Deal with layered bindings. For example, mBoundElement may be anonymous content.
2000-08-06 04:57:55 +00:00
// Now do a ContentInserted notification to cause the frames to get installed finally,
nsIContent* parent = mBoundElement->GetParent();
2000-08-06 04:57:55 +00:00
PRInt32 index = 0;
if (parent)
index = parent->IndexOf(mBoundElement);
// If |mBoundElement| is (in addition to having binding |mBinding|)
// also a descendant of another element with binding |mBinding|,
// then we might have just constructed it due to the
// notification of its parent. (We can know about both if the
// binding loads were triggered from the DOM rather than frame
// construction.) So we have to check both whether the element
// has a primary frame and whether it's in the undisplayed map
// before sending a ContentInserted notification, or bad things
// will happen.
nsIPresShell *shell = doc->GetShellAt(0);
if (shell) {
2000-08-14 18:52:47 +00:00
nsIFrame* childFrame;
shell->GetPrimaryFrameFor(mBoundElement, &childFrame);
if (!childFrame) {
// Check to see if it's in the undisplayed content map.
nsStyleContext* sc =
shell->FrameManager()->GetUndisplayedContent(mBoundElement);
if (!sc) {
nsCOMPtr<nsIDocumentObserver> obs(do_QueryInterface(shell));
obs->ContentInserted(doc, parent, mBoundElement, index);
}
}
}
2000-08-06 04:57:55 +00:00
}
static nsIXBLService* gXBLService;
static int gRefCnt;
protected:
nsXBLBindingRequest(nsIURL* aURL, nsIContent* aBoundElement)
: mBindingURL(aURL),
mBoundElement(aBoundElement)
{
gRefCnt++;
if (gRefCnt == 1) {
CallGetService("@mozilla.org/xbl;1", &gXBLService);
}
}
~nsXBLBindingRequest()
{
gRefCnt--;
if (gRefCnt == 0) {
NS_IF_RELEASE(gXBLService);
}
}
private:
// Hide so that only Create() and Destroy() can be used to
// allocate and deallocate from the heap
static void* operator new(size_t) CPP_THROW_NEW { return 0; }
static void operator delete(void*, size_t) {}
2000-08-05 22:33:29 +00:00
};
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;
2000-08-06 04:57:55 +00:00
nsIXBLService* nsXBLBindingRequest::gXBLService = nsnull;
int nsXBLBindingRequest::gRefCnt = 0;
2000-08-04 08:45:29 +00:00
// nsXBLStreamListener, a helper class used for
// asynchronous parsing of URLs
/* Header file */
class nsXBLStreamListener : public nsIStreamListener, public nsIDOMLoadListener
2000-08-04 08:45:29 +00:00
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIREQUESTOBSERVER
2000-08-04 08:45:29 +00:00
NS_IMETHOD Load(nsIDOMEvent* aEvent);
NS_IMETHOD BeforeUnload(nsIDOMEvent* aEvent) { return NS_OK; }
NS_IMETHOD Unload(nsIDOMEvent* aEvent) { return NS_OK; }
NS_IMETHOD Abort(nsIDOMEvent* aEvent) { return NS_OK; }
NS_IMETHOD Error(nsIDOMEvent* aEvent) { return NS_OK; }
NS_IMETHOD PageRestore(nsIDOMEvent* aEvent) { return NS_OK; }
NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) { return NS_OK; }
#ifdef MOZ_XUL
static nsIXULPrototypeCache* gXULCache;
static PRInt32 gRefCnt;
#endif
nsXBLStreamListener(nsXBLService* aXBLService, nsIStreamListener* aInner, nsIDocument* aDocument, nsIDocument* aBindingDocument);
2000-08-04 08:45:29 +00:00
virtual ~nsXBLStreamListener();
2000-08-06 04:57:55 +00:00
void AddRequest(nsXBLBindingRequest* aRequest) { mBindingRequests.AppendElement(aRequest); };
PRBool HasRequest(nsIURI* aURI, nsIContent* aBoundElement);
2000-08-05 22:33:29 +00:00
2000-08-06 04:57:55 +00:00
private:
nsXBLService* mXBLService; // [WEAK]
2000-08-06 04:57:55 +00:00
nsCOMPtr<nsIStreamListener> mInner;
nsAutoVoidArray mBindingRequests;
2000-08-06 04:57:55 +00:00
2000-08-15 21:00:52 +00:00
nsCOMPtr<nsIWeakReference> mDocument;
2000-08-06 04:57:55 +00:00
nsCOMPtr<nsIDocument> mBindingDocument;
2000-08-04 08:45:29 +00:00
};
#ifdef MOZ_XUL
nsIXULPrototypeCache* nsXBLStreamListener::gXULCache = nsnull;
PRInt32 nsXBLStreamListener::gRefCnt = 0;
#endif
2000-08-05 22:33:29 +00:00
2000-08-04 08:45:29 +00:00
/* Implementation file */
NS_IMPL_ISUPPORTS4(nsXBLStreamListener, nsIStreamListener, nsIRequestObserver, nsIDOMLoadListener, nsIDOMEventListener)
2000-08-04 08:45:29 +00:00
nsXBLStreamListener::nsXBLStreamListener(nsXBLService* aXBLService,
nsIStreamListener* aInner, nsIDocument* aDocument,
2000-08-06 04:57:55 +00:00
nsIDocument* aBindingDocument)
2000-08-04 08:45:29 +00:00
{
/* member initializers and constructor code */
mXBLService = aXBLService;
2000-08-04 08:45:29 +00:00
mInner = aInner;
mDocument = do_GetWeakReference(aDocument);
2000-08-06 04:57:55 +00:00
mBindingDocument = aBindingDocument;
#ifdef MOZ_XUL
gRefCnt++;
if (gRefCnt == 1) {
nsresult rv = CallGetService("@mozilla.org/xul/xul-prototype-cache;1",
&gXULCache);
if (NS_FAILED(rv)) return;
}
#endif
2000-08-04 08:45:29 +00:00
}
nsXBLStreamListener::~nsXBLStreamListener()
{
#ifdef MOZ_XUL
2000-08-04 08:45:29 +00:00
/* destructor code */
gRefCnt--;
if (gRefCnt == 0) {
NS_IF_RELEASE(gXULCache);
}
#endif
2000-08-04 08:45:29 +00:00
}
NS_IMETHODIMP
nsXBLStreamListener::OnDataAvailable(nsIRequest *request, nsISupports* aCtxt, nsIInputStream* aInStr,
2000-08-04 08:45:29 +00:00
PRUint32 aSourceOffset, PRUint32 aCount)
{
if (mInner)
return mInner->OnDataAvailable(request, aCtxt, aInStr, aSourceOffset, aCount);
2000-08-04 08:45:29 +00:00
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsXBLStreamListener::OnStartRequest(nsIRequest* request, nsISupports* aCtxt)
2000-08-04 08:45:29 +00:00
{
2000-08-06 04:57:55 +00:00
if (mInner)
return mInner->OnStartRequest(request, aCtxt);
2000-08-06 04:57:55 +00:00
2000-08-04 08:45:29 +00:00
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsXBLStreamListener::OnStopRequest(nsIRequest* request, nsISupports* aCtxt, nsresult aStatus)
2000-08-04 08:45:29 +00:00
{
nsresult rv = NS_OK;
2000-08-06 04:57:55 +00:00
if (mInner) {
rv = mInner->OnStopRequest(request, aCtxt, aStatus);
}
2000-08-06 04:57:55 +00:00
if (NS_FAILED(rv) || NS_FAILED(aStatus))
{
nsCOMPtr<nsIChannel> aChannel = do_QueryInterface(request);
if (aChannel)
{
nsCOMPtr<nsIURI> channelURI;
aChannel->GetURI(getter_AddRefs(channelURI));
nsCAutoString str;
channelURI->GetAsciiSpec(str);
printf("Failed to load XBL document %s\n", str.get());
}
2000-08-06 04:57:55 +00:00
PRUint32 count = mBindingRequests.Count();
for (PRUint32 i = 0; i < count; i++) {
nsXBLBindingRequest* req = (nsXBLBindingRequest*)mBindingRequests.ElementAt(i);
nsXBLBindingRequest::Destroy(mXBLService->mPool, req);
2000-08-06 04:57:55 +00:00
}
mBindingRequests.Clear();
mDocument = nsnull;
mBindingDocument = nsnull;
2000-08-06 04:57:55 +00:00
}
return rv;
}
PRBool
nsXBLStreamListener::HasRequest(nsIURI* 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);
PRBool eq;
if (req->mBoundElement == aElt &&
NS_SUCCEEDED(req->mBindingURL->Equals(aURI, &eq)) && eq)
return PR_TRUE;
}
return PR_FALSE;
}
nsresult
nsXBLStreamListener::Load(nsIDOMEvent* aEvent)
{
nsresult rv = NS_OK;
2000-08-15 21:00:52 +00:00
PRUint32 i;
PRUint32 count = mBindingRequests.Count();
2000-08-15 21:00:52 +00:00
// See if we're still alive.
nsCOMPtr<nsIDocument> doc(do_QueryReferent(mDocument));
if (!doc) {
NS_WARNING("XBL load did not complete until after document went away! Modal dialog bug?\n");
2000-08-15 21:00:52 +00:00
}
else {
2001-03-03 22:01:01 +00:00
// We have to do a flush prior to notification of the document load.
// This has to happen since the HTML content sink can be holding on
// to notifications related to our children (e.g., if you bind to the
// <body> tag) that result in duplication of content.
// We need to get the sink's notifications flushed and then make the binding
// ready.
if (count > 0) {
nsXBLBindingRequest* req = (nsXBLBindingRequest*)mBindingRequests.ElementAt(0);
nsIDocument* document = req->mBoundElement->GetCurrentDoc();
2001-03-03 22:01:01 +00:00
if (document)
document->FlushPendingNotifications(Flush_ContentAndNotify);
2001-03-03 22:01:01 +00:00
}
2000-08-15 21:00:52 +00:00
// Remove ourselves from the set of pending docs.
nsIBindingManager *bindingManager = doc->BindingManager();
nsIURI* documentURI = mBindingDocument->GetDocumentURI();
bindingManager->RemoveLoadingDocListener(documentURI);
2000-08-15 21:00:52 +00:00
if (!mBindingDocument->GetRootContent()) {
2000-09-02 01:09:47 +00:00
NS_ERROR("*** XBL doc with no root element! Something went horribly wrong! ***");
return NS_ERROR_FAILURE;
}
2000-08-15 21:00:52 +00:00
// Put our doc info in the doc table.
2000-08-15 21:00:52 +00:00
nsCOMPtr<nsIXBLDocumentInfo> info;
nsIBindingManager *xblDocBindingManager = mBindingDocument->BindingManager();
xblDocBindingManager->GetXBLDocumentInfo(documentURI, getter_AddRefs(info));
xblDocBindingManager->RemoveXBLDocumentInfo(info); // Break the self-imposed cycle.
if (!info) {
NS_ERROR("An XBL file is malformed. Did you forget the XBL namespace on the bindings tag?");
return NS_ERROR_FAILURE;
}
2000-08-15 21:00:52 +00:00
// If the doc is a chrome URI, then we put it into the XUL cache.
#ifdef MOZ_XUL
if (IsChromeOrResourceURI(documentURI)) {
PRBool useXULCache;
gXULCache->GetEnabled(&useXULCache);
if (useXULCache)
2000-10-10 20:52:25 +00:00
gXULCache->PutXBLDocumentInfo(info);
}
#endif
bindingManager->PutXBLDocumentInfo(info);
2000-08-15 21:00:52 +00:00
// Notify all pending requests that their bindings are
// ready and can be installed.
for (i = 0; i < count; i++) {
nsXBLBindingRequest* req = (nsXBLBindingRequest*)mBindingRequests.ElementAt(i);
req->DocumentLoaded(mBindingDocument);
}
}
for (i = 0; i < count; i++) {
nsXBLBindingRequest* req = (nsXBLBindingRequest*)mBindingRequests.ElementAt(i);
nsXBLBindingRequest::Destroy(mXBLService->mPool, req);
}
nsCOMPtr<nsIDOMEventReceiver> rec(do_QueryInterface(mBindingDocument));
rec->RemoveEventListener(NS_LITERAL_STRING("load"), (nsIDOMLoadListener*)this, PR_FALSE);
2000-08-15 21:00:52 +00:00
mBindingRequests.Clear();
mDocument = nsnull;
mBindingDocument = nsnull;
return rv;
2000-08-04 08:45:29 +00:00
}
2000-01-12 09:44:18 +00:00
// Implementation /////////////////////////////////////////////////////////////////
// Static member variable initialization
PRUint32 nsXBLService::gRefCnt = 0;
#ifdef MOZ_XUL
2000-08-05 22:33:29 +00:00
nsIXULPrototypeCache* nsXBLService::gXULCache = nsnull;
#endif
2000-08-05 22:33:29 +00:00
2000-05-28 04:10:50 +00:00
nsHashtable* nsXBLService::gClassTable = nsnull;
JSCList nsXBLService::gClassLRUList = JS_INIT_STATIC_CLIST(&nsXBLService::gClassLRUList);
PRUint32 nsXBLService::gClassLRUListLength = 0;
PRUint32 nsXBLService::gClassLRUListQuota = 64;
// Enabled by default. Must be over-ridden to disable
PRBool nsXBLService::gDisableChromeCache = PR_FALSE;
static const char kDisableChromeCachePref[] = "nglayout.debug.disable_xul_cache";
2000-01-12 09:44:18 +00:00
// Implement our nsISupports methods
NS_IMPL_ISUPPORTS3(nsXBLService, nsIXBLService, nsIObserver, nsISupportsWeakReference)
2000-01-12 09:44:18 +00:00
// Constructors/Destructors
nsXBLService::nsXBLService(void)
{
mPool.Init("XBL Binding Requests", kBucketSizes, kNumBuckets, kInitialSize);
2000-01-12 09:44:18 +00:00
gRefCnt++;
if (gRefCnt == 1) {
gClassTable = new nsHashtable();
#ifdef MOZ_XUL
// Find out if the XUL cache is on or off
gDisableChromeCache = nsContentUtils::GetBoolPref(kDisableChromeCachePref,
gDisableChromeCache);
2000-05-28 04:10:50 +00:00
CallGetService("@mozilla.org/xul/xul-prototype-cache;1", &gXULCache);
#endif
2000-01-12 09:44:18 +00:00
}
}
nsXBLService::~nsXBLService(void)
{
gRefCnt--;
if (gRefCnt == 0) {
// Walk the LRU list removing and deleting the nsXBLJSClasses.
FlushMemory();
// Any straggling nsXBLJSClass instances held by unfinalized JS objects
// created for bindings will be deleted when those objects are finalized
// (and not put on gClassLRUList, because length >= quota).
gClassLRUListLength = gClassLRUListQuota = 0;
// At this point, the only hash table entries should be for referenced
// XBL class structs held by unfinalized JS binding objects.
2000-05-28 04:10:50 +00:00
delete gClassTable;
gClassTable = nsnull;
#ifdef MOZ_XUL
NS_IF_RELEASE(gXULCache);
#endif
}
2000-01-12 09:44:18 +00:00
}
2000-01-12 09:32:29 +00:00
// This function loads a particular XBL file and installs all of the bindings
// onto the element.
NS_IMETHODIMP
nsXBLService::LoadBindings(nsIContent* aContent, nsIURI* aURL, PRBool aAugmentFlag,
nsXBLBinding** aBinding, PRBool* aResolveStyle)
2000-01-12 09:32:29 +00:00
{
2000-07-28 00:35:02 +00:00
*aBinding = nsnull;
*aResolveStyle = PR_FALSE;
2000-07-28 00:35:02 +00:00
nsresult rv;
2000-01-13 02:23:54 +00:00
nsCOMPtr<nsIDocument> document = aContent->GetOwnerDoc();
// XXX document may be null if we're in the midst of paint suppression
if (!document)
return NS_OK;
nsIBindingManager *bindingManager = document->BindingManager();
nsXBLBinding *binding = bindingManager->GetBinding(aContent);
2000-06-02 08:13:29 +00:00
if (binding && !aAugmentFlag) {
nsXBLBinding *styleBinding = binding->GetFirstStyleBinding();
2000-06-02 08:13:29 +00:00
if (styleBinding) {
if (binding->MarkedForDeath()) {
2000-07-28 02:22:59 +00:00
FlushStyleBindings(aContent);
binding = nsnull;
}
else {
// See if the URIs match.
nsIURI* uri = styleBinding->PrototypeBinding()->BindingURI();
PRBool equal;
if (NS_SUCCEEDED(uri->Equals(aURL, &equal)) && equal)
return NS_OK;
FlushStyleBindings(aContent);
binding = nsnull;
}
2000-06-02 08:13:29 +00:00
}
2000-04-27 02:08:35 +00:00
}
// Security check - remote pages can't load local bindings, except from chrome
nsIURI *docURI = document->GetDocumentURI();
PRBool isChrome = PR_FALSE;
rv = docURI->SchemeIs("chrome", &isChrome);
// Not everything with a chrome URI has a system principal. See bug 160042.
if (NS_FAILED(rv) || !isChrome) {
nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
rv = secMan->
CheckLoadURIWithPrincipal(document->GetPrincipal(), aURL,
nsIScriptSecurityManager::ALLOW_CHROME);
if (NS_FAILED(rv))
return rv;
}
// Content policy check
PRInt16 decision = nsIContentPolicy::ACCEPT;
rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_OTHER,
aURL,
docURI,
aContent,
EmptyCString(), // mime guess
nsnull, // extra
&decision);
if (NS_SUCCEEDED(rv) && !NS_CP_ACCEPTED(decision))
rv = NS_ERROR_NOT_AVAILABLE;
if (NS_FAILED(rv))
return rv;
PRBool ready;
nsRefPtr<nsXBLBinding> newBinding;
if (NS_FAILED(rv = GetBinding(aContent, aURL, PR_FALSE, &ready,
getter_AddRefs(newBinding)))) {
return rv;
}
2000-01-12 09:44:18 +00:00
2000-06-02 08:13:29 +00:00
if (!newBinding) {
#ifdef DEBUG
nsCAutoString spec;
aURL->GetSpec(spec);
nsCAutoString str(NS_LITERAL_CSTRING("Failed to locate XBL binding. XBL is now using id instead of name to reference bindings. Make sure you have switched over. The invalid binding name is: ") + spec);
NS_ERROR(str.get());
#endif
2000-08-04 08:45:29 +00:00
return NS_OK;
2000-03-30 03:18:44 +00:00
}
2000-06-02 08:13:29 +00:00
if (aAugmentFlag) {
nsXBLBinding *baseBinding;
nsXBLBinding *nextBinding = newBinding;
2000-06-02 08:13:29 +00:00
do {
baseBinding = nextBinding;
nextBinding = baseBinding->GetBaseBinding();
2000-06-02 08:13:29 +00:00
baseBinding->SetIsStyleBinding(PR_FALSE);
} while (nextBinding);
// XXX Handle adjusting the prototype chain! We need to somehow indicate to
// InstallImplementation that the whole chain should just be whacked and rebuilt.
2000-06-02 08:13:29 +00:00
// We are becoming the new binding.
bindingManager->SetBinding(aContent, newBinding);
baseBinding->SetBaseBinding(binding);
}
else {
// We loaded a style binding. It goes on the end.
if (binding) {
// Get the last binding that is in the append layer.
binding->RootBinding()->SetBaseBinding(newBinding);
2000-06-02 08:13:29 +00:00
}
else {
// Install the binding on the content node.
bindingManager->SetBinding(aContent, newBinding);
}
}
// Set the binding's bound element.
newBinding->SetBoundElement(aContent);
2000-01-13 02:23:54 +00:00
// Tell the binding to build the anonymous content.
newBinding->GenerateAnonymousContent();
// Tell the binding to install event handlers
newBinding->InstallEventHandlers();
// Set up our properties
newBinding->InstallImplementation();
// Figure out if we need to execute a constructor.
*aBinding = newBinding->GetFirstBindingWithConstructor();
NS_IF_ADDREF(*aBinding);
// Figure out if we have any scoped sheets. If so, we do a second resolve.
*aResolveStyle = newBinding->HasStyleSheets();
2000-01-12 09:32:29 +00:00
return NS_OK;
}
nsresult
2000-06-02 08:13:29 +00:00
nsXBLService::FlushStyleBindings(nsIContent* aContent)
2000-03-28 00:41:33 +00:00
{
nsCOMPtr<nsIDocument> document = aContent->GetOwnerDoc();
// XXX doc will be null if we're in the midst of paint suppression.
if (! document)
return NS_OK;
nsIBindingManager *bindingManager = document->BindingManager();
nsXBLBinding *binding = bindingManager->GetBinding(aContent);
2000-04-03 07:13:07 +00:00
if (binding) {
nsXBLBinding *styleBinding = binding->GetFirstStyleBinding();
2000-06-02 08:13:29 +00:00
if (styleBinding) {
// Clear out the script references.
2000-06-22 00:36:19 +00:00
styleBinding->UnhookEventHandlers();
2000-06-02 08:13:29 +00:00
styleBinding->ChangeDocument(document, nsnull);
}
if (styleBinding == binding)
bindingManager->SetBinding(aContent, nsnull); // Flush old style bindings
2000-04-03 07:13:07 +00:00
}
2000-06-02 08:13:29 +00:00
2000-03-28 00:41:33 +00:00
return NS_OK;
}
2000-03-11 10:36:39 +00:00
NS_IMETHODIMP
nsXBLService::ResolveTag(nsIContent* aContent, PRInt32* aNameSpaceID,
nsIAtom** aResult)
2000-03-11 10:36:39 +00:00
{
nsIDocument* document = aContent->GetOwnerDoc();
if (document) {
return document->BindingManager()->ResolveTag(aContent, aNameSpaceID,
aResult);
}
2000-03-11 10:36:39 +00:00
*aNameSpaceID = aContent->GetNameSpaceID();
NS_ADDREF(*aResult = aContent->Tag());
return NS_OK;
2000-03-11 10:36:39 +00:00
}
nsresult
nsXBLService::GetXBLDocumentInfo(nsIURI* aURI, nsIContent* aBoundElement, nsIXBLDocumentInfo** aResult)
{
*aResult = nsnull;
#ifdef MOZ_XUL
PRBool useXULCache;
gXULCache->GetEnabled(&useXULCache);
if (useXULCache) {
// The first line of defense is the chrome cache.
// This cache crosses the entire product, so any XBL bindings that are
// part of chrome will be reused across all XUL documents.
gXULCache->GetXBLDocumentInfo(aURI, aResult);
}
#endif
if (!*aResult) {
// The second line of defense is the binding manager's document table.
nsIDocument* boundDocument = aBoundElement->GetOwnerDoc();
if (boundDocument)
boundDocument->BindingManager()->GetXBLDocumentInfo(aURI, aResult);
}
return NS_OK;
}
//
// AttachGlobalKeyHandler
//
// Creates a new key handler and prepares to listen to key events on the given
// event receiver (either a document or an content node). If the receiver is content,
// then extra work needs to be done to hook it up to the document (XXX WHY??)
//
2000-09-22 05:02:20 +00:00
NS_IMETHODIMP
nsXBLService::AttachGlobalKeyHandler(nsIDOMEventReceiver* aReceiver)
{
// check if the receiver is a content node (not a document), and hook
// it to the document if that is the case.
2000-09-22 05:02:20 +00:00
nsCOMPtr<nsIDOMEventReceiver> rec = aReceiver;
nsCOMPtr<nsIContent> contentNode(do_QueryInterface(aReceiver));
if (contentNode) {
// Only attach if we're really in a document
nsCOMPtr<nsIDocument> doc = contentNode->GetCurrentDoc();
2000-09-22 05:02:20 +00:00
if (doc)
rec = do_QueryInterface(doc); // We're a XUL keyset. Attach to our document.
}
2000-09-22 05:02:20 +00:00
if (!rec)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMElement> elt(do_QueryInterface(contentNode));
2000-09-22 05:02:20 +00:00
// Create the key handler
2000-09-22 05:02:20 +00:00
nsXBLWindowKeyHandler* handler;
NS_NewXBLWindowKeyHandler(elt, rec, &handler); // This addRef's
2000-09-22 05:02:20 +00:00
if (!handler)
return NS_ERROR_FAILURE;
// listen to these events
nsCOMPtr<nsIDOMEventGroup> systemGroup;
rec->GetSystemEventGroup(getter_AddRefs(systemGroup));
nsCOMPtr<nsIDOM3EventTarget> target = do_QueryInterface(rec);
target->AddGroupedEventListener(NS_LITERAL_STRING("keydown"), handler,
PR_FALSE, systemGroup);
target->AddGroupedEventListener(NS_LITERAL_STRING("keyup"), handler,
PR_FALSE, systemGroup);
target->AddGroupedEventListener(NS_LITERAL_STRING("keypress"), handler,
PR_FALSE, systemGroup);
2000-09-22 05:02:20 +00:00
// Release. Do this so that only the event receiver holds onto the key handler.
NS_RELEASE(handler);
return NS_OK;
}
//
// AttachGlobalDragDropHandler
//
// Creates a new drag handler and prepares to listen to dragNdrop events on the given
// event receiver.
//
NS_IMETHODIMP
nsXBLService::AttachGlobalDragHandler(nsIDOMEventReceiver* aReceiver)
{
// Create the DnD handler
nsXBLWindowDragHandler* handler;
NS_NewXBLWindowDragHandler(aReceiver, &handler);
if (!handler)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMEventGroup> systemGroup;
aReceiver->GetSystemEventGroup(getter_AddRefs(systemGroup));
nsCOMPtr<nsIDOM3EventTarget> target = do_QueryInterface(aReceiver);
// listen to these events
target->AddGroupedEventListener(NS_LITERAL_STRING("draggesture"), handler,
PR_FALSE, systemGroup);
target->AddGroupedEventListener(NS_LITERAL_STRING("dragenter"), handler,
PR_FALSE, systemGroup);
target->AddGroupedEventListener(NS_LITERAL_STRING("dragexit"), handler,
PR_FALSE, systemGroup);
target->AddGroupedEventListener(NS_LITERAL_STRING("dragover"), handler,
PR_FALSE, systemGroup);
target->AddGroupedEventListener(NS_LITERAL_STRING("dragdrop"), handler,
PR_FALSE, systemGroup);
// Release. Do this so that only the event receiver holds onto the handler.
NS_RELEASE(handler);
return NS_OK;
} // AttachGlobalDragDropHandler
NS_IMETHODIMP
nsXBLService::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aSomeData)
{
if (nsCRT::strcmp(aTopic, "memory-pressure") == 0)
FlushMemory();
return NS_OK;
}
nsresult
nsXBLService::FlushMemory()
{
while (!JS_CLIST_IS_EMPTY(&gClassLRUList)) {
JSCList* lru = gClassLRUList.next;
nsXBLJSClass* c = NS_STATIC_CAST(nsXBLJSClass*, lru);
JS_REMOVE_AND_INIT_LINK(lru);
delete c;
gClassLRUListLength--;
}
return NS_OK;
}
// Internal helper methods ////////////////////////////////////////////////////////////////
NS_IMETHODIMP nsXBLService::BindingReady(nsIContent* aBoundElement,
nsIURI* aURI,
PRBool* aIsReady)
{
return GetBinding(aBoundElement, aURI, PR_TRUE, aIsReady, nsnull);
}
nsresult
nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
PRBool aPeekOnly, PRBool* aIsReady,
nsXBLBinding** aResult)
{
if (aResult)
*aResult = nsnull;
2000-01-13 02:23:54 +00:00
if (!aURI)
2000-01-13 02:23:54 +00:00
return NS_ERROR_FAILURE;
nsCOMPtr<nsIURL> url(do_QueryInterface(aURI));
if (!url) {
#ifdef DEBUG
NS_ERROR("Binding load from a non-URL URI not allowed.");
nsCAutoString spec;
aURI->GetSpec(spec);
fprintf(stderr, "Spec of non-URL URI is: '%s'\n", spec.get());
#endif
return NS_ERROR_FAILURE;
}
nsCAutoString ref;
url->GetRef(ref);
NS_ASSERTION(!ref.IsEmpty(), "Incorrect syntax for an XBL binding");
if (ref.IsEmpty())
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDocument> boundDocument = aBoundElement->GetOwnerDoc();
nsCOMPtr<nsIXBLDocumentInfo> docInfo;
LoadBindingDocumentInfo(aBoundElement, boundDocument, aURI, PR_FALSE,
getter_AddRefs(docInfo));
if (!docInfo)
2000-01-13 02:23:54 +00:00
return NS_ERROR_FAILURE;
// Get our doc info and determine our script access.
nsCOMPtr<nsIDocument> doc;
docInfo->GetDocument(getter_AddRefs(doc));
PRBool allowScripts;
docInfo->GetScriptAccess(&allowScripts);
nsXBLPrototypeBinding* protoBinding;
docInfo->GetPrototypeBinding(ref, &protoBinding);
2000-09-27 20:23:49 +00:00
NS_ASSERTION(protoBinding, "Unable to locate an XBL binding.");
if (!protoBinding)
return NS_ERROR_FAILURE;
2000-10-16 21:46:59 +00:00
nsCOMPtr<nsIContent> child = protoBinding->GetBindingElement();
// Our prototype binding must have all its resources loaded.
PRBool ready = protoBinding->LoadResources();
if (!ready) {
// Add our bound element to the protos list of elts that should
// be notified when the stylesheets and scripts finish loading.
protoBinding->AddResourceListener(aBoundElement);
return NS_ERROR_FAILURE; // The binding isn't ready yet.
}
2000-09-27 20:23:49 +00:00
// If our prototype already has a base, then don't check for an "extends" attribute.
nsRefPtr<nsXBLBinding> baseBinding;
PRBool hasBase = protoBinding->HasBasePrototype();
nsXBLPrototypeBinding* baseProto = protoBinding->GetBasePrototype();
2000-09-27 20:23:49 +00:00
if (baseProto) {
if (NS_FAILED(GetBinding(aBoundElement, baseProto->BindingURI(),
aPeekOnly, aIsReady, getter_AddRefs(baseBinding))))
2000-09-27 20:23:49 +00:00
return NS_ERROR_FAILURE; // We aren't ready yet.
}
else if (hasBase) {
// Check for the presence of 'extends' and 'display' attributes
nsAutoString display, extends;
child->GetAttr(kNameSpaceID_None, nsXBLAtoms::display, display);
child->GetAttr(kNameSpaceID_None, nsXBLAtoms::extends, extends);
PRBool hasDisplay = !display.IsEmpty();
PRBool hasExtends = !extends.IsEmpty();
2000-09-27 20:23:49 +00:00
nsAutoString value(extends);
if (!hasExtends)
2000-09-27 20:23:49 +00:00
protoBinding->SetHasBasePrototype(PR_FALSE);
else {
// Now slice 'em up to see what we've got.
2000-09-27 20:23:49 +00:00
nsAutoString prefix;
PRInt32 offset;
if (hasDisplay) {
offset = display.FindChar(':');
if (-1 != offset) {
display.Left(prefix, offset);
display.Cut(0, offset+1);
}
2000-09-27 20:23:49 +00:00
}
else if (hasExtends) {
offset = extends.FindChar(':');
if (-1 != offset) {
extends.Left(prefix, offset);
extends.Cut(0, offset+1);
display = extends;
}
}
nsAutoString nameSpace;
if (!prefix.IsEmpty()) {
nsCOMPtr<nsIAtom> prefixAtom = do_GetAtom(prefix);
nsCOMPtr<nsIDOM3Node> node(do_QueryInterface(child));
if (node) {
node->LookupNamespaceURI(prefix, nameSpace);
if (!nameSpace.IsEmpty()) {
if (!hasDisplay) {
// We extend some widget/frame. We don't really have a
// base binding.
protoBinding->SetHasBasePrototype(PR_FALSE);
//child->UnsetAttr(kNameSpaceID_None, nsXBLAtoms::extends, PR_FALSE);
2000-09-27 20:23:49 +00:00
}
PRInt32 nameSpaceID;
nsContentUtils::GetNSManagerWeakRef()->GetNameSpaceID(nameSpace,
&nameSpaceID);
nsCOMPtr<nsIAtom> tagName = do_GetAtom(display);
protoBinding->SetBaseTag(nameSpaceID, tagName);
}
2000-03-11 10:36:39 +00:00
}
}
if (hasExtends && (hasDisplay || nameSpace.IsEmpty())) {
// Look up the prefix.
// We have a base class binding. Load it right now.
nsCOMPtr<nsIURI> bindingURI;
nsresult rv =
NS_NewURI(getter_AddRefs(bindingURI), value,
doc->GetDocumentCharacterSet().get(),
doc->GetBaseURI());
NS_ENSURE_SUCCESS(rv, rv);
if (NS_FAILED(GetBinding(aBoundElement, bindingURI, aPeekOnly,
aIsReady, getter_AddRefs(baseBinding))))
return NS_ERROR_FAILURE; // Binding not yet ready or an error occurred.
if (!aPeekOnly) {
// Make sure to set the base prototype.
baseProto = baseBinding->PrototypeBinding();
protoBinding->SetBasePrototype(baseProto);
child->UnsetAttr(kNameSpaceID_None, nsXBLAtoms::extends, PR_FALSE);
child->UnsetAttr(kNameSpaceID_None, nsXBLAtoms::display, PR_FALSE);
}
}
2000-01-13 02:23:54 +00:00
}
}
2000-09-27 20:23:49 +00:00
*aIsReady = PR_TRUE;
if (!aPeekOnly) {
// Make a new binding
nsXBLBinding *newBinding = new nsXBLBinding(protoBinding);
NS_ENSURE_TRUE(newBinding, NS_ERROR_OUT_OF_MEMORY);
2000-09-27 20:23:49 +00:00
if (baseBinding)
newBinding->SetBaseBinding(baseBinding);
NS_ADDREF(*aResult = newBinding);
2000-09-27 20:23:49 +00:00
}
2000-01-13 02:23:54 +00:00
return NS_OK;
}
NS_IMETHODIMP
nsXBLService::LoadBindingDocumentInfo(nsIContent* aBoundElement,
nsIDocument* aBoundDocument,
nsIURI* aBindingURI,
PRBool aForceSyncLoad,
nsIXBLDocumentInfo** aResult)
2000-01-13 02:23:54 +00:00
{
NS_PRECONDITION(aBindingURI, "Must have a binding URI");
nsresult rv = NS_OK;
2000-01-13 02:23:54 +00:00
*aResult = nsnull;
nsCOMPtr<nsIXBLDocumentInfo> info;
nsCOMPtr<nsIURI> uriClone;
rv = aBindingURI->Clone(getter_AddRefs(uriClone));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURL> documentURI(do_QueryInterface(uriClone, &rv));
NS_ENSURE_TRUE(documentURI, rv);
documentURI->SetRef(EmptyCString());
#ifdef MOZ_XUL
2000-08-05 22:33:29 +00:00
// We've got a file. Check our XBL document cache.
PRBool useXULCache;
gXULCache->GetEnabled(&useXULCache);
if (useXULCache) {
2000-08-05 22:33:29 +00:00
// The first line of defense is the chrome cache.
// This cache crosses the entire product, so that any XBL bindings that are
// part of chrome will be reused across all XUL documents.
gXULCache->GetXBLDocumentInfo(documentURI, getter_AddRefs(info));
2000-08-05 22:33:29 +00:00
}
#endif
2000-08-05 22:33:29 +00:00
if (!info) {
2000-08-05 22:33:29 +00:00
// The second line of defense is the binding manager's document table.
nsIBindingManager *bindingManager = nsnull;
2000-09-22 05:02:20 +00:00
nsCOMPtr<nsIURL> bindingURL(do_QueryInterface(aBindingURI, &rv));
NS_ENSURE_SUCCESS(rv, rv);
2000-09-22 05:02:20 +00:00
if (aBoundDocument) {
bindingManager = aBoundDocument->BindingManager();
bindingManager->GetXBLDocumentInfo(documentURI, getter_AddRefs(info));
2000-09-22 05:02:20 +00:00
}
2000-08-21 22:30:36 +00:00
nsINodeInfo *ni = nsnull;
if (aBoundElement)
ni = aBoundElement->GetNodeInfo();
if (!info && bindingManager &&
(!ni || !(ni->Equals(nsXULAtoms::scrollbar, kNameSpaceID_XUL) ||
ni->Equals(nsXULAtoms::thumb, kNameSpaceID_XUL) ||
((ni->Equals(nsHTMLAtoms::input) ||
ni->Equals(nsHTMLAtoms::select)) &&
aBoundElement->IsContentOfType(nsIContent::eHTML)))) &&
!aForceSyncLoad) {
2000-08-06 04:57:55 +00:00
// 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.
2000-08-06 05:03:36 +00:00
nsCOMPtr<nsIStreamListener> listener;
2000-09-22 05:02:20 +00:00
if (bindingManager)
bindingManager->GetLoadingDocListener(documentURI, getter_AddRefs(listener));
2000-08-06 04:57:55 +00:00
if (listener) {
nsIStreamListener* ilist = listener.get();
nsXBLStreamListener* xblListener = NS_STATIC_CAST(nsXBLStreamListener*, ilist);
2000-08-06 04:57:55 +00:00
// Create a new load observer.
if (!xblListener->HasRequest(aBindingURI, aBoundElement)) {
nsXBLBindingRequest* req = nsXBLBindingRequest::Create(mPool, bindingURL, aBoundElement);
xblListener->AddRequest(req);
}
return NS_OK;
2000-08-05 22:33:29 +00:00
}
2000-08-06 04:57:55 +00:00
}
if (!info) {
2000-08-06 04:57:55 +00:00
// Finally, if all lines of defense fail, we go and fetch the binding
// document.
// Always load chrome synchronously
PRBool chrome;
if (NS_SUCCEEDED(documentURI->SchemeIs("chrome", &chrome)) && chrome)
aForceSyncLoad = PR_TRUE;
nsCOMPtr<nsIDocument> document;
FetchBindingDocument(aBoundElement, aBoundDocument, documentURI,
bindingURL, aForceSyncLoad, getter_AddRefs(document));
2000-08-06 04:57:55 +00:00
if (document) {
nsIBindingManager *xblDocBindingManager = document->BindingManager();
xblDocBindingManager->GetXBLDocumentInfo(documentURI, getter_AddRefs(info));
if (!info) {
NS_ERROR("An XBL file is malformed. Did you forget the XBL namespace on the bindings tag?");
return NS_ERROR_FAILURE;
}
xblDocBindingManager->RemoveXBLDocumentInfo(info); // Break the self-imposed cycle.
2000-08-06 04:57:55 +00:00
// If the doc is a chrome URI, then we put it into the XUL cache.
#ifdef MOZ_XUL
if (IsChromeOrResourceURI(documentURI)) {
if (useXULCache)
2000-10-10 20:52:25 +00:00
gXULCache->PutXBLDocumentInfo(info);
2000-08-06 04:57:55 +00:00
}
#endif
2000-08-06 04:57:55 +00:00
if (bindingManager) {
// Also put it in our binding manager's document table.
bindingManager->PutXBLDocumentInfo(info);
2000-08-06 04:57:55 +00:00
}
}
}
}
if (!info)
2000-08-06 04:57:55 +00:00
return NS_OK;
*aResult = info;
NS_IF_ADDREF(*aResult);
return NS_OK;
}
nsresult
2000-08-21 22:30:36 +00:00
nsXBLService::FetchBindingDocument(nsIContent* aBoundElement, nsIDocument* aBoundDocument,
nsIURI* aDocumentURI, nsIURL* aBindingURL,
PRBool aForceSyncLoad, nsIDocument** aResult)
{
nsresult rv = NS_OK;
// Initialize our out pointer to nsnull
*aResult = nsnull;
// Now we have to synchronously load the binding file.
2000-01-12 10:55:56 +00:00
// Create an XML content sink and a parser.
nsCOMPtr<nsILoadGroup> loadGroup;
2000-09-22 05:02:20 +00:00
if (aBoundDocument)
loadGroup = aBoundDocument->GetDocumentLoadGroup();
nsINodeInfo *ni = nsnull;
2000-08-21 22:30:36 +00:00
if (aBoundElement)
ni = aBoundElement->GetNodeInfo();
if (ni && (ni->Equals(nsXULAtoms::scrollbar, kNameSpaceID_XUL) ||
ni->Equals(nsXULAtoms::thumb, kNameSpaceID_XUL) ||
(ni->Equals(nsHTMLAtoms::select) &&
aBoundElement->IsContentOfType(nsIContent::eHTML))) ||
IsResourceURI(aDocumentURI))
aForceSyncLoad = PR_TRUE;
if(!aForceSyncLoad) {
// Create the XML document
nsCOMPtr<nsIDocument> doc = do_CreateInstance(kXMLDocumentCID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannel(getter_AddRefs(channel), aDocumentURI, nsnull, loadGroup);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIStreamListener> listener;
nsCOMPtr<nsIXMLContentSink> xblSink;
NS_NewXBLContentSink(getter_AddRefs(xblSink), doc, aDocumentURI, nsnull);
if (!xblSink)
return NS_ERROR_FAILURE;
if (NS_FAILED(rv = doc->StartDocumentLoad("loadAsInteractiveData",
channel,
loadGroup,
nsnull,
getter_AddRefs(listener),
PR_TRUE,
xblSink))) {
NS_ERROR("Failure to init XBL doc prior to load.");
return rv;
}
// We can be asynchronous
nsXBLStreamListener* xblListener = new nsXBLStreamListener(this, listener, aBoundDocument, doc);
NS_ENSURE_TRUE(xblListener,NS_ERROR_OUT_OF_MEMORY);
nsCOMPtr<nsIDOMEventReceiver> rec(do_QueryInterface(doc));
rec->AddEventListener(NS_LITERAL_STRING("load"), (nsIDOMLoadListener*)xblListener, PR_FALSE);
2000-08-06 04:57:55 +00:00
// Add ourselves to the list of loading docs.
nsIBindingManager *bindingManager;
if (aBoundDocument)
bindingManager = aBoundDocument->BindingManager();
else
bindingManager = nsnull;
if (bindingManager)
bindingManager->PutLoadingDocListener(aDocumentURI, xblListener);
2000-08-06 04:57:55 +00:00
// Add our request.
nsXBLBindingRequest* req = nsXBLBindingRequest::Create(mPool,
aBindingURL,
aBoundElement);
2000-08-06 04:57:55 +00:00
xblListener->AddRequest(req);
// Now kick off the async read.
channel->AsyncOpen(xblListener, nsnull);
2000-08-04 08:45:29 +00:00
return NS_OK;
}
// Now do a blocking synchronous parse of the file.
nsCOMPtr<nsIDOMDocument> domDoc;
nsCOMPtr<nsISyncLoadDOMService> loader =
do_GetService("@mozilla.org/content/syncload-dom-service;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
// Open channel
nsCOMPtr<nsIChannel> channel;
rv = NS_NewChannel(getter_AddRefs(channel), aDocumentURI, nsnull, loadGroup);
NS_ENSURE_SUCCESS(rv, rv);
rv = loader->LoadLocalXBLDocument(channel, getter_AddRefs(domDoc));
if (rv == NS_ERROR_FILE_NOT_FOUND) {
return NS_OK;
}
NS_ENSURE_SUCCESS(rv, rv);
return CallQueryInterface(domDoc, aResult);
2000-01-12 09:32:29 +00:00
}
// Creation Routine ///////////////////////////////////////////////////////////////////////
nsresult NS_NewXBLService(nsIXBLService** aResult);
nsresult
NS_NewXBLService(nsIXBLService** aResult)
{
nsXBLService* result = new nsXBLService;
if (! result)
return NS_ERROR_OUT_OF_MEMORY;
NS_ADDREF(*aResult = result);
// Register the first (and only) nsXBLService as a memory pressure observer
// so it can flush the LRU list in low-memory situations.
nsCOMPtr<nsIObserverService> os = do_GetService("@mozilla.org/observer-service;1");
if (os)
os->AddObserver(result, "memory-pressure", PR_TRUE);
return NS_OK;
}