2000-03-21 13:14:34 +00:00
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* Software distributed under the License is distributed on an " AS
* IS " basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied . See the License for the specific language governing
* rights and limitations under the License .
*
* The Original Code is Mozilla Communicator client code .
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation . Portions created by Netscape are
* Copyright ( C ) 1998 Netscape Communications Corporation . All
* Rights Reserved .
*
* Original Author : David W . Hyatt ( hyatt @ netscape . com )
*
2000-07-01 02:36:18 +00:00
* Contributor ( s ) : Brendan Eich ( brendan @ mozilla . org )
2000-03-21 13:14:34 +00:00
*/
2000-01-12 09:25:28 +00:00
# include "nsCOMPtr.h"
2000-05-28 04:10:50 +00:00
# include "nsXBLService.h"
2000-01-12 09:44:18 +00:00
# include "nsIInputStream.h"
2000-08-14 04:04:18 +00:00
# include "nsINameSpace.h"
2000-01-12 09:44:18 +00:00
# include "nsINameSpaceManager.h"
# include "nsHashtable.h"
# include "nsIURI.h"
# include "nsIURL.h"
# include "nsIChannel.h"
2000-08-04 08:45:29 +00:00
# include "nsIHTTPChannel.h"
2000-01-12 09:44:18 +00:00
# include "nsXPIDLString.h"
# include "nsIParser.h"
# include "nsParserCIID.h"
# include "nsNetUtil.h"
# include "plstr.h"
2000-01-12 09:57:45 +00:00
# include "nsIContent.h"
2000-08-14 04:04:18 +00:00
# include "nsIXMLContent.h"
2000-01-15 05:31:45 +00:00
# include "nsIDOMElement.h"
2000-01-12 09:57:45 +00:00
# include "nsIDocument.h"
2000-01-12 10:20:11 +00:00
# include "nsIXMLContentSink.h"
2000-01-12 10:09:31 +00:00
# include "nsLayoutCID.h"
2000-01-12 10:55:56 +00:00
# include "nsXMLDocument.h"
2000-01-13 02:23:54 +00:00
# include "nsHTMLAtoms.h"
2000-01-13 09:43:42 +00:00
# include "nsSupportsArray.h"
2000-01-25 06:35:27 +00:00
# include "nsITextContent.h"
2000-01-13 02:23:54 +00:00
# include "nsIXBLBinding.h"
2000-08-12 06:28:02 +00:00
# include "nsIXBLDocumentInfo.h"
2000-05-19 04:48:43 +00:00
# include "nsIChromeRegistry.h"
2000-05-27 08:20:04 +00:00
# include "nsIPref.h"
2000-01-12 10:09:31 +00:00
2000-08-14 04:04:18 +00:00
# include "nsIPresShell.h"
# include "nsIDocumentObserver.h"
2000-08-05 22:33:29 +00:00
# include "nsIXULContentUtils.h"
# include "nsIXULPrototypeCache.h"
2000-08-12 06:28:02 +00:00
# include "nsIDOMLoadListener.h"
2000-08-05 22:33:29 +00:00
2000-01-12 10:09:31 +00:00
// Static IIDs/CIDs. Try to minimize these.
static NS_DEFINE_CID ( kNameSpaceManagerCID , NS_NAMESPACEMANAGER_CID ) ;
2000-01-12 10:20:11 +00:00
static NS_DEFINE_CID ( kXMLDocumentCID , NS_XMLDOCUMENT_CID ) ;
static NS_DEFINE_CID ( kParserCID , NS_PARSER_IID ) ; // XXX What's up with this???
2000-05-19 04:48:43 +00:00
static NS_DEFINE_CID ( kChromeRegistryCID , NS_CHROMEREGISTRY_CID ) ;
2000-01-12 09:44:18 +00:00
2000-08-12 06:28:02 +00:00
static PRBool IsChromeURI ( nsIURI * aURI )
{
nsresult rv ;
nsXPIDLCString protocol ;
rv = aURI - > GetScheme ( getter_Copies ( protocol ) ) ;
if ( NS_SUCCEEDED ( rv ) ) {
if ( ! PL_strcmp ( protocol , " chrome " ) ) {
return PR_TRUE ;
}
}
return PR_FALSE ;
}
2000-08-05 22:33:29 +00:00
// Individual binding requests.
struct nsXBLBindingRequest
{
2000-08-06 04:57:55 +00:00
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 ;
}
}
2000-08-14 04:04:18 +00:00
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 ) ;
}
2000-08-06 04:57:55 +00:00
void DocumentLoaded ( nsIDocument * aBindingDoc )
{
nsCOMPtr < nsIDocument > doc ;
mBoundElement - > GetDocument ( * getter_AddRefs ( doc ) ) ;
if ( ! doc )
return ;
2000-08-14 04:04:18 +00:00
// Get the binding.
PRBool ready = PR_FALSE ;
gXBLService - > BindingReady ( mBoundElement , mBindingURL , & ready ) ;
2000-08-06 04:57:55 +00:00
2000-08-14 04:04:18 +00:00
if ( ! ready )
return ;
2000-08-06 04:57:55 +00:00
2000-08-14 04:04:18 +00:00
// XXX Deal with layered bindings.
2000-08-06 04:57:55 +00:00
// 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 ) ;
2000-08-14 04:04:18 +00:00
nsCOMPtr < nsIPresShell > shell = getter_AddRefs ( doc - > GetShellAt ( 0 ) ) ;
if ( shell ) {
nsCOMPtr < nsIDocumentObserver > obs ( do_QueryInterface ( shell ) ) ;
obs - > ContentRemoved ( doc , parent , mBoundElement , index ) ;
obs - > ContentInserted ( doc , parent , mBoundElement , index ) ;
}
2000-08-06 04:57:55 +00:00
}
static nsIXBLService * gXBLService ;
static int gRefCnt ;
2000-08-05 22:33:29 +00:00
} ;
2000-08-14 04:04:18 +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 */
2000-08-12 06:28:02 +00:00
class nsXBLStreamListener : public nsIStreamListener , public nsIDOMLoadListener
2000-08-04 08:45:29 +00:00
{
public :
NS_DECL_ISUPPORTS
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSISTREAMOBSERVER
2000-08-12 06:28:02 +00:00
nsresult Load ( nsIDOMEvent * aEvent ) ;
nsresult Unload ( nsIDOMEvent * aEvent ) { return NS_OK ; } ;
nsresult Abort ( nsIDOMEvent * aEvent ) { return NS_OK ; } ;
nsresult Error ( nsIDOMEvent * aEvent ) { return NS_OK ; } ;
nsresult HandleEvent ( nsIDOMEvent * aEvent ) { return NS_OK ; } ;
static nsIXULPrototypeCache * gXULCache ;
static nsIXULContentUtils * gXULUtils ;
static PRInt32 gRefCnt ;
2000-08-06 04:57:55 +00:00
nsXBLStreamListener ( 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 ) ; } ;
2000-08-14 04:04:18 +00:00
PRBool HasRequest ( const nsCString & aURI , nsIContent * aBoundElement ) ;
2000-08-05 22:33:29 +00:00
2000-08-06 04:57:55 +00:00
private :
nsCOMPtr < nsIStreamListener > mInner ;
2000-08-05 22:33:29 +00:00
nsVoidArray mBindingRequests ;
2000-08-06 04:57:55 +00:00
2000-08-14 04:04:18 +00:00
nsIDocument * mDocument ;
2000-08-06 04:57:55 +00:00
nsCOMPtr < nsIDocument > mBindingDocument ;
2000-08-04 08:45:29 +00:00
} ;
2000-08-12 06:28:02 +00:00
nsIXULPrototypeCache * nsXBLStreamListener : : gXULCache = nsnull ;
nsIXULContentUtils * nsXBLStreamListener : : gXULUtils = nsnull ;
PRInt32 nsXBLStreamListener : : gRefCnt = 0 ;
2000-08-05 22:33:29 +00:00
2000-08-04 08:45:29 +00:00
/* Implementation file */
2000-08-12 06:28:02 +00:00
NS_IMPL_ISUPPORTS4 ( nsXBLStreamListener , nsIStreamListener , nsIStreamObserver , nsIDOMLoadListener , nsIDOMEventListener )
2000-08-04 08:45:29 +00:00
2000-08-06 04:57:55 +00:00
nsXBLStreamListener : : nsXBLStreamListener ( nsIStreamListener * aInner , nsIDocument * aDocument ,
nsIDocument * aBindingDocument )
2000-08-04 08:45:29 +00:00
{
NS_INIT_ISUPPORTS ( ) ;
/* member initializers and constructor code */
mInner = aInner ;
2000-08-06 04:57:55 +00:00
mDocument = aDocument ;
mBindingDocument = aBindingDocument ;
2000-08-12 06:28:02 +00:00
gRefCnt + + ;
if ( gRefCnt = = 1 ) {
nsresult rv = nsServiceManager : : GetService ( " component://netscape/rdf/xul-content-utils " ,
NS_GET_IID ( nsIXULContentUtils ) ,
( nsISupports * * ) & gXULUtils ) ;
if ( NS_FAILED ( rv ) ) return ;
rv = nsServiceManager : : GetService ( " component://netscape/rdf/xul-prototype-cache " ,
NS_GET_IID ( nsIXULPrototypeCache ) ,
( nsISupports * * ) & gXULCache ) ;
if ( NS_FAILED ( rv ) ) return ;
}
2000-08-04 08:45:29 +00:00
}
nsXBLStreamListener : : ~ nsXBLStreamListener ( )
{
/* destructor code */
2000-08-12 06:28:02 +00:00
gRefCnt - - ;
if ( gRefCnt = = 0 ) {
if ( gXULUtils ) {
nsServiceManager : : ReleaseService ( " component://netscape/rdf/xul-content-utils " , gXULUtils ) ;
gXULUtils = nsnull ;
}
if ( gXULCache ) {
nsServiceManager : : ReleaseService ( " component://netscape/rdf/xul-prototype-cache " , gXULCache ) ;
gXULCache = nsnull ;
}
}
2000-08-04 08:45:29 +00:00
}
/* void onDataAvailable (in nsIChannel channel, in nsISupports ctxt, in nsIInputStream inStr, in unsigned long sourceOffset, in unsigned long count); */
NS_IMETHODIMP
nsXBLStreamListener : : OnDataAvailable ( nsIChannel * aChannel , nsISupports * aCtxt , nsIInputStream * aInStr ,
PRUint32 aSourceOffset , PRUint32 aCount )
{
if ( mInner )
return mInner - > OnDataAvailable ( aChannel , aCtxt , aInStr , aSourceOffset , aCount ) ;
return NS_ERROR_FAILURE ;
}
/* void onStartRequest (in nsIChannel channel, in nsISupports ctxt); */
NS_IMETHODIMP
nsXBLStreamListener : : OnStartRequest ( nsIChannel * aChannel , nsISupports * aCtxt )
{
2000-08-06 04:57:55 +00:00
if ( mInner )
return mInner - > OnStartRequest ( aChannel , aCtxt ) ;
2000-08-04 08:45:29 +00:00
return NS_ERROR_FAILURE ;
}
/* void onStopRequest (in nsIChannel channel, in nsISupports ctxt, in nsresult status, in wstring statusArg); */
NS_IMETHODIMP
nsXBLStreamListener : : OnStopRequest ( nsIChannel * aChannel , nsISupports * aCtxt , nsresult aStatus , const PRUnichar * aStatusArg )
{
2000-08-12 06:28:02 +00:00
nsresult rv = NS_OK ;
2000-08-06 04:57:55 +00:00
if ( mInner ) {
2000-08-12 06:28:02 +00:00
rv = mInner - > OnStopRequest ( aChannel , aCtxt , aStatus , aStatusArg ) ;
}
2000-08-06 04:57:55 +00:00
2000-08-14 04:04:18 +00:00
if ( NS_FAILED ( rv ) | | NS_FAILED ( aStatus ) ) {
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 ) ;
delete req ;
}
2000-08-14 04:04:18 +00:00
mDocument = nsnull ;
mBindingDocument = nsnull ;
2000-08-06 04:57:55 +00:00
}
2000-08-12 06:28:02 +00:00
return rv ;
}
2000-08-14 04:04:18 +00:00
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 ;
}
2000-08-12 06:28:02 +00:00
nsresult
nsXBLStreamListener : : Load ( nsIDOMEvent * aEvent )
{
nsresult rv = NS_OK ;
// 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 ( ) ;
nsCOMPtr < nsIContent > root = getter_AddRefs ( mBindingDocument - > GetRootContent ( ) ) ;
if ( root )
nsXBLService : : StripWhitespaceNodes ( root ) ;
// Put our doc in the doc table.
nsCOMPtr < nsIXBLDocumentInfo > info ;
NS_NewXBLDocumentInfo ( mBindingDocument , getter_AddRefs ( info ) ) ;
// If the doc is a chrome URI, then we put it into the XUL cache.
PRBool cached = PR_FALSE ;
if ( IsChromeURI ( uri ) & & gXULUtils - > UseXULCache ( ) ) {
cached = PR_TRUE ;
gXULCache - > PutXBLDocumentInfo ( info ) ;
// Cache whether or not this chrome XBL can execute scripts.
nsCOMPtr < nsIChromeRegistry > reg ( do_GetService ( kChromeRegistryCID , & rv ) ) ;
if ( NS_SUCCEEDED ( rv ) & & reg ) {
PRBool allow = PR_TRUE ;
reg - > AllowScriptsForSkin ( uri , & allow ) ;
info - > SetScriptAccess ( allow ) ;
}
}
2000-08-14 04:04:18 +00:00
if ( ! cached )
bindingManager - > PutXBLDocumentInfo ( info ) ;
2000-08-12 06:28:02 +00:00
// Notify all pending requests that their bindings are
// ready and can be installed.
PRUint32 i ;
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 ) ;
delete req ;
}
2000-08-14 04:04:18 +00:00
nsCOMPtr < nsIDOMEventReceiver > rec ( do_QueryInterface ( mBindingDocument ) ) ;
2000-08-12 06:28:02 +00:00
rec - > RemoveEventListener ( NS_ConvertASCIItoUCS2 ( " load " ) , ( nsIDOMLoadListener * ) this , PR_FALSE ) ;
2000-08-14 04:04:18 +00:00
mDocument = nsnull ;
mBindingDocument = nsnull ;
2000-08-12 06:28:02 +00:00
return rv ;
2000-08-04 08:45:29 +00:00
}
2000-01-12 09:44:18 +00:00
// nsProxyStream
// A helper class used for synchronous parsing of URLs.
class nsProxyStream : public nsIInputStream
{
private :
const char * mBuffer ;
PRUint32 mSize ;
PRUint32 mIndex ;
public :
nsProxyStream ( void ) : mBuffer ( nsnull )
{
NS_INIT_REFCNT ( ) ;
}
virtual ~ nsProxyStream ( void ) {
}
// nsISupports
NS_DECL_ISUPPORTS
// nsIBaseStream
NS_IMETHOD Close ( void ) {
return NS_OK ;
}
// nsIInputStream
NS_IMETHOD Available ( PRUint32 * aLength ) {
* aLength = mSize - mIndex ;
return NS_OK ;
}
NS_IMETHOD Read ( char * aBuf , PRUint32 aCount , PRUint32 * aReadCount ) {
PRUint32 readCount = 0 ;
while ( mIndex < mSize & & aCount > 0 ) {
* aBuf = mBuffer [ mIndex ] ;
aBuf + + ;
mIndex + + ;
readCount + + ;
aCount - - ;
}
* aReadCount = readCount ;
return NS_OK ;
}
// Implementation
void SetBuffer ( const char * aBuffer , PRUint32 aSize ) {
mBuffer = aBuffer ;
mSize = aSize ;
mIndex = 0 ;
}
} ;
2000-02-02 22:24:56 +00:00
NS_IMPL_ISUPPORTS ( nsProxyStream , NS_GET_IID ( nsIInputStream ) ) ;
2000-01-12 09:44:18 +00:00
// Implementation /////////////////////////////////////////////////////////////////
// Static member variable initialization
PRUint32 nsXBLService : : gRefCnt = 0 ;
2000-08-05 22:33:29 +00:00
nsIXULContentUtils * nsXBLService : : gXULUtils = nsnull ;
nsIXULPrototypeCache * nsXBLService : : gXULCache = nsnull ;
2000-01-12 10:09:31 +00:00
nsINameSpaceManager * nsXBLService : : gNameSpaceManager = nsnull ;
2000-05-28 04:10:50 +00:00
nsHashtable * nsXBLService : : gClassTable = nsnull ;
2000-01-12 10:09:31 +00:00
2000-07-01 02:36:18 +00:00
JSCList nsXBLService : : gClassLRUList = JS_INIT_STATIC_CLIST ( & nsXBLService : : gClassLRUList ) ;
PRUint32 nsXBLService : : gClassLRUListLength = 0 ;
PRUint32 nsXBLService : : gClassLRUListQuota = 64 ;
2000-01-13 02:23:54 +00:00
nsIAtom * nsXBLService : : kExtendsAtom = nsnull ;
2000-08-14 04:04:18 +00:00
nsIAtom * nsXBLService : : kScrollbarAtom = nsnull ;
2000-01-13 02:23:54 +00:00
2000-05-27 08:20:04 +00:00
// 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 10:09:31 +00:00
PRInt32 nsXBLService : : kNameSpaceID_XBL ;
2000-01-12 09:44:18 +00:00
// Implement our nsISupports methods
2000-07-25 01:44:22 +00:00
NS_IMPL_ISUPPORTS2 ( nsXBLService , nsIXBLService , nsIMemoryPressureObserver )
2000-01-12 09:25:28 +00:00
2000-01-12 09:44:18 +00:00
// Constructors/Destructors
nsXBLService : : nsXBLService ( void )
{
NS_INIT_REFCNT ( ) ;
2000-08-14 04:04:18 +00:00
mPool . Init ( " XBL Binding Requests " , kBucketSizes , kNumBuckets , kInitialSize ) ;
2000-01-12 09:44:18 +00:00
gRefCnt + + ;
if ( gRefCnt = = 1 ) {
2000-08-05 22:33:29 +00:00
2000-01-12 10:09:31 +00:00
// Register the XBL namespace.
nsresult rv = nsComponentManager : : CreateInstance ( kNameSpaceManagerCID ,
nsnull ,
2000-02-02 22:24:56 +00:00
NS_GET_IID ( nsINameSpaceManager ) ,
2000-01-12 10:09:31 +00:00
( void * * ) & gNameSpaceManager ) ;
NS_ASSERTION ( NS_SUCCEEDED ( rv ) , " unable to create namespace manager " ) ;
if ( NS_FAILED ( rv ) ) return ;
// XXX This is sure to change. Copied from mozilla/layout/xul/content/src/nsXULAtoms.cpp
static const char kXBLNameSpaceURI [ ]
= " http://www.mozilla.org/xbl " ;
2000-04-16 11:19:26 +00:00
rv = gNameSpaceManager - > RegisterNameSpace ( NS_ConvertASCIItoUCS2 ( kXBLNameSpaceURI ) , kNameSpaceID_XBL ) ;
2000-01-12 10:09:31 +00:00
NS_ASSERTION ( NS_SUCCEEDED ( rv ) , " unable to register XBL namespace " ) ;
if ( NS_FAILED ( rv ) ) return ;
2000-01-13 02:23:54 +00:00
// Create our atoms
kExtendsAtom = NS_NewAtom ( " extends " ) ;
2000-08-14 04:04:18 +00:00
kScrollbarAtom = NS_NewAtom ( " scrollbar " ) ;
2000-05-27 08:20:04 +00:00
// Find out if the XUL cache is on or off
NS_WITH_SERVICE ( nsIPref , prefs , NS_PREF_PROGID , & rv ) ;
if ( NS_SUCCEEDED ( rv ) )
prefs - > GetBoolPref ( kDisableChromeCachePref , & gDisableChromeCache ) ;
2000-05-28 04:10:50 +00:00
gClassTable = new nsHashtable ( ) ;
2000-07-11 01:28:36 +00:00
// Register the first (and only) nsXBLService as a memory pressure observer
// so it can flush the LRU list in low-memory situations.
nsMemory : : RegisterObserver ( this ) ;
2000-08-05 22:33:29 +00:00
rv = nsServiceManager : : GetService ( " component://netscape/rdf/xul-content-utils " ,
NS_GET_IID ( nsIXULContentUtils ) ,
( nsISupports * * ) & gXULUtils ) ;
if ( NS_FAILED ( rv ) ) return ;
rv = nsServiceManager : : GetService ( " component://netscape/rdf/xul-prototype-cache " ,
NS_GET_IID ( nsIXULPrototypeCache ) ,
( nsISupports * * ) & gXULCache ) ;
if ( NS_FAILED ( rv ) ) return ;
2000-01-12 09:44:18 +00:00
}
}
nsXBLService : : ~ nsXBLService ( void )
{
gRefCnt - - ;
2000-01-12 10:09:31 +00:00
if ( gRefCnt = = 0 ) {
NS_IF_RELEASE ( gNameSpaceManager ) ;
2000-01-13 02:23:54 +00:00
// Release our atoms
NS_RELEASE ( kExtendsAtom ) ;
2000-08-14 04:04:18 +00:00
NS_RELEASE ( kScrollbarAtom ) ;
2000-07-01 02:36:18 +00:00
// Walk the LRU list removing and deleting the nsXBLJSClasses.
2000-07-11 01:28:36 +00:00
FlushMemory ( REASON_HEAP_MINIMIZE , 0 ) ;
2000-07-01 02:36:18 +00:00
// 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 ;
2000-07-01 02:36:18 +00:00
gClassTable = nsnull ;
2000-07-11 05:43:34 +00:00
nsMemory : : UnregisterObserver ( this ) ;
2000-08-05 22:33:29 +00:00
if ( gXULUtils ) {
nsServiceManager : : ReleaseService ( " component://netscape/rdf/xul-content-utils " , gXULUtils ) ;
gXULUtils = nsnull ;
}
if ( gXULCache ) {
nsServiceManager : : ReleaseService ( " component://netscape/rdf/xul-prototype-cache " , gXULCache ) ;
gXULCache = nsnull ;
}
2000-01-12 10:09:31 +00:00
}
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
2000-07-28 00:35:02 +00:00
nsXBLService : : LoadBindings ( nsIContent * aContent , const nsString & aURL , PRBool aAugmentFlag ,
nsIXBLBinding * * aBinding )
2000-01-12 09:32:29 +00:00
{
2000-07-28 00:35:02 +00:00
* aBinding = nsnull ;
2000-01-12 09:57:45 +00:00
nsresult rv ;
2000-01-13 02:23:54 +00:00
2000-05-09 21:42:40 +00:00
nsCOMPtr < nsIDocument > document ;
aContent - > GetDocument ( * getter_AddRefs ( document ) ) ;
nsCOMPtr < nsIBindingManager > bindingManager ;
document - > GetBindingManager ( getter_AddRefs ( bindingManager ) ) ;
2000-01-13 02:23:54 +00:00
nsCOMPtr < nsIXBLBinding > binding ;
2000-05-09 21:42:40 +00:00
bindingManager - > GetBinding ( aContent , getter_AddRefs ( binding ) ) ;
2000-06-02 08:13:29 +00:00
if ( binding & & ! aAugmentFlag ) {
nsCOMPtr < nsIXBLBinding > styleBinding ;
binding - > GetFirstStyleBinding ( getter_AddRefs ( styleBinding ) ) ;
if ( styleBinding ) {
2000-08-14 04:04:18 +00:00
PRBool marked = PR_FALSE ;
binding - > MarkedForDeath ( & marked ) ;
if ( marked ) {
2000-07-28 02:22:59 +00:00
FlushStyleBindings ( aContent ) ;
binding = nsnull ;
}
2000-08-14 04:04:18 +00:00
else {
// See if the URIs match.
nsCAutoString uri ;
styleBinding - > GetBindingURI ( uri ) ;
if ( uri . EqualsWithConversion ( aURL ) )
return NS_OK ;
else {
FlushStyleBindings ( aContent ) ;
binding = nsnull ;
}
}
2000-06-02 08:13:29 +00:00
}
2000-04-27 02:08:35 +00:00
}
2000-01-13 08:54:16 +00:00
2000-06-02 08:13:29 +00:00
nsCOMPtr < nsIXBLBinding > newBinding ;
2000-04-26 01:13:55 +00:00
nsCAutoString url ; url . AssignWithConversion ( aURL ) ;
2000-08-05 22:33:29 +00:00
if ( NS_FAILED ( rv = GetBinding ( aContent , url , getter_AddRefs ( newBinding ) ) ) ) {
2000-01-12 09:57:45 +00:00
return rv ;
}
2000-01-12 09:44:18 +00:00
2000-06-02 08:13:29 +00:00
if ( ! newBinding ) {
2000-04-19 08:20:20 +00:00
nsCAutoString str = " 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: " ;
2000-04-16 11:19:26 +00:00
str . AppendWithConversion ( aURL ) ;
2000-03-30 03:18:44 +00:00
NS_ERROR ( str ) ;
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 ) {
nsCOMPtr < nsIXBLBinding > baseBinding ;
nsCOMPtr < nsIXBLBinding > nextBinding = newBinding ;
do {
baseBinding = nextBinding ;
baseBinding - > GetBaseBinding ( getter_AddRefs ( nextBinding ) ) ;
baseBinding - > SetIsStyleBinding ( PR_FALSE ) ;
} while ( nextBinding ) ;
// XXX Handle adjusting the prototype chain! We need to somehow indicate to
// InstallProperties that the whole chain should just be whacked and rebuilt.
// 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.
nsCOMPtr < nsIXBLBinding > rootBinding ;
binding - > GetRootBinding ( getter_AddRefs ( rootBinding ) ) ;
rootBinding - > SetBaseBinding ( newBinding ) ;
}
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
2000-01-13 08:54:16 +00:00
// Tell the binding to build the anonymous content.
2000-06-02 08:13:29 +00:00
newBinding - > GenerateAnonymousContent ( aContent ) ;
2000-01-13 08:54:16 +00:00
// Tell the binding to install event handlers
2000-07-28 00:35:02 +00:00
newBinding - > InstallEventHandlers ( aContent , aBinding ) ;
2000-01-13 08:54:16 +00:00
2000-03-29 01:24:35 +00:00
// Set up our properties
2000-06-02 08:13:29 +00:00
newBinding - > InstallProperties ( aContent ) ;
2000-03-29 01:24:35 +00:00
2000-01-12 09:32:29 +00:00
return NS_OK ;
}
// For a given element, returns a flat list of all the anonymous children that need
// frames built.
NS_IMETHODIMP
2000-05-22 08:23:09 +00:00
nsXBLService : : GetContentList ( nsIContent * aContent , nsISupportsArray * * aResult , nsIContent * * aParent ,
PRBool * aMultipleInsertionPoints )
2000-01-12 09:32:29 +00:00
{
2000-01-13 09:43:42 +00:00
// Iterate over all of the bindings one by one and build up an array
// of anonymous items.
* aResult = nsnull ;
2000-03-14 11:09:46 +00:00
* aParent = nsnull ;
2000-05-22 08:23:09 +00:00
* aMultipleInsertionPoints = PR_FALSE ;
2000-01-13 09:43:42 +00:00
2000-05-09 21:42:40 +00:00
nsCOMPtr < nsIDocument > document ;
aContent - > GetDocument ( * getter_AddRefs ( document ) ) ;
nsCOMPtr < nsIBindingManager > bindingManager ;
2000-05-18 19:58:27 +00:00
NS_ASSERTION ( document , " no document " ) ;
if ( ! document ) return NS_ERROR_FAILURE ;
2000-05-09 21:42:40 +00:00
document - > GetBindingManager ( getter_AddRefs ( bindingManager ) ) ;
2000-01-13 09:43:42 +00:00
nsCOMPtr < nsIXBLBinding > binding ;
2000-05-09 21:42:40 +00:00
bindingManager - > GetBinding ( aContent , getter_AddRefs ( binding ) ) ;
2000-01-13 09:43:42 +00:00
while ( binding ) {
// Get the anonymous content.
nsCOMPtr < nsIContent > content ;
binding - > GetAnonymousContent ( getter_AddRefs ( content ) ) ;
if ( content ) {
PRInt32 childCount ;
content - > ChildCount ( childCount ) ;
for ( PRInt32 i = 0 ; i < childCount ; i + + ) {
nsCOMPtr < nsIContent > anonymousChild ;
content - > ChildAt ( i , * getter_AddRefs ( anonymousChild ) ) ;
if ( ! ( * aResult ) )
NS_NewISupportsArray ( aResult ) ; // This call addrefs the array.
2000-01-15 05:31:45 +00:00
2000-01-25 06:35:27 +00:00
( * aResult ) - > AppendElement ( anonymousChild ) ;
2000-01-13 09:43:42 +00:00
}
2000-03-14 11:09:46 +00:00
2000-05-22 08:23:09 +00:00
binding - > GetSingleInsertionPoint ( aParent , aMultipleInsertionPoints ) ;
2000-03-11 10:36:39 +00:00
return NS_OK ;
2000-01-13 09:43:42 +00:00
}
nsCOMPtr < nsIXBLBinding > nextBinding ;
binding - > GetBaseBinding ( getter_AddRefs ( nextBinding ) ) ;
binding = nextBinding ;
}
2000-01-12 09:57:45 +00:00
return NS_OK ;
}
2000-03-28 00:41:33 +00:00
NS_IMETHODIMP
2000-06-02 08:13:29 +00:00
nsXBLService : : FlushStyleBindings ( nsIContent * aContent )
2000-03-28 00:41:33 +00:00
{
2000-05-09 21:42:40 +00:00
nsCOMPtr < nsIDocument > document ;
aContent - > GetDocument ( * getter_AddRefs ( document ) ) ;
nsCOMPtr < nsIBindingManager > bindingManager ;
document - > GetBindingManager ( getter_AddRefs ( bindingManager ) ) ;
2000-04-03 07:13:07 +00:00
nsCOMPtr < nsIXBLBinding > binding ;
2000-05-09 21:42:40 +00:00
bindingManager - > GetBinding ( aContent , getter_AddRefs ( binding ) ) ;
2000-04-03 07:13:07 +00:00
if ( binding ) {
2000-06-02 08:13:29 +00:00
nsCOMPtr < nsIXBLBinding > styleBinding ;
binding - > GetFirstStyleBinding ( getter_AddRefs ( styleBinding ) ) ;
if ( styleBinding ) {
// Clear out the script references.
nsCOMPtr < nsIDocument > document ;
aContent - > GetDocument ( * getter_AddRefs ( document ) ) ;
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
2000-05-24 08:19:10 +00:00
nsXBLService : : ResolveTag ( nsIContent * aContent , PRInt32 * aNameSpaceID , nsIAtom * * aResult )
2000-03-11 10:36:39 +00:00
{
2000-05-09 21:42:40 +00:00
nsCOMPtr < nsIDocument > document ;
aContent - > GetDocument ( * getter_AddRefs ( document ) ) ;
if ( document ) {
nsCOMPtr < nsIBindingManager > bindingManager ;
document - > GetBindingManager ( getter_AddRefs ( bindingManager ) ) ;
if ( bindingManager )
2000-05-24 08:19:10 +00:00
return bindingManager - > ResolveTag ( aContent , aNameSpaceID , aResult ) ;
2000-05-09 21:42:40 +00:00
}
2000-03-11 10:36:39 +00:00
2000-05-24 08:19:10 +00:00
aContent - > GetNameSpaceID ( * aNameSpaceID ) ;
2000-05-09 21:42:40 +00:00
aContent - > GetTag ( * aResult ) ; // Addref happens here.
return NS_OK ;
2000-03-11 10:36:39 +00:00
}
2000-01-12 09:57:45 +00:00
2000-05-19 04:48:43 +00:00
NS_IMETHODIMP
2000-08-12 06:28:02 +00:00
nsXBLService : : GetXBLDocumentInfo ( const nsCString & aURLStr , nsIContent * aBoundElement , nsIXBLDocumentInfo * * aResult )
2000-05-19 04:48:43 +00:00
{
2000-08-12 06:28:02 +00:00
* aResult = nsnull ;
if ( gXULUtils - > 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 ( aURLStr , aResult ) ;
}
2000-05-19 04:48:43 +00:00
2000-08-12 06:28:02 +00:00
if ( ! * aResult ) {
// The second line of defense is the binding manager's document table.
nsCOMPtr < nsIDocument > boundDocument ;
aBoundElement - > GetDocument ( * getter_AddRefs ( boundDocument ) ) ;
nsCOMPtr < nsIBindingManager > bindingManager ;
boundDocument - > GetBindingManager ( getter_AddRefs ( bindingManager ) ) ;
bindingManager - > GetXBLDocumentInfo ( aURLStr , aResult ) ;
}
2000-05-19 04:48:43 +00:00
return NS_OK ;
}
2000-07-11 01:28:36 +00:00
NS_IMETHODIMP
nsXBLService : : FlushMemory ( PRUint32 reason , size_t requestedAmount )
{
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 - - ;
if ( reason = = REASON_ALLOC_FAILURE ) {
if ( requestedAmount < = sizeof ( nsXBLJSClass ) )
break ;
requestedAmount - = sizeof ( nsXBLJSClass ) ;
}
}
return NS_OK ;
}
2000-01-12 09:57:45 +00:00
// Internal helper methods ////////////////////////////////////////////////////////////////
2000-08-14 04:04:18 +00:00
NS_IMETHODIMP nsXBLService : : GetBinding ( nsIContent * aBoundElement ,
const nsCString & aURLStr ,
nsIXBLBinding * * aResult )
2000-01-12 09:57:45 +00:00
{
2000-08-14 04:04:18 +00:00
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 ;
2000-01-13 02:23:54 +00:00
2000-03-26 10:06:21 +00:00
if ( aURLStr . IsEmpty ( ) )
2000-01-13 02:23:54 +00:00
return NS_ERROR_FAILURE ;
// XXX Obtain the # marker and remove it from the URL.
2000-04-26 00:50:48 +00:00
nsCAutoString uri ( aURLStr ) ;
PRInt32 indx = uri . RFindChar ( ' # ' ) ;
nsCAutoString ref ;
uri . Right ( ref , uri . Length ( ) - ( indx + 1 ) ) ;
uri . Truncate ( indx ) ;
2000-01-13 02:23:54 +00:00
2000-08-12 06:28:02 +00:00
nsCOMPtr < nsIXBLDocumentInfo > docInfo ;
GetBindingDocumentInfo ( aBoundElement , uri , ref , getter_AddRefs ( docInfo ) ) ;
if ( ! docInfo )
2000-01-13 02:23:54 +00:00
return NS_ERROR_FAILURE ;
2000-08-12 06:28:02 +00:00
// Get our doc info and determine our script access.
nsCOMPtr < nsIDocument > doc ;
docInfo - > GetDocument ( getter_AddRefs ( doc ) ) ;
PRBool allowScripts ;
docInfo - > GetScriptAccess ( & allowScripts ) ;
2000-01-13 02:23:54 +00:00
// We have a doc. Obtain our specific binding element.
// Walk the children looking for the binding that matches the ref
// specified in the URL.
nsCOMPtr < nsIContent > root = getter_AddRefs ( doc - > GetRootContent ( ) ) ;
if ( ! root )
return NS_ERROR_FAILURE ;
2000-04-16 11:19:26 +00:00
nsAutoString bindingName ; bindingName . AssignWithConversion ( NS_STATIC_CAST ( const char * , ref ) ) ;
2000-01-13 02:23:54 +00:00
PRInt32 count ;
root - > ChildCount ( count ) ;
2000-05-12 06:25:48 +00:00
2000-01-13 02:23:54 +00:00
for ( PRInt32 i = 0 ; i < count ; i + + ) {
nsCOMPtr < nsIContent > child ;
root - > ChildAt ( i , * getter_AddRefs ( child ) ) ;
nsAutoString value ;
2000-04-19 08:20:20 +00:00
child - > GetAttribute ( kNameSpaceID_None , nsHTMLAtoms : : id , value ) ;
2000-01-13 02:23:54 +00:00
// If no ref is specified just use this.
2000-08-14 04:04:18 +00:00
if ( ( bindingName . IsEmpty ( ) ) | | ( bindingName = = value ) ) {
2000-01-13 02:23:54 +00:00
// Check for the presence of an extends attribute
2000-08-14 04:04:18 +00:00
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.
}
}
}
2000-03-11 10:36:39 +00:00
}
2000-01-12 09:57:45 +00:00
}
2000-01-13 02:23:54 +00:00
2000-08-14 04:04:18 +00:00
* aIsReady = PR_TRUE ;
if ( ! aPeekOnly ) {
// Make a new binding
NS_NewXBLBinding ( uri , ref , aResult ) ;
// Initialize its bound element.
( * aResult ) - > SetBindingElement ( child ) ;
( * aResult ) - > SetAllowScripts ( allowScripts ) ;
if ( baseBinding )
( * aResult ) - > SetBaseBinding ( baseBinding ) ;
}
2000-01-13 02:23:54 +00:00
break ;
}
}
return NS_OK ;
}
NS_IMETHODIMP
2000-08-12 06:28:02 +00:00
nsXBLService : : GetBindingDocumentInfo ( nsIContent * aBoundElement , const nsCString & aURLStr , const nsCString & aRef ,
nsIXBLDocumentInfo * * aResult )
2000-01-13 02:23:54 +00:00
{
2000-05-19 04:48:43 +00:00
nsresult rv ;
2000-01-13 02:23:54 +00:00
* aResult = nsnull ;
2000-08-05 22:33:29 +00:00
// We've got a file. Check our XBL document cache.
2000-08-12 06:28:02 +00:00
nsCOMPtr < nsIXBLDocumentInfo > info ;
2000-08-05 22:33:29 +00:00
if ( gXULUtils - > UseXULCache ( ) ) {
// 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.
2000-08-12 06:28:02 +00:00
gXULCache - > GetXBLDocumentInfo ( aURLStr , getter_AddRefs ( info ) ) ;
2000-08-05 22:33:29 +00:00
}
2000-08-12 06:28:02 +00:00
if ( ! info ) {
2000-08-05 22:33:29 +00:00
// The second line of defense is the binding manager's document table.
nsCOMPtr < nsIDocument > boundDocument ;
aBoundElement - > GetDocument ( * getter_AddRefs ( boundDocument ) ) ;
nsCOMPtr < nsIBindingManager > bindingManager ;
boundDocument - > GetBindingManager ( getter_AddRefs ( bindingManager ) ) ;
2000-08-12 06:28:02 +00:00
bindingManager - > GetXBLDocumentInfo ( aURLStr , getter_AddRefs ( info ) ) ;
2000-08-06 04:57:55 +00:00
2000-08-14 04:04:18 +00:00
nsCOMPtr < nsIAtom > tagName ;
aBoundElement - > GetTag ( * getter_AddRefs ( tagName ) ) ;
if ( ! info & & ( tagName . get ( ) ! = kScrollbarAtom ) ) {
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-08-06 04:57:55 +00:00
bindingManager - > GetLoadingDocListener ( aURLStr , getter_AddRefs ( listener ) ) ;
if ( listener ) {
2000-08-14 04:04:18 +00:00
nsIStreamListener * ilist = listener . get ( ) ;
nsXBLStreamListener * xblListener = NS_STATIC_CAST ( nsXBLStreamListener * , ilist ) ;
2000-08-06 04:57:55 +00:00
// Create a new load observer.
nsCAutoString bindingURI ( aURLStr ) ;
bindingURI + = " # " ;
bindingURI + = aRef ;
2000-08-14 04:04:18 +00:00
if ( ! xblListener - > HasRequest ( bindingURI , aBoundElement ) ) {
nsXBLBindingRequest * req = new ( mPool ) nsXBLBindingRequest ( bindingURI , aBoundElement ) ;
xblListener - > AddRequest ( req ) ;
}
2000-08-12 06:28:02 +00:00
return NS_OK ;
2000-08-05 22:33:29 +00:00
}
2000-08-06 04:57:55 +00:00
}
2000-08-12 06:28:02 +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.
nsCOMPtr < nsIURL > uri ;
nsComponentManager : : CreateInstance ( " component://netscape/network/standard-url " ,
nsnull ,
NS_GET_IID ( nsIURL ) ,
getter_AddRefs ( uri ) ) ;
uri - > SetSpec ( aURLStr ) ;
2000-08-12 06:28:02 +00:00
nsCOMPtr < nsIDocument > document ;
2000-08-06 04:57:55 +00:00
FetchBindingDocument ( aBoundElement , uri , aRef , getter_AddRefs ( document ) ) ;
if ( document ) {
2000-08-12 06:28:02 +00:00
NS_NewXBLDocumentInfo ( document , getter_AddRefs ( info ) ) ;
2000-08-06 04:57:55 +00:00
// If the doc is a chrome URI, then we put it into the XUL cache.
PRBool cached = PR_FALSE ;
2000-08-12 06:28:02 +00:00
if ( IsChromeURI ( uri ) & & gXULUtils - > UseXULCache ( ) ) {
2000-08-06 04:57:55 +00:00
cached = PR_TRUE ;
2000-08-12 06:28:02 +00:00
gXULCache - > PutXBLDocumentInfo ( info ) ;
2000-08-06 04:57:55 +00:00
// Cache whether or not this chrome XBL can execute scripts.
nsCOMPtr < nsIChromeRegistry > reg ( do_GetService ( kChromeRegistryCID , & rv ) ) ;
if ( NS_SUCCEEDED ( rv ) & & reg ) {
2000-08-12 06:28:02 +00:00
PRBool allow = PR_TRUE ;
2000-08-06 04:57:55 +00:00
reg - > AllowScriptsForSkin ( uri , & allow ) ;
2000-08-12 06:28:02 +00:00
info - > SetScriptAccess ( allow ) ;
2000-08-06 04:57:55 +00:00
}
}
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 ) ) ;
2000-08-12 06:28:02 +00:00
bindingManager - > PutXBLDocumentInfo ( info ) ;
2000-08-06 04:57:55 +00:00
}
2000-05-19 04:48:43 +00:00
}
2000-01-12 09:57:45 +00:00
}
}
2000-08-12 06:28:02 +00:00
if ( ! info )
2000-08-06 04:57:55 +00:00
return NS_OK ;
2000-08-12 06:28:02 +00:00
* aResult = info ;
2000-01-12 09:57:45 +00:00
NS_IF_ADDREF ( * aResult ) ;
return NS_OK ;
}
NS_IMETHODIMP
2000-08-06 04:57:55 +00:00
nsXBLService : : FetchBindingDocument ( nsIContent * aBoundElement , nsIURI * aURI , const nsCString & aRef , nsIDocument * * aResult )
2000-01-12 09:57:45 +00:00
{
2000-01-12 10:20:11 +00:00
// Initialize our out pointer to nsnull
2000-01-12 09:57:45 +00:00
* aResult = nsnull ;
2000-01-12 10:20:11 +00:00
// Create the XML document
nsCOMPtr < nsIDocument > doc ;
nsresult rv = nsComponentManager : : CreateInstance ( kXMLDocumentCID , nsnull ,
2000-02-02 22:24:56 +00:00
NS_GET_IID ( nsIDocument ) ,
2000-01-12 10:20:11 +00:00
getter_AddRefs ( doc ) ) ;
if ( NS_FAILED ( rv ) ) return rv ;
2000-01-12 10:55:56 +00:00
// XXX This is evil, but we're living in layout, so I'm
// just going to do it.
nsXMLDocument * xmlDoc = ( nsXMLDocument * ) ( doc . get ( ) ) ;
2000-01-12 10:20:11 +00:00
// 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.
2000-08-12 06:28:02 +00:00
nsCOMPtr < nsIDocument > boundDoc ;
aBoundElement - > GetDocument ( * getter_AddRefs ( boundDoc ) ) ;
nsCOMPtr < nsILoadGroup > loadGroup ;
boundDoc - > GetDocumentLoadGroup ( getter_AddRefs ( loadGroup ) ) ;
2000-01-12 10:20:11 +00:00
nsCOMPtr < nsIChannel > channel ;
2000-08-12 06:28:02 +00:00
rv = NS_OpenURI ( getter_AddRefs ( channel ) , aURI , nsnull , loadGroup ) ;
2000-01-12 10:20:11 +00:00
if ( NS_FAILED ( rv ) ) return rv ;
2000-01-12 10:55:56 +00:00
// Call StartDocumentLoad
nsCOMPtr < nsIStreamListener > listener ;
2000-08-12 06:28:02 +00:00
if ( NS_FAILED ( rv = xmlDoc - > StartDocumentLoad ( " loadAsData " , channel ,
loadGroup , nsnull , getter_AddRefs ( listener ) ) ) ) {
2000-01-12 10:55:56 +00:00
NS_ERROR ( " Failure to init XBL doc prior to load. " ) ;
return rv ;
}
2000-01-12 10:20:11 +00:00
2000-08-14 04:04:18 +00:00
nsCOMPtr < nsIAtom > tagName ;
aBoundElement - > GetTag ( * getter_AddRefs ( tagName ) ) ;
if ( tagName ! = kScrollbarAtom ) {
2000-08-12 06:28:02 +00:00
// We can be asynchronous
2000-08-06 04:57:55 +00:00
nsXBLStreamListener * xblListener = new nsXBLStreamListener ( listener , boundDoc , doc ) ;
2000-08-14 04:04:18 +00:00
2000-08-12 06:28:02 +00:00
nsCOMPtr < nsIDOMEventReceiver > rec ( do_QueryInterface ( doc ) ) ;
rec - > AddEventListener ( NS_ConvertASCIItoUCS2 ( " load " ) , ( nsIDOMLoadListener * ) xblListener , PR_FALSE ) ;
2000-08-06 04:57:55 +00:00
// 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 ;
2000-08-14 04:04:18 +00:00
nsXBLBindingRequest * req = new ( mPool ) nsXBLBindingRequest ( bindingURI , aBoundElement ) ;
2000-08-06 04:57:55 +00:00
xblListener - > AddRequest ( req ) ;
// Now kick off the async read.
2000-08-12 06:28:02 +00:00
channel - > AsyncRead ( xblListener , nsnull ) ;
2000-08-04 08:45:29 +00:00
return NS_OK ;
}
2000-01-12 10:20:11 +00:00
// Now do a blocking synchronous parse of the file.
nsCOMPtr < nsIInputStream > in ;
PRUint32 sourceOffset = 0 ;
2000-03-29 03:58:50 +00:00
rv = channel - > OpenInputStream ( getter_AddRefs ( in ) ) ;
2000-01-12 10:20:11 +00:00
// If we couldn't open the channel, then just return.
if ( NS_FAILED ( rv ) ) return NS_OK ;
NS_ASSERTION ( in ! = nsnull , " no input stream " ) ;
if ( ! in ) return NS_ERROR_FAILURE ;
rv = NS_ERROR_OUT_OF_MEMORY ;
nsProxyStream * proxy = new nsProxyStream ( ) ;
if ( ! proxy )
return NS_ERROR_FAILURE ;
listener - > OnStartRequest ( channel , nsnull ) ;
while ( PR_TRUE ) {
char buf [ 1024 ] ;
PRUint32 readCount ;
if ( NS_FAILED ( rv = in - > Read ( buf , sizeof ( buf ) , & readCount ) ) )
break ; // error
if ( readCount = = 0 )
break ; // eof
proxy - > SetBuffer ( buf , readCount ) ;
rv = listener - > OnDataAvailable ( channel , nsnull , proxy , sourceOffset , readCount ) ;
sourceOffset + = readCount ;
if ( NS_FAILED ( rv ) )
break ;
}
listener - > OnStopRequest ( channel , nsnull , NS_OK , nsnull ) ;
// don't leak proxy!
proxy - > Close ( ) ;
delete proxy ;
// The document is parsed. We now have a prototype document.
// Everything worked, so we can just hand this back now.
* aResult = doc ;
NS_IF_ADDREF ( * aResult ) ;
2000-01-25 06:35:27 +00:00
// The XML content sink produces a ridiculous # of content nodes.
// It generates text nodes even for whitespace. The following
// call walks the generated document tree and trims out these
// nodes.
nsCOMPtr < nsIContent > root = getter_AddRefs ( doc - > GetRootContent ( ) ) ;
if ( root )
StripWhitespaceNodes ( root ) ;
return NS_OK ;
}
2000-08-12 06:28:02 +00:00
nsresult
2000-01-25 06:35:27 +00:00
nsXBLService : : StripWhitespaceNodes ( nsIContent * aElement )
{
PRInt32 childCount ;
aElement - > ChildCount ( childCount ) ;
for ( PRInt32 i = 0 ; i < childCount ; i + + ) {
nsCOMPtr < nsIContent > child ;
aElement - > ChildAt ( i , * getter_AddRefs ( child ) ) ;
nsCOMPtr < nsITextContent > text = do_QueryInterface ( child ) ;
if ( text ) {
nsAutoString result ;
text - > CopyText ( result ) ;
result . StripWhitespace ( ) ;
2000-03-26 10:06:21 +00:00
if ( result . IsEmpty ( ) ) {
2000-01-25 06:35:27 +00:00
// This node contained nothing but whitespace.
// Remove it from the content model.
aElement - > RemoveChildAt ( i , PR_TRUE ) ;
i - - ; // Decrement our count, since we just removed this child.
childCount - - ; // Also decrement our total count.
}
}
else StripWhitespaceNodes ( child ) ;
}
2000-01-12 09:32:29 +00:00
return NS_OK ;
}
// Creation Routine ///////////////////////////////////////////////////////////////////////
2000-01-12 09:25:28 +00:00
nsresult
NS_NewXBLService ( nsIXBLService * * aResult )
{
* aResult = new nsXBLService ;
if ( ! * aResult )
return NS_ERROR_OUT_OF_MEMORY ;
NS_ADDREF ( * aResult ) ;
return NS_OK ;
2000-01-12 21:34:41 +00:00
}