From d100985675e5e683f6c7966b886d8f09e5d1aee2 Mon Sep 17 00:00:00 2001 From: "waterson%netscape.com" Date: Sat, 22 Jan 2000 22:00:35 +0000 Subject: [PATCH] Bugs 18127, 20677, 23905. Partial fix. Give the nsXULPrototypeDocument it's own JSContext to use for compiling scripts and event handlers. Modify script and event handler code to compile shared scripts using this context, if appropriate. r=brendan --- content/events/src/nsEventListenerManager.cpp | 37 ++- content/xul/content/src/nsXULElement.cpp | 204 ++++++++++---- content/xul/content/src/nsXULElement.h | 6 +- content/xul/document/public/nsIXULDocument.h | 5 + .../document/public/nsIXULPrototypeDocument.h | 2 +- content/xul/document/src/nsXULDocument.cpp | 22 +- content/xul/document/src/nsXULDocument.h | 1 + .../document/src/nsXULPrototypeDocument.cpp | 254 +++++++++++++++--- dom/public/nsIScriptObjectOwner.h | 25 +- layout/events/src/nsEventListenerManager.cpp | 37 ++- rdf/content/public/nsIXULDocument.h | 5 + rdf/content/public/nsIXULPrototypeDocument.h | 2 +- rdf/content/src/nsXULDocument.cpp | 22 +- rdf/content/src/nsXULDocument.h | 1 + rdf/content/src/nsXULElement.cpp | 204 ++++++++++---- rdf/content/src/nsXULElement.h | 6 +- rdf/content/src/nsXULKeyListener.cpp | 13 +- rdf/content/src/nsXULPrototypeDocument.cpp | 254 +++++++++++++++--- 18 files changed, 866 insertions(+), 234 deletions(-) diff --git a/content/events/src/nsEventListenerManager.cpp b/content/events/src/nsEventListenerManager.cpp index ad8ec427e671..d98202133c0c 100644 --- a/content/events/src/nsEventListenerManager.cpp +++ b/content/events/src/nsEventListenerManager.cpp @@ -553,13 +553,17 @@ nsEventListenerManager::AddScriptEventListener(nsIScriptContext* aContext, } if (!done) { - rv = aContext->CompileEventHandler(scriptObject, aName, aBody, - (handlerOwner != nsnull), - &handler); - if (NS_FAILED(rv)) - return rv; - if (handlerOwner) - handlerOwner->SetCompiledEventHandler(aName, handler); + if (handlerOwner) { + // Always let the handler owner compile the event handler, as + // it may want to use a special context or scope object. + rv = handlerOwner->CompileEventHandler(aContext, scriptObject, aName, aBody, &handler); + } + else { + rv = aContext->CompileEventHandler(scriptObject, aName, aBody, + (handlerOwner != nsnull), + &handler); + } + if (NS_FAILED(rv)) return rv; } } return SetJSEventListener(aContext, aScriptObjectOwner, aName, aIID, aDeferCompilation); @@ -621,12 +625,19 @@ nsEventListenerManager::HandleEventSubType(nsListenerStruct* aListenerStruct, nsAutoString handlerBody; result = content->GetAttribute(kNameSpaceID_None, atom, handlerBody); if (NS_SUCCEEDED(result)) { - result = scriptCX->CompileEventHandler(jsobj, atom, handlerBody, - (handlerOwner != nsnull), - &handler); - aListenerStruct->mHandlerIsString &= ~aSubType; - if (handlerOwner) - handlerOwner->SetCompiledEventHandler(atom, handler); + if (handlerOwner) { + // Always let the handler owner compile the event + // handler, as it may want to use a special + // context or scope object. + result = handlerOwner->CompileEventHandler(scriptCX, jsobj, atom, handlerBody, &handler); + } + else { + result = scriptCX->CompileEventHandler(jsobj, atom, handlerBody, + (handlerOwner != nsnull), + &handler); + } + if (NS_SUCCEEDED(result)) + aListenerStruct->mHandlerIsString &= ~aSubType; } } } diff --git a/content/xul/content/src/nsXULElement.cpp b/content/xul/content/src/nsXULElement.cpp index 9b1db3370d59..ad1769239f9a 100644 --- a/content/xul/content/src/nsXULElement.cpp +++ b/content/xul/content/src/nsXULElement.cpp @@ -1594,14 +1594,18 @@ nsXULElement::GetScriptObject(nsIScriptContext* aContext, void** aScriptObject) // tag... nsresult (*fn)(nsIScriptContext* aContext, nsISupports* aSupports, nsISupports* aParent, void** aReturn); + const char* rootname; if (Tag() == kTreeAtom) { fn = NS_NewScriptXULTreeElement; + rootname = "nsXULTreeElement::mScriptObject"; } else if (Tag() == kEditorAtom) { fn = NS_NewScriptXULEditorElement; + rootname = "nsXULEditorElement::mScriptObject"; } else { fn = NS_NewScriptXULElement; + rootname = "nsXULElement::mScriptObject"; } // Create the script object; N.B. that if |mDocument| is null, @@ -1611,7 +1615,7 @@ nsXULElement::GetScriptObject(nsIScriptContext* aContext, void** aScriptObject) rv = fn(aContext, (nsIDOMXULElement*) this, mDocument, (void**) &mScriptObject); // Ensure that a reference exists to this element - aContext->AddNamedReference((void*) &mScriptObject, mScriptObject, "nsXULElement::mScriptObject"); + aContext->AddNamedReference((void*) &mScriptObject, mScriptObject, rootname); } *aScriptObject = mScriptObject; @@ -1651,29 +1655,85 @@ nsXULElement::GetCompiledEventHandler(nsIAtom *aName, void** aHandler) } NS_IMETHODIMP -nsXULElement::SetCompiledEventHandler(nsIAtom *aName, void* aHandler) +nsXULElement::CompileEventHandler(nsIScriptContext* aContext, + void* aTarget, + nsIAtom *aName, + const nsString& aBody, + void** aHandler) { + nsresult rv; + XUL_PROTOTYPE_ATTRIBUTE_METER(gNumCacheSets); + + nsCOMPtr context; + JSObject* scopeObject; + PRBool shared; + if (mPrototype) { + // It'll be shared amonst the instances of the prototype + shared = PR_TRUE; + + // Use the prototype document's special context + nsCOMPtr xuldoc = do_QueryInterface(mDocument); + NS_ASSERTION(xuldoc != nsnull, "mDocument is not an nsIXULDocument"); + if (! xuldoc) + return NS_ERROR_UNEXPECTED; + + nsCOMPtr protodoc; + rv = xuldoc->GetMasterPrototype(getter_AddRefs(protodoc)); + if (NS_FAILED(rv)) return rv; + + NS_ASSERTION(protodoc != nsnull, "xul document has no prototype"); + if (! protodoc) + return NS_ERROR_UNEXPECTED; + + nsCOMPtr global = do_QueryInterface(protodoc); + NS_ASSERTION(global != nsnull, "prototype doc is not a script global"); + if (! global) + return NS_ERROR_UNEXPECTED; + + rv = global->GetContext(getter_AddRefs(context)); + if (NS_FAILED(rv)) return rv; + + // Use the prototype script's special scope object + nsCOMPtr owner = do_QueryInterface(protodoc); + if (! owner) + return NS_ERROR_UNEXPECTED; + + rv = owner->GetScriptObject(context, (void**) &scopeObject); + if (NS_FAILED(rv)) return rv; + } + else { + // We don't have a prototype; do a one-off compile. + shared = PR_FALSE; + context = aContext; + scopeObject = NS_STATIC_CAST(JSObject*, aTarget); + } + + NS_ASSERTION(context != nsnull, "no script context"); + if (! context) + return NS_ERROR_UNEXPECTED; + + // Compile the event handler + rv = context->CompileEventHandler(scopeObject, aName, aBody, shared, aHandler); + if (NS_FAILED(rv)) return rv; + + if (shared) { + // If it's a shared handler, we need to bind the shared + // function object to the real target. + rv = aContext->BindCompiledEventHandler(aTarget, aName, *aHandler); + if (NS_FAILED(rv)) return rv; + } + + if (mPrototype) { + // Remember the compiled event handler for (PRInt32 i = 0; i < mPrototype->mNumAttributes; ++i) { nsXULPrototypeAttribute* attr = &(mPrototype->mAttributes[i]); if ((attr->mNameSpaceID == kNameSpaceID_None) && (attr->mName.get() == aName)) { - nsresult rv; - XUL_PROTOTYPE_ATTRIBUTE_METER(gNumCacheFills); - attr->mEventHandler = aHandler; - - nsCOMPtr global; - mDocument->GetScriptGlobalObject(getter_AddRefs(global)); - NS_ASSERTION(global != nsnull, "no script global object"); - if (! global) - return NS_ERROR_UNEXPECTED; - - nsCOMPtr context; - rv = global->GetContext(getter_AddRefs(context)); - if (NS_FAILED(rv)) return rv; + attr->mEventHandler = *aHandler; JSContext *cx = (JSContext*) context->GetNativeContext(); if (!cx) @@ -1686,6 +1746,7 @@ nsXULElement::SetCompiledEventHandler(nsIAtom *aName, void* aHandler) } } } + return NS_OK; } @@ -1926,7 +1987,10 @@ nsXULElement::InsertChildAt(nsIContent* aKid, PRInt32 aIndex, PRBool aNotify) if (insertOk) { aKid->SetParent(NS_STATIC_CAST(nsIStyledContent*, this)); //nsRange::OwnerChildInserted(this, aIndex); + + //XXXwaterson this should be shallow aKid->SetDocument(mDocument, PR_TRUE); + if (aNotify && ElementIsInDocument()) { mDocument->ContentInserted(NS_STATIC_CAST(nsIStyledContent*, this), aKid, aIndex); } @@ -1965,10 +2029,21 @@ nsXULElement::ReplaceChildAt(nsIContent* aKid, PRInt32 aIndex, PRBool aNotify) if (replaceOk) { aKid->SetParent(NS_STATIC_CAST(nsIStyledContent*, this)); //nsRange::OwnerChildReplaced(this, aIndex, oldKid); + + //XXXwaterson this should be shallow aKid->SetDocument(mDocument, PR_TRUE); + if (aNotify && ElementIsInDocument()) { mDocument->ContentReplaced(NS_STATIC_CAST(nsIStyledContent*, this), oldKid, aKid, aIndex); } + +#if 0 //XXXwaterson put this in eventually. + // This will cause the script object to be unrooted for each + // element in the subtree. + oldKid->SetDocument(nsnull, PR_TRUE); +#endif + + // We've got no mo' parent. oldKid->SetParent(nsnull); } return NS_OK; @@ -1992,7 +2067,10 @@ nsXULElement::AppendChildTo(nsIContent* aKid, PRBool aNotify) if (appendOk) { aKid->SetParent(NS_STATIC_CAST(nsIStyledContent*, this)); // ranges don't need adjustment since new child is at end of list + + //XXXwaterson this should be shallow aKid->SetDocument(mDocument, PR_TRUE); + if (aNotify && ElementIsInDocument()) { PRUint32 cnt; rv = mChildren->Count(&cnt); @@ -2097,6 +2175,14 @@ nsXULElement::RemoveChildAt(PRInt32 aIndex, PRBool aNotify) if (aNotify && removeOk && ElementIsInDocument()) { doc->ContentRemoved(NS_STATIC_CAST(nsIStyledContent*, this), oldKid, aIndex); } + +#if 0 //XXXwaterson put this in eventually + // This will cause the script object to be unrooted for each + // element in the subtree. + oldKid->SetDocument(nsnull, PR_TRUE); +#endif + + // We've got no mo' parent. oldKid->SetParent(nsnull); } @@ -3921,33 +4007,13 @@ nsXULPrototypeScript::~nsXULPrototypeScript() } nsresult -nsXULPrototypeScript::Compile(const PRUnichar* aText, PRInt32 aTextLength, - nsIURI* aURI, PRInt32 aLineNo, +nsXULPrototypeScript::Compile(const PRUnichar* aText, + PRInt32 aTextLength, + nsIURI* aURI, + PRInt32 aLineNo, nsIDocument* aDocument, nsIXULPrototypeDocument* aPrototypeDocument) { - nsresult rv; - - nsCOMPtr global; - aDocument->GetScriptGlobalObject(getter_AddRefs(global)); - NS_ASSERTION(global != nsnull, "no script global object"); - if (! global) - return NS_ERROR_UNEXPECTED; - - nsCOMPtr context; - rv = global->GetContext(getter_AddRefs(context)); - if (NS_FAILED(rv)) return rv; - - JSContext *cx = (JSContext*) context->GetNativeContext(); - if (!cx) - return NS_ERROR_UNEXPECTED; - - nsCOMPtr principal = - dont_AddRef(aDocument->GetDocumentPrincipal()); - - nsXPIDLCString urlspec; - aURI->GetSpec(getter_Copies(urlspec)); - // We'll compile the script using the prototype document's special // script object as the parent. This ensures that we won't end up // with an uncollectable reference. @@ -3959,17 +4025,61 @@ nsXULPrototypeScript::Compile(const PRUnichar* aText, PRInt32 aTextLength, // and the first document would indirectly reference the prototype // document because it keeps the prototype cache // alive. Circularity! - void * scopeObject; - nsCOMPtr owner= do_QueryInterface(aPrototypeDocument, &rv); - if (NS_FAILED(rv)) return rv; + nsresult rv; + + // Use the prototype document's special context + nsCOMPtr context; + + { + nsCOMPtr global = do_QueryInterface(aPrototypeDocument); + NS_ASSERTION(global != nsnull, "prototype doc is not a script global"); + if (! global) + return NS_ERROR_UNEXPECTED; + + rv = global->GetContext(getter_AddRefs(context)); + if (NS_FAILED(rv)) return rv; + } + + NS_ASSERTION(context != nsnull, "no context for script global"); + if (! context) + return NS_ERROR_UNEXPECTED; + + // Use the prototype script's special scope object + JSObject* scopeObject; + + { + nsCOMPtr owner = do_QueryInterface(aPrototypeDocument); + if (! owner) + return NS_ERROR_UNEXPECTED; - rv = owner->GetScriptObject(context, &scopeObject); + rv = owner->GetScriptObject(context, (void**) &scopeObject); + if (NS_FAILED(rv)) return rv; + } + + // Use the enclosing document's principal + // XXX is this right? or should we use the protodoc's? + nsCOMPtr principal = + dont_AddRef(aDocument->GetDocumentPrincipal()); + + nsXPIDLCString urlspec; + aURI->GetSpec(getter_Copies(urlspec)); + + // Ok, compile it to create a prototype script object! + rv = context->CompileScript(aText, + aTextLength, + scopeObject, + principal, + urlspec, + aLineNo, + mLangVersion, + (void**) &mScriptObject); + if (NS_FAILED(rv)) return rv; - rv = context->CompileScript(aText, aTextLength, (JSObject *)scopeObject, - principal, urlspec, aLineNo, mLangVersion, - (void**) &mScriptObject); - if (NS_FAILED(rv)) return rv; + // Root the compiled prototype script object. + JSContext* cx = NS_STATIC_CAST(JSContext*, context->GetNativeContext()); + if (!cx) + return NS_ERROR_UNEXPECTED; rv = AddJSGCRoot(cx, &mScriptObject, "nsXULPrototypeScript::mScriptObject"); return rv; diff --git a/content/xul/content/src/nsXULElement.h b/content/xul/content/src/nsXULElement.h index ccd95aacfcab..e9268adf4363 100644 --- a/content/xul/content/src/nsXULElement.h +++ b/content/xul/content/src/nsXULElement.h @@ -463,8 +463,12 @@ public: NS_IMETHOD SetScriptObject(void *aScriptObject); // nsIScriptEventHandlerOwner + NS_IMETHOD CompileEventHandler(nsIScriptContext* aContext, + void* aTarget, + nsIAtom *aName, + const nsString& aBody, + void** aHandler); NS_IMETHOD GetCompiledEventHandler(nsIAtom *aName, void** aHandler); - NS_IMETHOD SetCompiledEventHandler(nsIAtom *aName, void* aHandler); // nsIJSScriptObject virtual PRBool AddProperty(JSContext *aContext, JSObject *aObj, diff --git a/content/xul/document/public/nsIXULDocument.h b/content/xul/document/public/nsIXULDocument.h index 8a1fa0849412..ea38ca876396 100644 --- a/content/xul/document/public/nsIXULDocument.h +++ b/content/xul/document/public/nsIXULDocument.h @@ -113,6 +113,11 @@ public: */ NS_IMETHOD SetMasterPrototype(nsIXULPrototypeDocument* aDocument) = 0; + /** + * Get the master prototype. + */ + NS_IMETHOD GetMasterPrototype(nsIXULPrototypeDocument** aPrototypeDocument) = 0; + /** * Set the current prototype */ diff --git a/content/xul/document/public/nsIXULPrototypeDocument.h b/content/xul/document/public/nsIXULPrototypeDocument.h index 4786352016ea..487dd9be4018 100644 --- a/content/xul/document/public/nsIXULPrototypeDocument.h +++ b/content/xul/document/public/nsIXULPrototypeDocument.h @@ -33,8 +33,8 @@ class nsIAtom; class nsIPrincipal; -class nsIURI; class nsIStyleSheet; +class nsIURI; class nsString; class nsVoidArray; class nsXULPrototypeElement; diff --git a/content/xul/document/src/nsXULDocument.cpp b/content/xul/document/src/nsXULDocument.cpp index b15d80b71e4f..01fd121746a7 100644 --- a/content/xul/document/src/nsXULDocument.cpp +++ b/content/xul/document/src/nsXULDocument.cpp @@ -2108,6 +2108,14 @@ nsXULDocument::SetMasterPrototype(nsIXULPrototypeDocument* aDocument) return NS_OK; } +NS_IMETHODIMP +nsXULDocument::GetMasterPrototype(nsIXULPrototypeDocument** aDocument) +{ + *aDocument = mMasterPrototype; + NS_IF_ADDREF(*aDocument); + return NS_OK; +} + NS_IMETHODIMP nsXULDocument::SetCurrentPrototype(nsIXULPrototypeDocument* aDocument) { @@ -2683,7 +2691,7 @@ nsXULDocument::AddSubtreeToDocument(nsIContent* aElement) if (NS_FAILED(rv)) return rv; } - // Finally, recurse to children. + // 4. Recurse to children. PRInt32 count; nsCOMPtr xulcontent = do_QueryInterface(aElement); rv = xulcontent ? xulcontent->PeekChildCount(count) : aElement->ChildCount(count); @@ -2708,6 +2716,7 @@ nsXULDocument::RemoveSubtreeFromDocument(nsIContent* aElement) // document. nsresult rv; + // 1. Remove any children from the document. PRInt32 count; nsCOMPtr xulcontent = do_QueryInterface(aElement); rv = xulcontent ? xulcontent->PeekChildCount(count) : aElement->ChildCount(count); @@ -2722,11 +2731,11 @@ nsXULDocument::RemoveSubtreeFromDocument(nsIContent* aElement) if (NS_FAILED(rv)) return rv; } - // 1. Remove the element from the resource-to-element map + // 2. Remove the element from the resource-to-element map rv = RemoveElementFromMap(aElement); if (NS_FAILED(rv)) return rv; - // 2. If the element is a 'command updater', then remove the + // 3. If the element is a 'command updater', then remove the // element from the document's command dispatcher. nsAutoString value; rv = aElement->GetAttribute(kNameSpaceID_None, kCommandUpdaterAtom, value); @@ -3951,10 +3960,9 @@ nsXULDocument::CreateElement(PRInt32 aNameSpaceID, return NS_ERROR_UNEXPECTED; } - rv = result->SetDocument(this, PR_FALSE); - NS_ASSERTION(NS_SUCCEEDED(rv), "unable to set element's document"); - if (NS_FAILED(rv)) return rv; - +#if 1 // XXXwaterson remove this eventually + result->SetDocument(this, PR_FALSE); +#endif result->SetContentID(mNextContentID++); *aResult = result; diff --git a/content/xul/document/src/nsXULDocument.h b/content/xul/document/src/nsXULDocument.h index 0aa86464570c..7361b7866d5c 100644 --- a/content/xul/document/src/nsXULDocument.h +++ b/content/xul/document/src/nsXULDocument.h @@ -285,6 +285,7 @@ public: NS_IMETHOD AddForwardReference(nsForwardReference* aRef); NS_IMETHOD ResolveForwardReferences(); NS_IMETHOD SetMasterPrototype(nsIXULPrototypeDocument* aDocument); + NS_IMETHOD GetMasterPrototype(nsIXULPrototypeDocument** aDocument); NS_IMETHOD SetCurrentPrototype(nsIXULPrototypeDocument* aDocument); NS_IMETHOD SetDocumentURL(nsIURI* anURL); NS_IMETHOD PrepareStyleSheets(nsIURI* anURL); diff --git a/content/xul/document/src/nsXULPrototypeDocument.cpp b/content/xul/document/src/nsXULPrototypeDocument.cpp index b83a2257de96..f5188177a5b1 100644 --- a/content/xul/document/src/nsXULPrototypeDocument.cpp +++ b/content/xul/document/src/nsXULPrototypeDocument.cpp @@ -27,19 +27,23 @@ */ #include "nsCOMPtr.h" -#include "nsString2.h" -#include "nsVoidArray.h" #include "nsIPrincipal.h" +#include "nsIScriptGlobalObject.h" +#include "nsIScriptGlobalObjectData.h" +#include "nsIScriptSecurityManager.h" +#include "nsIServiceManager.h" #include "nsISupportsArray.h" #include "nsIURI.h" -#include "nsIServiceManager.h" -#include "nsIScriptSecurityManager.h" #include "nsIXULPrototypeDocument.h" +#include "nsString2.h" +#include "nsVoidArray.h" #include "nsXULElement.h" -#include "nsIJSRuntimeService.h" + class nsXULPrototypeDocument : public nsIXULPrototypeDocument, - public nsIScriptObjectOwner + public nsIScriptObjectOwner, + public nsIScriptGlobalObject, + public nsIScriptGlobalObjectData { public: static nsresult @@ -71,12 +75,33 @@ public: NS_IMETHOD GetScriptObject(nsIScriptContext *aContext, void **aObject); NS_IMETHOD SetScriptObject(void *aObject); + // nsIScriptGlobalObject methods + NS_IMETHOD SetContext(nsIScriptContext *aContext); + NS_IMETHOD GetContext(nsIScriptContext **aContext); + NS_IMETHOD SetNewDocument(nsIDOMDocument *aDocument); + NS_IMETHOD SetWebShell(nsIWebShell *aWebShell); + NS_IMETHOD GetWebShell(nsIWebShell **aWebShell); + NS_IMETHOD SetOpenerWindow(nsIDOMWindow *aOpener); + NS_IMETHOD SetGlobalObjectOwner(nsIScriptGlobalObjectOwner* aOwner); + NS_IMETHOD GetGlobalObjectOwner(nsIScriptGlobalObjectOwner** aOwner); + NS_IMETHOD HandleDOMEvent(nsIPresContext* aPresContext, + nsEvent* aEvent, + nsIDOMEvent** aDOMEvent, + PRUint32 aFlags, + nsEventStatus* aEventStatus); + + // nsIScriptGlobalObjectData methods + NS_IMETHOD GetPrincipal(nsIPrincipal** aPrincipal); + + protected: nsCOMPtr mURI; nsXULPrototypeElement* mRoot; nsCOMPtr mStyleSheetReferences; nsCOMPtr mOverlayReferences; nsCOMPtr mDocumentPrincipal; + + nsCOMPtr mScriptContext; JSObject *mScriptObject; // XXX JS language rabies bigotry badness nsXULPrototypeDocument(); @@ -85,13 +110,30 @@ protected: friend NS_IMETHODIMP NS_NewXULPrototypeDocument(nsISupports* aOuter, REFNSIID aIID, void** aResult); + + static JSClass gSharedGlobalClass; + + static void PR_CALLBACK + FinalizeScriptObject(JSContext* cx, JSObject* obj); +}; + +JSClass nsXULPrototypeDocument::gSharedGlobalClass = { + "nsXULPrototypeScript compilation scope", + JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, nsXULPrototypeDocument::FinalizeScriptObject }; + //---------------------------------------------------------------------- +// +// ctors, dtors, n' stuff +// nsXULPrototypeDocument::nsXULPrototypeDocument() - : mRoot(nsnull), mScriptObject(nsnull) + : mRoot(nsnull), + mScriptObject(nsnull) { NS_INIT_REFCNT(); } @@ -115,19 +157,14 @@ nsXULPrototypeDocument::Init() nsXULPrototypeDocument::~nsXULPrototypeDocument() { delete mRoot; - - nsresult rv; - NS_WITH_SERVICE(nsIJSRuntimeService, rtsvc, "nsJSRuntimeService", &rv); - if (NS_SUCCEEDED(rv)) { - JSRuntime *rt; - rv = rtsvc->GetRuntime(&rt); - if (NS_SUCCEEDED(rv) && rt) - JS_RemoveRootRT(rt, &mScriptObject); - } } -NS_IMPL_ISUPPORTS2(nsXULPrototypeDocument, nsIXULPrototypeDocument, nsIScriptObjectOwner); +NS_IMPL_ISUPPORTS4(nsXULPrototypeDocument, + nsIXULPrototypeDocument, + nsIScriptObjectOwner, + nsIScriptGlobalObject, + nsIScriptGlobalObjectData); NS_IMETHODIMP NS_NewXULPrototypeDocument(nsISupports* aOuter, REFNSIID aIID, void** aResult) @@ -156,6 +193,9 @@ NS_NewXULPrototypeDocument(nsISupports* aOuter, REFNSIID aIID, void** aResult) //---------------------------------------------------------------------- +// +// nsIXULPrototypeDocument methods +// NS_IMETHODIMP nsXULPrototypeDocument::GetURI(nsIURI** aResult) @@ -169,7 +209,7 @@ nsXULPrototypeDocument::GetURI(nsIURI** aResult) NS_IMETHODIMP nsXULPrototypeDocument::SetURI(nsIURI* aURI) { - mURI = aURI; + mURI = dont_QueryInterface(aURI); return NS_OK; } @@ -257,14 +297,20 @@ nsXULPrototypeDocument::GetDocumentPrincipal(nsIPrincipal** aResult) { if (!mDocumentPrincipal) { nsresult rv; - NS_WITH_SERVICE(nsIScriptSecurityManager, securityManager, - NS_SCRIPTSECURITYMANAGER_PROGID, &rv); + NS_WITH_SERVICE(nsIScriptSecurityManager, + securityManager, + NS_SCRIPTSECURITYMANAGER_PROGID, + &rv); + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; + rv = securityManager->GetCodebasePrincipal(mURI, getter_AddRefs(mDocumentPrincipal)); + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; } + *aResult = mDocumentPrincipal; NS_ADDREF(*aResult); return NS_OK; @@ -278,40 +324,43 @@ nsXULPrototypeDocument::SetDocumentPrincipal(nsIPrincipal* aPrincipal) return NS_OK; } -JSClass null_class = { - "nsXULPrototypeScript compilation scope", 0, - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub -}; - //---------------------------------------------------------------------- +// +// nsIScriptObjectOwner methods +// NS_IMETHODIMP nsXULPrototypeDocument::GetScriptObject(nsIScriptContext *aContext, void **aObject) { - // The prototype document will have its own special secret script - // object that can be used to compile scripts and event handlers. - if (!mScriptObject) { - JSContext *cx = (JSContext *)aContext->GetNativeContext(); - if (!cx) - return NS_ERROR_UNEXPECTED; + // The prototype document has its own special secret script object + // that can be used to compile scripts and event handlers. + nsresult rv; - mScriptObject = JS_NewObject(cx, &null_class, nsnull, nsnull); - if (!mScriptObject) + nsCOMPtr context; + + if (mScriptContext && aContext != mScriptContext.get()) { + rv = GetContext(getter_AddRefs(context)); + if (NS_FAILED(rv)) return rv; + } + else { + context = aContext; + } + + if (! mScriptObject) { + JSContext* cx = NS_STATIC_CAST(JSContext*, context->GetNativeContext()); + if (! cx) return NS_ERROR_OUT_OF_MEMORY; - // Be sure to unlink the script object from the parent global - // object. This ensures that we don't end up with a circular - // reference back to the first document. - JS_SetPrototype(cx, mScriptObject, nsnull); - JS_SetParent(cx, mScriptObject, nsnull); + mScriptObject = JS_NewObject(cx, &gSharedGlobalClass, nsnull, nsnull); + if (! mScriptObject) + return NS_ERROR_OUT_OF_MEMORY; - JS_AddNamedRoot(cx, &mScriptObject, "nsXULPrototypeDocument::mScriptObject"); - - // We need standard classes, in particular RegExp, to compile JS. - if (!JS_InitStandardClasses(cx, mScriptObject)) - return NS_ERROR_FAILURE; + // Add an owning reference from JS back to us. This'll be + // released when the JSObject is finalized. + ::JS_SetPrivate(cx, mScriptObject, this); + NS_ADDREF(this); } + *aObject = mScriptObject; return NS_OK; } @@ -322,3 +371,120 @@ nsXULPrototypeDocument::SetScriptObject(void *aObject) mScriptObject = (JSObject *)aObject; return NS_OK; } + +//---------------------------------------------------------------------- +// +// nsIScriptGlobalObject methods +// + +NS_IMETHODIMP +nsXULPrototypeDocument::SetContext(nsIScriptContext *aContext) +{ + mScriptContext = aContext; + return NS_OK; +} + + +NS_IMETHODIMP +nsXULPrototypeDocument::GetContext(nsIScriptContext **aContext) +{ + if (! mScriptContext) { + nsresult rv; + rv = NS_CreateScriptContext(this, getter_AddRefs(mScriptContext)); + if (NS_FAILED(rv)) return rv; + } + + *aContext = mScriptContext; + NS_IF_ADDREF(*aContext); + return NS_OK; +} + + +NS_IMETHODIMP +nsXULPrototypeDocument::SetNewDocument(nsIDOMDocument *aDocument) +{ + NS_NOTREACHED("waaah!"); + return NS_ERROR_UNEXPECTED; +} + + +NS_IMETHODIMP +nsXULPrototypeDocument::SetWebShell(nsIWebShell *aWebShell) +{ + NS_NOTREACHED("waaah!"); + return NS_ERROR_UNEXPECTED; +} + + +NS_IMETHODIMP +nsXULPrototypeDocument::GetWebShell(nsIWebShell **aWebShell) +{ + NS_NOTREACHED("waaah!"); + return NS_ERROR_UNEXPECTED; +} + + +NS_IMETHODIMP +nsXULPrototypeDocument::SetOpenerWindow(nsIDOMWindow *aOpener) +{ + NS_NOTREACHED("waaah!"); + return NS_ERROR_UNEXPECTED; +} + + +NS_IMETHODIMP +nsXULPrototypeDocument::SetGlobalObjectOwner(nsIScriptGlobalObjectOwner* aOwner) +{ + NS_NOTREACHED("waaah!"); + return NS_ERROR_UNEXPECTED; +} + + +NS_IMETHODIMP +nsXULPrototypeDocument::GetGlobalObjectOwner(nsIScriptGlobalObjectOwner** aOwner) +{ + NS_NOTREACHED("waaah!"); + return NS_ERROR_UNEXPECTED; +} + + +NS_IMETHODIMP +nsXULPrototypeDocument::HandleDOMEvent(nsIPresContext* aPresContext, + nsEvent* aEvent, + nsIDOMEvent** aDOMEvent, + PRUint32 aFlags, + nsEventStatus* aEventStatus) +{ + NS_NOTREACHED("waaah!"); + return NS_ERROR_UNEXPECTED; +} + +//---------------------------------------------------------------------- +// +// nsIScriptGlobalObjectData methods +// + +NS_IMETHODIMP +nsXULPrototypeDocument::GetPrincipal(nsIPrincipal** aPrincipal) +{ + return GetDocumentPrincipal(aPrincipal); +} + +//---------------------------------------------------------------------- +// +// Implementation methods +// + +void PR_CALLBACK +nsXULPrototypeDocument::FinalizeScriptObject(JSContext* cx, JSObject* obj) +{ + nsXULPrototypeDocument* native = + NS_STATIC_CAST(nsXULPrototypeDocument*, ::JS_GetPrivate(cx, obj)); + + if (native) { + // Clear the native object's reference back to us, and release + // our ownership of it. + native->SetScriptObject(nsnull); + NS_RELEASE(native); + } +} diff --git a/dom/public/nsIScriptObjectOwner.h b/dom/public/nsIScriptObjectOwner.h index 1ff75aab4c5a..5367783088c5 100644 --- a/dom/public/nsIScriptObjectOwner.h +++ b/dom/public/nsIScriptObjectOwner.h @@ -90,9 +90,30 @@ class nsIScriptEventHandlerOwner : public nsISupports public: NS_DEFINE_STATIC_IID_ACCESSOR(NS_ISCRIPTEVENTHANDLEROWNER_IID) - NS_IMETHOD GetCompiledEventHandler(nsIAtom *aName, void** aHandler) = 0; + /** + * Compile the specified event handler, and bind it to aTarget using + * aContext. + * + * @param aContext the context to use when creating event handler + * @param aTarget the object to which to bind the event handler + * @param aName the name of the handler + * @param aBody the handler script body + * @param aHandler the compiled, bound handler object + */ + NS_IMETHOD CompileEventHandler(nsIScriptContext* aContext, + void* aTarget, + nsIAtom *aName, + const nsString& aBody, + void** aHandler) = 0; - NS_IMETHOD SetCompiledEventHandler(nsIAtom *aName, void* aHandler) = 0; + /** + * Retrieve an already-compiled event handler that can be bound to a + * target object using a script context. + * + * @param aName the name of the event handler to retrieve + * @param aHandler the compiled event handler + */ + NS_IMETHOD GetCompiledEventHandler(nsIAtom *aName, void** aHandler) = 0; }; #endif // nsIScriptObjectOwner_h__ diff --git a/layout/events/src/nsEventListenerManager.cpp b/layout/events/src/nsEventListenerManager.cpp index ad8ec427e671..d98202133c0c 100644 --- a/layout/events/src/nsEventListenerManager.cpp +++ b/layout/events/src/nsEventListenerManager.cpp @@ -553,13 +553,17 @@ nsEventListenerManager::AddScriptEventListener(nsIScriptContext* aContext, } if (!done) { - rv = aContext->CompileEventHandler(scriptObject, aName, aBody, - (handlerOwner != nsnull), - &handler); - if (NS_FAILED(rv)) - return rv; - if (handlerOwner) - handlerOwner->SetCompiledEventHandler(aName, handler); + if (handlerOwner) { + // Always let the handler owner compile the event handler, as + // it may want to use a special context or scope object. + rv = handlerOwner->CompileEventHandler(aContext, scriptObject, aName, aBody, &handler); + } + else { + rv = aContext->CompileEventHandler(scriptObject, aName, aBody, + (handlerOwner != nsnull), + &handler); + } + if (NS_FAILED(rv)) return rv; } } return SetJSEventListener(aContext, aScriptObjectOwner, aName, aIID, aDeferCompilation); @@ -621,12 +625,19 @@ nsEventListenerManager::HandleEventSubType(nsListenerStruct* aListenerStruct, nsAutoString handlerBody; result = content->GetAttribute(kNameSpaceID_None, atom, handlerBody); if (NS_SUCCEEDED(result)) { - result = scriptCX->CompileEventHandler(jsobj, atom, handlerBody, - (handlerOwner != nsnull), - &handler); - aListenerStruct->mHandlerIsString &= ~aSubType; - if (handlerOwner) - handlerOwner->SetCompiledEventHandler(atom, handler); + if (handlerOwner) { + // Always let the handler owner compile the event + // handler, as it may want to use a special + // context or scope object. + result = handlerOwner->CompileEventHandler(scriptCX, jsobj, atom, handlerBody, &handler); + } + else { + result = scriptCX->CompileEventHandler(jsobj, atom, handlerBody, + (handlerOwner != nsnull), + &handler); + } + if (NS_SUCCEEDED(result)) + aListenerStruct->mHandlerIsString &= ~aSubType; } } } diff --git a/rdf/content/public/nsIXULDocument.h b/rdf/content/public/nsIXULDocument.h index 8a1fa0849412..ea38ca876396 100644 --- a/rdf/content/public/nsIXULDocument.h +++ b/rdf/content/public/nsIXULDocument.h @@ -113,6 +113,11 @@ public: */ NS_IMETHOD SetMasterPrototype(nsIXULPrototypeDocument* aDocument) = 0; + /** + * Get the master prototype. + */ + NS_IMETHOD GetMasterPrototype(nsIXULPrototypeDocument** aPrototypeDocument) = 0; + /** * Set the current prototype */ diff --git a/rdf/content/public/nsIXULPrototypeDocument.h b/rdf/content/public/nsIXULPrototypeDocument.h index 4786352016ea..487dd9be4018 100644 --- a/rdf/content/public/nsIXULPrototypeDocument.h +++ b/rdf/content/public/nsIXULPrototypeDocument.h @@ -33,8 +33,8 @@ class nsIAtom; class nsIPrincipal; -class nsIURI; class nsIStyleSheet; +class nsIURI; class nsString; class nsVoidArray; class nsXULPrototypeElement; diff --git a/rdf/content/src/nsXULDocument.cpp b/rdf/content/src/nsXULDocument.cpp index b15d80b71e4f..01fd121746a7 100644 --- a/rdf/content/src/nsXULDocument.cpp +++ b/rdf/content/src/nsXULDocument.cpp @@ -2108,6 +2108,14 @@ nsXULDocument::SetMasterPrototype(nsIXULPrototypeDocument* aDocument) return NS_OK; } +NS_IMETHODIMP +nsXULDocument::GetMasterPrototype(nsIXULPrototypeDocument** aDocument) +{ + *aDocument = mMasterPrototype; + NS_IF_ADDREF(*aDocument); + return NS_OK; +} + NS_IMETHODIMP nsXULDocument::SetCurrentPrototype(nsIXULPrototypeDocument* aDocument) { @@ -2683,7 +2691,7 @@ nsXULDocument::AddSubtreeToDocument(nsIContent* aElement) if (NS_FAILED(rv)) return rv; } - // Finally, recurse to children. + // 4. Recurse to children. PRInt32 count; nsCOMPtr xulcontent = do_QueryInterface(aElement); rv = xulcontent ? xulcontent->PeekChildCount(count) : aElement->ChildCount(count); @@ -2708,6 +2716,7 @@ nsXULDocument::RemoveSubtreeFromDocument(nsIContent* aElement) // document. nsresult rv; + // 1. Remove any children from the document. PRInt32 count; nsCOMPtr xulcontent = do_QueryInterface(aElement); rv = xulcontent ? xulcontent->PeekChildCount(count) : aElement->ChildCount(count); @@ -2722,11 +2731,11 @@ nsXULDocument::RemoveSubtreeFromDocument(nsIContent* aElement) if (NS_FAILED(rv)) return rv; } - // 1. Remove the element from the resource-to-element map + // 2. Remove the element from the resource-to-element map rv = RemoveElementFromMap(aElement); if (NS_FAILED(rv)) return rv; - // 2. If the element is a 'command updater', then remove the + // 3. If the element is a 'command updater', then remove the // element from the document's command dispatcher. nsAutoString value; rv = aElement->GetAttribute(kNameSpaceID_None, kCommandUpdaterAtom, value); @@ -3951,10 +3960,9 @@ nsXULDocument::CreateElement(PRInt32 aNameSpaceID, return NS_ERROR_UNEXPECTED; } - rv = result->SetDocument(this, PR_FALSE); - NS_ASSERTION(NS_SUCCEEDED(rv), "unable to set element's document"); - if (NS_FAILED(rv)) return rv; - +#if 1 // XXXwaterson remove this eventually + result->SetDocument(this, PR_FALSE); +#endif result->SetContentID(mNextContentID++); *aResult = result; diff --git a/rdf/content/src/nsXULDocument.h b/rdf/content/src/nsXULDocument.h index 0aa86464570c..7361b7866d5c 100644 --- a/rdf/content/src/nsXULDocument.h +++ b/rdf/content/src/nsXULDocument.h @@ -285,6 +285,7 @@ public: NS_IMETHOD AddForwardReference(nsForwardReference* aRef); NS_IMETHOD ResolveForwardReferences(); NS_IMETHOD SetMasterPrototype(nsIXULPrototypeDocument* aDocument); + NS_IMETHOD GetMasterPrototype(nsIXULPrototypeDocument** aDocument); NS_IMETHOD SetCurrentPrototype(nsIXULPrototypeDocument* aDocument); NS_IMETHOD SetDocumentURL(nsIURI* anURL); NS_IMETHOD PrepareStyleSheets(nsIURI* anURL); diff --git a/rdf/content/src/nsXULElement.cpp b/rdf/content/src/nsXULElement.cpp index 9b1db3370d59..ad1769239f9a 100644 --- a/rdf/content/src/nsXULElement.cpp +++ b/rdf/content/src/nsXULElement.cpp @@ -1594,14 +1594,18 @@ nsXULElement::GetScriptObject(nsIScriptContext* aContext, void** aScriptObject) // tag... nsresult (*fn)(nsIScriptContext* aContext, nsISupports* aSupports, nsISupports* aParent, void** aReturn); + const char* rootname; if (Tag() == kTreeAtom) { fn = NS_NewScriptXULTreeElement; + rootname = "nsXULTreeElement::mScriptObject"; } else if (Tag() == kEditorAtom) { fn = NS_NewScriptXULEditorElement; + rootname = "nsXULEditorElement::mScriptObject"; } else { fn = NS_NewScriptXULElement; + rootname = "nsXULElement::mScriptObject"; } // Create the script object; N.B. that if |mDocument| is null, @@ -1611,7 +1615,7 @@ nsXULElement::GetScriptObject(nsIScriptContext* aContext, void** aScriptObject) rv = fn(aContext, (nsIDOMXULElement*) this, mDocument, (void**) &mScriptObject); // Ensure that a reference exists to this element - aContext->AddNamedReference((void*) &mScriptObject, mScriptObject, "nsXULElement::mScriptObject"); + aContext->AddNamedReference((void*) &mScriptObject, mScriptObject, rootname); } *aScriptObject = mScriptObject; @@ -1651,29 +1655,85 @@ nsXULElement::GetCompiledEventHandler(nsIAtom *aName, void** aHandler) } NS_IMETHODIMP -nsXULElement::SetCompiledEventHandler(nsIAtom *aName, void* aHandler) +nsXULElement::CompileEventHandler(nsIScriptContext* aContext, + void* aTarget, + nsIAtom *aName, + const nsString& aBody, + void** aHandler) { + nsresult rv; + XUL_PROTOTYPE_ATTRIBUTE_METER(gNumCacheSets); + + nsCOMPtr context; + JSObject* scopeObject; + PRBool shared; + if (mPrototype) { + // It'll be shared amonst the instances of the prototype + shared = PR_TRUE; + + // Use the prototype document's special context + nsCOMPtr xuldoc = do_QueryInterface(mDocument); + NS_ASSERTION(xuldoc != nsnull, "mDocument is not an nsIXULDocument"); + if (! xuldoc) + return NS_ERROR_UNEXPECTED; + + nsCOMPtr protodoc; + rv = xuldoc->GetMasterPrototype(getter_AddRefs(protodoc)); + if (NS_FAILED(rv)) return rv; + + NS_ASSERTION(protodoc != nsnull, "xul document has no prototype"); + if (! protodoc) + return NS_ERROR_UNEXPECTED; + + nsCOMPtr global = do_QueryInterface(protodoc); + NS_ASSERTION(global != nsnull, "prototype doc is not a script global"); + if (! global) + return NS_ERROR_UNEXPECTED; + + rv = global->GetContext(getter_AddRefs(context)); + if (NS_FAILED(rv)) return rv; + + // Use the prototype script's special scope object + nsCOMPtr owner = do_QueryInterface(protodoc); + if (! owner) + return NS_ERROR_UNEXPECTED; + + rv = owner->GetScriptObject(context, (void**) &scopeObject); + if (NS_FAILED(rv)) return rv; + } + else { + // We don't have a prototype; do a one-off compile. + shared = PR_FALSE; + context = aContext; + scopeObject = NS_STATIC_CAST(JSObject*, aTarget); + } + + NS_ASSERTION(context != nsnull, "no script context"); + if (! context) + return NS_ERROR_UNEXPECTED; + + // Compile the event handler + rv = context->CompileEventHandler(scopeObject, aName, aBody, shared, aHandler); + if (NS_FAILED(rv)) return rv; + + if (shared) { + // If it's a shared handler, we need to bind the shared + // function object to the real target. + rv = aContext->BindCompiledEventHandler(aTarget, aName, *aHandler); + if (NS_FAILED(rv)) return rv; + } + + if (mPrototype) { + // Remember the compiled event handler for (PRInt32 i = 0; i < mPrototype->mNumAttributes; ++i) { nsXULPrototypeAttribute* attr = &(mPrototype->mAttributes[i]); if ((attr->mNameSpaceID == kNameSpaceID_None) && (attr->mName.get() == aName)) { - nsresult rv; - XUL_PROTOTYPE_ATTRIBUTE_METER(gNumCacheFills); - attr->mEventHandler = aHandler; - - nsCOMPtr global; - mDocument->GetScriptGlobalObject(getter_AddRefs(global)); - NS_ASSERTION(global != nsnull, "no script global object"); - if (! global) - return NS_ERROR_UNEXPECTED; - - nsCOMPtr context; - rv = global->GetContext(getter_AddRefs(context)); - if (NS_FAILED(rv)) return rv; + attr->mEventHandler = *aHandler; JSContext *cx = (JSContext*) context->GetNativeContext(); if (!cx) @@ -1686,6 +1746,7 @@ nsXULElement::SetCompiledEventHandler(nsIAtom *aName, void* aHandler) } } } + return NS_OK; } @@ -1926,7 +1987,10 @@ nsXULElement::InsertChildAt(nsIContent* aKid, PRInt32 aIndex, PRBool aNotify) if (insertOk) { aKid->SetParent(NS_STATIC_CAST(nsIStyledContent*, this)); //nsRange::OwnerChildInserted(this, aIndex); + + //XXXwaterson this should be shallow aKid->SetDocument(mDocument, PR_TRUE); + if (aNotify && ElementIsInDocument()) { mDocument->ContentInserted(NS_STATIC_CAST(nsIStyledContent*, this), aKid, aIndex); } @@ -1965,10 +2029,21 @@ nsXULElement::ReplaceChildAt(nsIContent* aKid, PRInt32 aIndex, PRBool aNotify) if (replaceOk) { aKid->SetParent(NS_STATIC_CAST(nsIStyledContent*, this)); //nsRange::OwnerChildReplaced(this, aIndex, oldKid); + + //XXXwaterson this should be shallow aKid->SetDocument(mDocument, PR_TRUE); + if (aNotify && ElementIsInDocument()) { mDocument->ContentReplaced(NS_STATIC_CAST(nsIStyledContent*, this), oldKid, aKid, aIndex); } + +#if 0 //XXXwaterson put this in eventually. + // This will cause the script object to be unrooted for each + // element in the subtree. + oldKid->SetDocument(nsnull, PR_TRUE); +#endif + + // We've got no mo' parent. oldKid->SetParent(nsnull); } return NS_OK; @@ -1992,7 +2067,10 @@ nsXULElement::AppendChildTo(nsIContent* aKid, PRBool aNotify) if (appendOk) { aKid->SetParent(NS_STATIC_CAST(nsIStyledContent*, this)); // ranges don't need adjustment since new child is at end of list + + //XXXwaterson this should be shallow aKid->SetDocument(mDocument, PR_TRUE); + if (aNotify && ElementIsInDocument()) { PRUint32 cnt; rv = mChildren->Count(&cnt); @@ -2097,6 +2175,14 @@ nsXULElement::RemoveChildAt(PRInt32 aIndex, PRBool aNotify) if (aNotify && removeOk && ElementIsInDocument()) { doc->ContentRemoved(NS_STATIC_CAST(nsIStyledContent*, this), oldKid, aIndex); } + +#if 0 //XXXwaterson put this in eventually + // This will cause the script object to be unrooted for each + // element in the subtree. + oldKid->SetDocument(nsnull, PR_TRUE); +#endif + + // We've got no mo' parent. oldKid->SetParent(nsnull); } @@ -3921,33 +4007,13 @@ nsXULPrototypeScript::~nsXULPrototypeScript() } nsresult -nsXULPrototypeScript::Compile(const PRUnichar* aText, PRInt32 aTextLength, - nsIURI* aURI, PRInt32 aLineNo, +nsXULPrototypeScript::Compile(const PRUnichar* aText, + PRInt32 aTextLength, + nsIURI* aURI, + PRInt32 aLineNo, nsIDocument* aDocument, nsIXULPrototypeDocument* aPrototypeDocument) { - nsresult rv; - - nsCOMPtr global; - aDocument->GetScriptGlobalObject(getter_AddRefs(global)); - NS_ASSERTION(global != nsnull, "no script global object"); - if (! global) - return NS_ERROR_UNEXPECTED; - - nsCOMPtr context; - rv = global->GetContext(getter_AddRefs(context)); - if (NS_FAILED(rv)) return rv; - - JSContext *cx = (JSContext*) context->GetNativeContext(); - if (!cx) - return NS_ERROR_UNEXPECTED; - - nsCOMPtr principal = - dont_AddRef(aDocument->GetDocumentPrincipal()); - - nsXPIDLCString urlspec; - aURI->GetSpec(getter_Copies(urlspec)); - // We'll compile the script using the prototype document's special // script object as the parent. This ensures that we won't end up // with an uncollectable reference. @@ -3959,17 +4025,61 @@ nsXULPrototypeScript::Compile(const PRUnichar* aText, PRInt32 aTextLength, // and the first document would indirectly reference the prototype // document because it keeps the prototype cache // alive. Circularity! - void * scopeObject; - nsCOMPtr owner= do_QueryInterface(aPrototypeDocument, &rv); - if (NS_FAILED(rv)) return rv; + nsresult rv; + + // Use the prototype document's special context + nsCOMPtr context; + + { + nsCOMPtr global = do_QueryInterface(aPrototypeDocument); + NS_ASSERTION(global != nsnull, "prototype doc is not a script global"); + if (! global) + return NS_ERROR_UNEXPECTED; + + rv = global->GetContext(getter_AddRefs(context)); + if (NS_FAILED(rv)) return rv; + } + + NS_ASSERTION(context != nsnull, "no context for script global"); + if (! context) + return NS_ERROR_UNEXPECTED; + + // Use the prototype script's special scope object + JSObject* scopeObject; + + { + nsCOMPtr owner = do_QueryInterface(aPrototypeDocument); + if (! owner) + return NS_ERROR_UNEXPECTED; - rv = owner->GetScriptObject(context, &scopeObject); + rv = owner->GetScriptObject(context, (void**) &scopeObject); + if (NS_FAILED(rv)) return rv; + } + + // Use the enclosing document's principal + // XXX is this right? or should we use the protodoc's? + nsCOMPtr principal = + dont_AddRef(aDocument->GetDocumentPrincipal()); + + nsXPIDLCString urlspec; + aURI->GetSpec(getter_Copies(urlspec)); + + // Ok, compile it to create a prototype script object! + rv = context->CompileScript(aText, + aTextLength, + scopeObject, + principal, + urlspec, + aLineNo, + mLangVersion, + (void**) &mScriptObject); + if (NS_FAILED(rv)) return rv; - rv = context->CompileScript(aText, aTextLength, (JSObject *)scopeObject, - principal, urlspec, aLineNo, mLangVersion, - (void**) &mScriptObject); - if (NS_FAILED(rv)) return rv; + // Root the compiled prototype script object. + JSContext* cx = NS_STATIC_CAST(JSContext*, context->GetNativeContext()); + if (!cx) + return NS_ERROR_UNEXPECTED; rv = AddJSGCRoot(cx, &mScriptObject, "nsXULPrototypeScript::mScriptObject"); return rv; diff --git a/rdf/content/src/nsXULElement.h b/rdf/content/src/nsXULElement.h index ccd95aacfcab..e9268adf4363 100644 --- a/rdf/content/src/nsXULElement.h +++ b/rdf/content/src/nsXULElement.h @@ -463,8 +463,12 @@ public: NS_IMETHOD SetScriptObject(void *aScriptObject); // nsIScriptEventHandlerOwner + NS_IMETHOD CompileEventHandler(nsIScriptContext* aContext, + void* aTarget, + nsIAtom *aName, + const nsString& aBody, + void** aHandler); NS_IMETHOD GetCompiledEventHandler(nsIAtom *aName, void** aHandler); - NS_IMETHOD SetCompiledEventHandler(nsIAtom *aName, void* aHandler); // nsIJSScriptObject virtual PRBool AddProperty(JSContext *aContext, JSObject *aObj, diff --git a/rdf/content/src/nsXULKeyListener.cpp b/rdf/content/src/nsXULKeyListener.cpp index 369f44ffbd18..552ffc27ddd5 100644 --- a/rdf/content/src/nsXULKeyListener.cpp +++ b/rdf/content/src/nsXULKeyListener.cpp @@ -403,7 +403,6 @@ nsXULKeyListenerImpl::Init( nsIDOMElement * aElement, nsIDOMDocument * aDocument) { - printf("nsXULKeyListenerImpl::Init()\n"); element = aElement; // Weak reference. Don't addref it. nsCOMPtr xulDoc = do_QueryInterface(aDocument); @@ -1552,12 +1551,14 @@ nsXULKeyListenerImpl::HandleEventUsingKeyset(nsIDOMElement* aKeysetElement, nsID nsAutoString value; keyContent->GetAttribute(kNameSpaceID_None, eventName, value); if (value != "") { - context->CompileEventHandler(scriptObject, eventName, value, - PR_TRUE, &handler); + if (handlerOwner) { + handlerOwner->CompileEventHandler(context, scriptObject, eventName, value, &handler); + } + else { + context->CompileEventHandler(scriptObject, eventName, value, + PR_TRUE, &handler); + } } - - if (handler) - handlerOwner->SetCompiledEventHandler(eventName, handler); } if (handler) { diff --git a/rdf/content/src/nsXULPrototypeDocument.cpp b/rdf/content/src/nsXULPrototypeDocument.cpp index b83a2257de96..f5188177a5b1 100644 --- a/rdf/content/src/nsXULPrototypeDocument.cpp +++ b/rdf/content/src/nsXULPrototypeDocument.cpp @@ -27,19 +27,23 @@ */ #include "nsCOMPtr.h" -#include "nsString2.h" -#include "nsVoidArray.h" #include "nsIPrincipal.h" +#include "nsIScriptGlobalObject.h" +#include "nsIScriptGlobalObjectData.h" +#include "nsIScriptSecurityManager.h" +#include "nsIServiceManager.h" #include "nsISupportsArray.h" #include "nsIURI.h" -#include "nsIServiceManager.h" -#include "nsIScriptSecurityManager.h" #include "nsIXULPrototypeDocument.h" +#include "nsString2.h" +#include "nsVoidArray.h" #include "nsXULElement.h" -#include "nsIJSRuntimeService.h" + class nsXULPrototypeDocument : public nsIXULPrototypeDocument, - public nsIScriptObjectOwner + public nsIScriptObjectOwner, + public nsIScriptGlobalObject, + public nsIScriptGlobalObjectData { public: static nsresult @@ -71,12 +75,33 @@ public: NS_IMETHOD GetScriptObject(nsIScriptContext *aContext, void **aObject); NS_IMETHOD SetScriptObject(void *aObject); + // nsIScriptGlobalObject methods + NS_IMETHOD SetContext(nsIScriptContext *aContext); + NS_IMETHOD GetContext(nsIScriptContext **aContext); + NS_IMETHOD SetNewDocument(nsIDOMDocument *aDocument); + NS_IMETHOD SetWebShell(nsIWebShell *aWebShell); + NS_IMETHOD GetWebShell(nsIWebShell **aWebShell); + NS_IMETHOD SetOpenerWindow(nsIDOMWindow *aOpener); + NS_IMETHOD SetGlobalObjectOwner(nsIScriptGlobalObjectOwner* aOwner); + NS_IMETHOD GetGlobalObjectOwner(nsIScriptGlobalObjectOwner** aOwner); + NS_IMETHOD HandleDOMEvent(nsIPresContext* aPresContext, + nsEvent* aEvent, + nsIDOMEvent** aDOMEvent, + PRUint32 aFlags, + nsEventStatus* aEventStatus); + + // nsIScriptGlobalObjectData methods + NS_IMETHOD GetPrincipal(nsIPrincipal** aPrincipal); + + protected: nsCOMPtr mURI; nsXULPrototypeElement* mRoot; nsCOMPtr mStyleSheetReferences; nsCOMPtr mOverlayReferences; nsCOMPtr mDocumentPrincipal; + + nsCOMPtr mScriptContext; JSObject *mScriptObject; // XXX JS language rabies bigotry badness nsXULPrototypeDocument(); @@ -85,13 +110,30 @@ protected: friend NS_IMETHODIMP NS_NewXULPrototypeDocument(nsISupports* aOuter, REFNSIID aIID, void** aResult); + + static JSClass gSharedGlobalClass; + + static void PR_CALLBACK + FinalizeScriptObject(JSContext* cx, JSObject* obj); +}; + +JSClass nsXULPrototypeDocument::gSharedGlobalClass = { + "nsXULPrototypeScript compilation scope", + JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, nsXULPrototypeDocument::FinalizeScriptObject }; + //---------------------------------------------------------------------- +// +// ctors, dtors, n' stuff +// nsXULPrototypeDocument::nsXULPrototypeDocument() - : mRoot(nsnull), mScriptObject(nsnull) + : mRoot(nsnull), + mScriptObject(nsnull) { NS_INIT_REFCNT(); } @@ -115,19 +157,14 @@ nsXULPrototypeDocument::Init() nsXULPrototypeDocument::~nsXULPrototypeDocument() { delete mRoot; - - nsresult rv; - NS_WITH_SERVICE(nsIJSRuntimeService, rtsvc, "nsJSRuntimeService", &rv); - if (NS_SUCCEEDED(rv)) { - JSRuntime *rt; - rv = rtsvc->GetRuntime(&rt); - if (NS_SUCCEEDED(rv) && rt) - JS_RemoveRootRT(rt, &mScriptObject); - } } -NS_IMPL_ISUPPORTS2(nsXULPrototypeDocument, nsIXULPrototypeDocument, nsIScriptObjectOwner); +NS_IMPL_ISUPPORTS4(nsXULPrototypeDocument, + nsIXULPrototypeDocument, + nsIScriptObjectOwner, + nsIScriptGlobalObject, + nsIScriptGlobalObjectData); NS_IMETHODIMP NS_NewXULPrototypeDocument(nsISupports* aOuter, REFNSIID aIID, void** aResult) @@ -156,6 +193,9 @@ NS_NewXULPrototypeDocument(nsISupports* aOuter, REFNSIID aIID, void** aResult) //---------------------------------------------------------------------- +// +// nsIXULPrototypeDocument methods +// NS_IMETHODIMP nsXULPrototypeDocument::GetURI(nsIURI** aResult) @@ -169,7 +209,7 @@ nsXULPrototypeDocument::GetURI(nsIURI** aResult) NS_IMETHODIMP nsXULPrototypeDocument::SetURI(nsIURI* aURI) { - mURI = aURI; + mURI = dont_QueryInterface(aURI); return NS_OK; } @@ -257,14 +297,20 @@ nsXULPrototypeDocument::GetDocumentPrincipal(nsIPrincipal** aResult) { if (!mDocumentPrincipal) { nsresult rv; - NS_WITH_SERVICE(nsIScriptSecurityManager, securityManager, - NS_SCRIPTSECURITYMANAGER_PROGID, &rv); + NS_WITH_SERVICE(nsIScriptSecurityManager, + securityManager, + NS_SCRIPTSECURITYMANAGER_PROGID, + &rv); + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; + rv = securityManager->GetCodebasePrincipal(mURI, getter_AddRefs(mDocumentPrincipal)); + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; } + *aResult = mDocumentPrincipal; NS_ADDREF(*aResult); return NS_OK; @@ -278,40 +324,43 @@ nsXULPrototypeDocument::SetDocumentPrincipal(nsIPrincipal* aPrincipal) return NS_OK; } -JSClass null_class = { - "nsXULPrototypeScript compilation scope", 0, - JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, - JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub -}; - //---------------------------------------------------------------------- +// +// nsIScriptObjectOwner methods +// NS_IMETHODIMP nsXULPrototypeDocument::GetScriptObject(nsIScriptContext *aContext, void **aObject) { - // The prototype document will have its own special secret script - // object that can be used to compile scripts and event handlers. - if (!mScriptObject) { - JSContext *cx = (JSContext *)aContext->GetNativeContext(); - if (!cx) - return NS_ERROR_UNEXPECTED; + // The prototype document has its own special secret script object + // that can be used to compile scripts and event handlers. + nsresult rv; - mScriptObject = JS_NewObject(cx, &null_class, nsnull, nsnull); - if (!mScriptObject) + nsCOMPtr context; + + if (mScriptContext && aContext != mScriptContext.get()) { + rv = GetContext(getter_AddRefs(context)); + if (NS_FAILED(rv)) return rv; + } + else { + context = aContext; + } + + if (! mScriptObject) { + JSContext* cx = NS_STATIC_CAST(JSContext*, context->GetNativeContext()); + if (! cx) return NS_ERROR_OUT_OF_MEMORY; - // Be sure to unlink the script object from the parent global - // object. This ensures that we don't end up with a circular - // reference back to the first document. - JS_SetPrototype(cx, mScriptObject, nsnull); - JS_SetParent(cx, mScriptObject, nsnull); + mScriptObject = JS_NewObject(cx, &gSharedGlobalClass, nsnull, nsnull); + if (! mScriptObject) + return NS_ERROR_OUT_OF_MEMORY; - JS_AddNamedRoot(cx, &mScriptObject, "nsXULPrototypeDocument::mScriptObject"); - - // We need standard classes, in particular RegExp, to compile JS. - if (!JS_InitStandardClasses(cx, mScriptObject)) - return NS_ERROR_FAILURE; + // Add an owning reference from JS back to us. This'll be + // released when the JSObject is finalized. + ::JS_SetPrivate(cx, mScriptObject, this); + NS_ADDREF(this); } + *aObject = mScriptObject; return NS_OK; } @@ -322,3 +371,120 @@ nsXULPrototypeDocument::SetScriptObject(void *aObject) mScriptObject = (JSObject *)aObject; return NS_OK; } + +//---------------------------------------------------------------------- +// +// nsIScriptGlobalObject methods +// + +NS_IMETHODIMP +nsXULPrototypeDocument::SetContext(nsIScriptContext *aContext) +{ + mScriptContext = aContext; + return NS_OK; +} + + +NS_IMETHODIMP +nsXULPrototypeDocument::GetContext(nsIScriptContext **aContext) +{ + if (! mScriptContext) { + nsresult rv; + rv = NS_CreateScriptContext(this, getter_AddRefs(mScriptContext)); + if (NS_FAILED(rv)) return rv; + } + + *aContext = mScriptContext; + NS_IF_ADDREF(*aContext); + return NS_OK; +} + + +NS_IMETHODIMP +nsXULPrototypeDocument::SetNewDocument(nsIDOMDocument *aDocument) +{ + NS_NOTREACHED("waaah!"); + return NS_ERROR_UNEXPECTED; +} + + +NS_IMETHODIMP +nsXULPrototypeDocument::SetWebShell(nsIWebShell *aWebShell) +{ + NS_NOTREACHED("waaah!"); + return NS_ERROR_UNEXPECTED; +} + + +NS_IMETHODIMP +nsXULPrototypeDocument::GetWebShell(nsIWebShell **aWebShell) +{ + NS_NOTREACHED("waaah!"); + return NS_ERROR_UNEXPECTED; +} + + +NS_IMETHODIMP +nsXULPrototypeDocument::SetOpenerWindow(nsIDOMWindow *aOpener) +{ + NS_NOTREACHED("waaah!"); + return NS_ERROR_UNEXPECTED; +} + + +NS_IMETHODIMP +nsXULPrototypeDocument::SetGlobalObjectOwner(nsIScriptGlobalObjectOwner* aOwner) +{ + NS_NOTREACHED("waaah!"); + return NS_ERROR_UNEXPECTED; +} + + +NS_IMETHODIMP +nsXULPrototypeDocument::GetGlobalObjectOwner(nsIScriptGlobalObjectOwner** aOwner) +{ + NS_NOTREACHED("waaah!"); + return NS_ERROR_UNEXPECTED; +} + + +NS_IMETHODIMP +nsXULPrototypeDocument::HandleDOMEvent(nsIPresContext* aPresContext, + nsEvent* aEvent, + nsIDOMEvent** aDOMEvent, + PRUint32 aFlags, + nsEventStatus* aEventStatus) +{ + NS_NOTREACHED("waaah!"); + return NS_ERROR_UNEXPECTED; +} + +//---------------------------------------------------------------------- +// +// nsIScriptGlobalObjectData methods +// + +NS_IMETHODIMP +nsXULPrototypeDocument::GetPrincipal(nsIPrincipal** aPrincipal) +{ + return GetDocumentPrincipal(aPrincipal); +} + +//---------------------------------------------------------------------- +// +// Implementation methods +// + +void PR_CALLBACK +nsXULPrototypeDocument::FinalizeScriptObject(JSContext* cx, JSObject* obj) +{ + nsXULPrototypeDocument* native = + NS_STATIC_CAST(nsXULPrototypeDocument*, ::JS_GetPrivate(cx, obj)); + + if (native) { + // Clear the native object's reference back to us, and release + // our ownership of it. + native->SetScriptObject(nsnull); + NS_RELEASE(native); + } +}