diff --git a/mozilla/content/html/document/src/nsHTMLDocument.cpp b/mozilla/content/html/document/src/nsHTMLDocument.cpp
index b4f69f30236..f8202fc914e 100644
--- a/mozilla/content/html/document/src/nsHTMLDocument.cpp
+++ b/mozilla/content/html/document/src/nsHTMLDocument.cpp
@@ -965,11 +965,16 @@ nsHTMLDocument::EndLoad()
// document.write("foo");
// location.href = "http://www.mozilla.org";
// document.write("bar");
-
- scx->SetTerminationFunction(DocumentWriteTerminationFunc,
- NS_STATIC_CAST(nsIDocument *, this));
-
- return;
+
+ nsresult rv =
+ scx->SetTerminationFunction(DocumentWriteTerminationFunc,
+ NS_STATIC_CAST(nsIDocument *, this));
+ // If we fail to set the termination function, just go ahead
+ // and EndLoad now. The slight bugginess involved is better
+ // than leaking.
+ if (NS_SUCCEEDED(rv)) {
+ return;
+ }
}
}
}
diff --git a/mozilla/content/xbl/src/nsXBLProtoImpl.cpp b/mozilla/content/xbl/src/nsXBLProtoImpl.cpp
index d67005bb810..31013416fa7 100644
--- a/mozilla/content/xbl/src/nsXBLProtoImpl.cpp
+++ b/mozilla/content/xbl/src/nsXBLProtoImpl.cpp
@@ -76,12 +76,15 @@ nsXBLProtoImpl::InstallImplementation(nsXBLPrototypeBinding* aBinding, nsIConten
// class object in the bound document that represents the concrete version of this implementation.
// This function also has the side effect of building up the prototype implementation if it has
// not been built already.
- void * targetScriptObject = nsnull;
+ nsCOMPtr holder;
void * targetClassObject = nsnull;
nsresult rv = InitTargetObjects(aBinding, context, aBoundElement,
- &targetScriptObject, &targetClassObject);
+ getter_AddRefs(holder), &targetClassObject);
NS_ENSURE_SUCCESS(rv, rv); // kick out if we were unable to properly intialize our target objects
+ JSObject * targetScriptObject;
+ holder->GetJSObject(&targetScriptObject);
+
// Walk our member list and install each one in turn.
for (nsXBLProtoImplMember* curr = mMembers;
curr;
@@ -95,10 +98,12 @@ nsresult
nsXBLProtoImpl::InitTargetObjects(nsXBLPrototypeBinding* aBinding,
nsIScriptContext* aContext,
nsIContent* aBoundElement,
- void** aScriptObject,
+ nsIXPConnectJSObjectHolder** aScriptObjectHolder,
void** aTargetClassObject)
{
nsresult rv = NS_OK;
+ *aScriptObjectHolder = nsnull;
+
if (!mClassObject) {
rv = CompilePrototypeMembers(aBinding); // This is the first time we've ever installed this binding on an element.
// We need to go ahead and compile all methods and properties on a class
@@ -128,7 +133,6 @@ nsXBLProtoImpl::InitTargetObjects(nsXBLPrototypeBinding* aBinding,
// concrete base class. We need to alter the object so that our concrete class is interposed
// between the object and its base class. We become the new base class of the object, and the
// object's old base class becomes the new class' base class.
- *aScriptObject = object;
rv = aBinding->InitClass(mClassName, aContext, (void *) object, aTargetClassObject);
if (NS_FAILED(rv))
return rv;
@@ -142,6 +146,8 @@ nsXBLProtoImpl::InitTargetObjects(nsXBLPrototypeBinding* aBinding,
NS_DOMClassInfo_PreserveWrapper(node, nativeWrapper);
}
}
+
+ wrapper.swap(*aScriptObjectHolder);
return rv;
}
diff --git a/mozilla/content/xbl/src/nsXBLProtoImpl.h b/mozilla/content/xbl/src/nsXBLProtoImpl.h
index 10998ba342a..c861abcadb5 100644
--- a/mozilla/content/xbl/src/nsXBLProtoImpl.h
+++ b/mozilla/content/xbl/src/nsXBLProtoImpl.h
@@ -44,6 +44,8 @@
#include "nsXBLProtoImplMember.h"
#include "nsXBLPrototypeBinding.h"
+class nsIXPConnectJSObjectHolder;
+
MOZ_DECL_CTOR_COUNTER(nsXBLProtoImpl)
class nsXBLProtoImpl
@@ -70,7 +72,8 @@ public:
nsresult InstallImplementation(nsXBLPrototypeBinding* aBinding, nsIContent* aBoundElement);
nsresult InitTargetObjects(nsXBLPrototypeBinding* aBinding, nsIScriptContext* aContext,
nsIContent* aBoundElement,
- void** aScriptObject, void** aTargetClassObject);
+ nsIXPConnectJSObjectHolder** aScriptObjectHolder,
+ void** aTargetClassObject);
nsresult CompilePrototypeMembers(nsXBLPrototypeBinding* aBinding);
void SetMemberList(nsXBLProtoImplMember* aMemberList) { delete mMembers; mMembers = aMemberList; };
diff --git a/mozilla/dom/public/nsIScriptContext.h b/mozilla/dom/public/nsIScriptContext.h
index ef247bd2e0e..3d4ec745145 100644
--- a/mozilla/dom/public/nsIScriptContext.h
+++ b/mozilla/dom/public/nsIScriptContext.h
@@ -310,11 +310,13 @@ public:
/**
* Called to specify a function that should be called when the current
* script (if there is one) terminates. Generally used if breakdown
- * of script state needs to be happen, but should be deferred till
+ * of script state needs to happen, but should be deferred till
* the end of script evaluation.
+ *
+ * @throws NS_ERROR_OUT_OF_MEMORY if that happens
*/
- virtual void SetTerminationFunction(nsScriptTerminationFunc aFunc,
- nsISupports* aRef) = 0;
+ virtual nsresult SetTerminationFunction(nsScriptTerminationFunc aFunc,
+ nsISupports* aRef) = 0;
/**
* Called to disable/enable script execution in this context.
diff --git a/mozilla/dom/src/base/nsGlobalWindow.cpp b/mozilla/dom/src/base/nsGlobalWindow.cpp
index 801de2733ab..259e913dd84 100644
--- a/mozilla/dom/src/base/nsGlobalWindow.cpp
+++ b/mozilla/dom/src/base/nsGlobalWindow.cpp
@@ -3595,6 +3595,10 @@ nsGlobalWindow::Close()
nsIScriptContext *currentCX = nsJSUtils::GetDynamicScriptContext(cx);
if (currentCX && currentCX == mContext) {
+ // We ignore the return value here. If setting the termination function
+ // fails, it's better to fail to close the window than it is to crash
+ // (which is what would tend to happen if we did this synchronously
+ // here).
currentCX->SetTerminationFunction(CloseWindow,
NS_STATIC_CAST(nsIDOMWindow *,
this));
diff --git a/mozilla/dom/src/base/nsJSEnvironment.cpp b/mozilla/dom/src/base/nsJSEnvironment.cpp
index 3aa99fea428..589bebc9b61 100644
--- a/mozilla/dom/src/base/nsJSEnvironment.cpp
+++ b/mozilla/dom/src/base/nsJSEnvironment.cpp
@@ -713,7 +713,7 @@ nsJSContext::nsJSContext(JSRuntime *aRuntime) : mGCOnDestruction(PR_TRUE)
mIsInitialized = PR_FALSE;
mNumEvaluations = 0;
mOwner = nsnull;
- mTerminationFunc = nsnull;
+ mTerminations = nsnull;
mScriptsEnabled = PR_TRUE;
mBranchCallbackCount = 0;
mBranchCallbackTime = LL_ZERO;
@@ -724,6 +724,8 @@ nsJSContext::nsJSContext(JSRuntime *aRuntime) : mGCOnDestruction(PR_TRUE)
nsJSContext::~nsJSContext()
{
+ NS_PRECONDITION(!mTerminations, "Shouldn't have termination funcs by now");
+
// Cope with JS_NewContext failure in ctor (XXXbe move NewContext to Init?)
if (!mContext)
return;
@@ -845,12 +847,12 @@ nsJSContext::EvaluateStringWithValue(const nsAString& aScript,
return NS_ERROR_FAILURE;
}
- // The result of evaluation, used only if there were no errors. This need
- // not be a GC root currently, provided we run the GC only from the branch
- // callback or from ScriptEvaluated. TODO: use JS_Begin/EndRequest to keep
- // the GC from racing with JS execution on any thread.
+ // The result of evaluation, used only if there were no errors. TODO: use
+ // JS_Begin/EndRequest to keep the GC from racing with JS execution on any
+ // thread.
jsval val;
+ nsJSContext::TerminationFuncHolder holder(this);
if (ok) {
JSVersion newVersion = JSVERSION_UNKNOWN;
@@ -864,8 +866,6 @@ nsJSContext::EvaluateStringWithValue(const nsAString& aScript,
if (aVersion)
oldVersion = ::JS_SetVersion(mContext, newVersion);
- mTerminationFuncArg = nsnull;
- mTerminationFunc = nsnull;
ok = ::JS_EvaluateUCScriptForPrincipals(mContext,
(JSObject *)aScopeObject,
jsprin,
@@ -910,6 +910,22 @@ nsJSContext::EvaluateStringWithValue(const nsAString& aScript,
if (NS_FAILED(stack->Pop(nsnull)))
rv = NS_ERROR_FAILURE;
+ // Need to lock, since ScriptEvaluated can GC.
+ PRBool locked = PR_FALSE;
+ if (ok && JSVAL_IS_GCTHING(val)) {
+ locked = ::JS_LockGCThing(mContext, JSVAL_TO_GCTHING(val));
+ if (!locked) {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ // ScriptEvaluated needs to come after we pop the stack
+ ScriptEvaluated(PR_TRUE);
+
+ if (locked) {
+ ::JS_UnlockGCThing(mContext, JSVAL_TO_GCTHING(val));
+ }
+
return rv;
}
@@ -1031,6 +1047,7 @@ nsJSContext::EvaluateString(const nsAString& aScript,
// the GC from racing with JS execution on any thread.
jsval val;
+ nsJSContext::TerminationFuncHolder holder(this);
if (ok) {
JSVersion newVersion = JSVERSION_UNKNOWN;
@@ -1044,8 +1061,6 @@ nsJSContext::EvaluateString(const nsAString& aScript,
if (aVersion)
oldVersion = ::JS_SetVersion(mContext, newVersion);
- mTerminationFuncArg = nsnull;
- mTerminationFunc = nsnull;
ok = ::JS_EvaluateUCScriptForPrincipals(mContext,
(JSObject *)aScopeObject,
jsprin,
@@ -1086,12 +1101,13 @@ nsJSContext::EvaluateString(const nsAString& aScript,
}
}
- ScriptEvaluated(PR_TRUE);
-
// Pop here, after JS_ValueToString and any other possible evaluation.
if (NS_FAILED(stack->Pop(nsnull)))
rv = NS_ERROR_FAILURE;
+ // ScriptEvaluated needs to come after we pop the stack
+ ScriptEvaluated(PR_TRUE);
+
return rv;
}
@@ -1202,8 +1218,7 @@ nsJSContext::ExecuteScript(void* aScriptObject,
jsval val;
JSBool ok;
- mTerminationFuncArg = nsnull;
- mTerminationFunc = nsnull;
+ nsJSContext::TerminationFuncHolder holder(this);
ok = ::JS_ExecuteScript(mContext,
(JSObject*) aScopeObject,
(JSScript*) ::JS_GetPrivate(mContext,
@@ -1230,12 +1245,13 @@ nsJSContext::ExecuteScript(void* aScriptObject,
NotifyXPCIfExceptionPending(mContext);
}
- ScriptEvaluated(PR_TRUE);
-
// Pop here, after JS_ValueToString and any other possible evaluation.
if (NS_FAILED(stack->Pop(nsnull)))
rv = NS_ERROR_FAILURE;
+ // ScriptEvaluated needs to come after we pop the stack
+ ScriptEvaluated(PR_TRUE);
+
return rv;
}
@@ -1384,19 +1400,16 @@ nsJSContext::CallEventHandler(JSObject *aTarget, JSObject *aHandler,
if (NS_FAILED(rv) || NS_FAILED(stack->Push(mContext)))
return NS_ERROR_FAILURE;
- mTerminationFuncArg = nsnull;
- mTerminationFunc = nsnull;
-
// check if the event handler can be run on the object in question
rv = sSecurityManager->CheckFunctionAccess(mContext, aHandler, aTarget);
+ nsJSContext::TerminationFuncHolder holder(this);
+
if (NS_SUCCEEDED(rv)) {
jsval funval = OBJECT_TO_JSVAL(aHandler);
PRBool ok = ::JS_CallFunctionValue(mContext, aTarget, funval, argc, argv,
rval);
- ScriptEvaluated(PR_TRUE);
-
if (!ok) {
// Tell XPConnect about any pending exceptions. This is needed
// to avoid dropping JS exceptions in case we got here through
@@ -1415,6 +1428,22 @@ nsJSContext::CallEventHandler(JSObject *aTarget, JSObject *aHandler,
if (NS_FAILED(stack->Pop(nsnull)))
return NS_ERROR_FAILURE;
+ // Need to lock, since ScriptEvaluated can GC.
+ PRBool locked = PR_FALSE;
+ if (NS_SUCCEEDED(rv) && JSVAL_IS_GCTHING(*rval)) {
+ locked = ::JS_LockGCThing(mContext, JSVAL_TO_GCTHING(*rval));
+ if (!locked) {
+ rv = NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ // ScriptEvaluated needs to come after we pop the stack
+ ScriptEvaluated(PR_TRUE);
+
+ if (locked) {
+ ::JS_UnlockGCThing(mContext, JSVAL_TO_GCTHING(*rval));
+ }
+
return rv;
}
@@ -1905,10 +1934,18 @@ nsJSContext::GC()
void
nsJSContext::ScriptEvaluated(PRBool aTerminated)
{
- if (aTerminated && mTerminationFunc) {
- (*mTerminationFunc)(mTerminationFuncArg);
- mTerminationFuncArg = nsnull;
- mTerminationFunc = nsnull;
+ if (aTerminated && mTerminations) {
+ // Make sure to null out mTerminations before doing anything that
+ // might cause new termination funcs to be added!
+ nsJSContext::TerminationFuncClosure* start = mTerminations;
+ mTerminations = nsnull;
+
+ for (nsJSContext::TerminationFuncClosure* cur = start;
+ cur;
+ cur = cur->mNext) {
+ (*(cur->mTerminationFunc))(cur->mTerminationFuncArg);
+ }
+ delete start;
}
mNumEvaluations++;
@@ -1940,12 +1977,18 @@ nsJSContext::GetOwner()
return mOwner;
}
-void
+nsresult
nsJSContext::SetTerminationFunction(nsScriptTerminationFunc aFunc,
nsISupports* aRef)
{
- mTerminationFunc = aFunc;
- mTerminationFuncArg = aRef;
+ nsJSContext::TerminationFuncClosure* newClosure =
+ new nsJSContext::TerminationFuncClosure(aFunc, aRef, mTerminations);
+ if (!newClosure) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ mTerminations = newClosure;
+ return NS_OK;
}
PRBool
diff --git a/mozilla/dom/src/base/nsJSEnvironment.h b/mozilla/dom/src/base/nsJSEnvironment.h
index 91217b14a81..06c92d2f280 100644
--- a/mozilla/dom/src/base/nsJSEnvironment.h
+++ b/mozilla/dom/src/base/nsJSEnvironment.h
@@ -120,8 +120,8 @@ public:
virtual void ScriptEvaluated(PRBool aTerminated);
virtual void SetOwner(nsIScriptContextOwner* owner);
virtual nsIScriptContextOwner *GetOwner();
- virtual void SetTerminationFunction(nsScriptTerminationFunc aFunc,
- nsISupports* aRef);
+ virtual nsresult SetTerminationFunction(nsScriptTerminationFunc aFunc,
+ nsISupports* aRef);
virtual PRBool GetScriptsEnabled();
virtual void SetScriptsEnabled(PRBool aEnabled, PRBool aFireTimeouts);
@@ -146,10 +146,55 @@ private:
PRUint32 mNumEvaluations;
nsIScriptContextOwner* mOwner; /* NB: weak reference, not ADDREF'd */
- nsScriptTerminationFunc mTerminationFunc;
- nsCOMPtr mTerminationFuncArg;
+protected:
+ struct TerminationFuncClosure {
+ TerminationFuncClosure(nsScriptTerminationFunc aFunc,
+ nsISupports* aArg,
+ TerminationFuncClosure* aNext) :
+ mTerminationFunc(aFunc),
+ mTerminationFuncArg(aArg),
+ mNext(aNext)
+ {}
+ ~TerminationFuncClosure()
+ {
+ delete mNext;
+ }
+
+ nsScriptTerminationFunc mTerminationFunc;
+ nsCOMPtr mTerminationFuncArg;
+ TerminationFuncClosure* mNext;
+ };
+ struct TerminationFuncHolder {
+ TerminationFuncHolder(nsJSContext* aContext) :
+ mContext(aContext),
+ mTerminations(aContext->mTerminations)
+ {
+ aContext->mTerminations = nsnull;
+ }
+ ~TerminationFuncHolder() {
+ // Have to be careful here. mContext might have picked up new
+ // termination funcs while the script was evaluating. Prepend whatever
+ // we have to the current termination funcs on the context (since our
+ // termination funcs were posted first).
+ if (mTerminations) {
+ TerminationFuncClosure* cur = mTerminations;
+ while (cur->mNext) {
+ cur = cur->mNext;
+ }
+ cur->mNext = mContext->mTerminations;
+ mContext->mTerminations = mTerminations;
+ }
+ }
+
+ nsJSContext* mContext;
+ TerminationFuncClosure* mTerminations;
+ };
+
+ TerminationFuncClosure* mTerminations;
+
+private:
PRPackedBool mIsInitialized;
PRPackedBool mScriptsEnabled;
PRPackedBool mGCOnDestruction;