diff --git a/dom/public/nsIScriptContext.h b/dom/public/nsIScriptContext.h index 2869a48f4c9b..a8ea3f618b23 100644 --- a/dom/public/nsIScriptContext.h +++ b/dom/public/nsIScriptContext.h @@ -77,6 +77,15 @@ public: nsString& aRetValue, PRBool* aIsUndefined) = 0; + NS_IMETHOD EvaluateStringWithValue(const nsString& aScript, + void *aScopeObject, + nsIPrincipal *aPrincipal, + const char *aURL, + PRUint32 aLineNo, + const char* aVersion, + void* aRetValue, + PRBool* aIsUndefined) = 0; + /** * Compile a script. * @@ -187,6 +196,17 @@ public: nsIAtom* aName, void* aHandler) = 0; + NS_IMETHOD CompileFunction(void* aTarget, + const nsCString& aName, + PRUint32 aArgCount, + const char** aArgArray, + const nsString& aBody, + const char* aURL, + PRUint32 aLineNo, + PRBool aShared, + void** aFunctionObject) = 0; + + /** * Set the default scripting language version for this context, which must * be a context specific to a particular scripting language. diff --git a/dom/src/base/nsJSEnvironment.cpp b/dom/src/base/nsJSEnvironment.cpp index 7028f7946da9..f4b58eb699af 100644 --- a/dom/src/base/nsJSEnvironment.cpp +++ b/dom/src/base/nsJSEnvironment.cpp @@ -194,6 +194,123 @@ nsJSContext::~nsJSContext() NS_IMPL_ISUPPORTS(nsJSContext, NS_GET_IID(nsIScriptContext)); +NS_IMETHODIMP +nsJSContext::EvaluateStringWithValue(const nsString& aScript, + void *aScopeObject, + nsIPrincipal *aPrincipal, + const char *aURL, + PRUint32 aLineNo, + const char* aVersion, + void* aRetValue, + PRBool* aIsUndefined) +{ + if (!mScriptsEnabled) { + *aIsUndefined = PR_TRUE; + return NS_OK; + } + + nsresult rv; + if (!aScopeObject) + aScopeObject = ::JS_GetGlobalObject(mContext); + + // Safety first: get an object representing the script's principals, i.e., + // the entities who signed this script, or the fully-qualified-domain-name + // or "codebase" from which it was loaded. + JSPrincipals *jsprin; + nsCOMPtr principal = aPrincipal; + if (aPrincipal) { + aPrincipal->GetJSPrincipals(&jsprin); + } + else { + nsCOMPtr global = dont_AddRef(GetGlobalObject()); + if (!global) + return NS_ERROR_FAILURE; + nsCOMPtr objPrincipal = do_QueryInterface(global, &rv); + if (NS_FAILED(rv)) + return NS_ERROR_FAILURE; + rv = objPrincipal->GetPrincipal(getter_AddRefs(principal)); + if (NS_FAILED(rv)) + return NS_ERROR_FAILURE; + principal->GetJSPrincipals(&jsprin); + } + // From here on, we must JSPRINCIPALS_DROP(jsprin) before returning... + + PRBool ok = PR_FALSE; + nsCOMPtr securityManager; + rv = GetSecurityManager(getter_AddRefs(securityManager)); + if (NS_SUCCEEDED(rv)) + rv = securityManager->CanExecuteScripts(principal, &ok); + if (NS_FAILED(rv)) { + JSPRINCIPALS_DROP(mContext, jsprin); + return NS_ERROR_FAILURE; + } + + // Push our JSContext on the current thread's context stack so JS called + // from native code via XPConnect uses the right context. Do this whether + // or not the SecurityManager said "ok", in order to simplify control flow + // below where we pop before returning. + NS_WITH_SERVICE(nsIJSContextStack, stack, "nsThreadJSContextStack", &rv); + if (NS_FAILED(rv) || NS_FAILED(stack->Push(mContext))) { + JSPRINCIPALS_DROP(mContext, jsprin); + 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. + jsval val; + + if (ok) { + JSVersion newVersion; + + // SecurityManager said "ok", but don't execute if aVersion is specified + // and unknown. Do execute with the default version (and avoid thrashing + // the context's version) if aVersion is not specified. + ok = (!aVersion || + (newVersion = ::JS_StringToVersion(aVersion)) != JSVERSION_UNKNOWN); + if (ok) { + JSVersion oldVersion; + + if (aVersion) + oldVersion = ::JS_SetVersion(mContext, newVersion); + mRef = nsnull; + mTerminationFunc = nsnull; + ok = ::JS_EvaluateUCScriptForPrincipals(mContext, + (JSObject *)aScopeObject, + jsprin, + (jschar*)aScript.GetUnicode(), + aScript.Length(), + aURL, + aLineNo, + &val); + if (aVersion) + ::JS_SetVersion(mContext, oldVersion); + } + } + + // Whew! Finally done with these manually ref-counted things. + JSPRINCIPALS_DROP(mContext, jsprin); + + // If all went well, convert val to a string (XXXbe unless undefined?). + if (ok) { + if (aIsUndefined) *aIsUndefined = JSVAL_IS_VOID(val); + *NS_STATIC_CAST(jsval*, aRetValue) = val; + } + else { + if (aIsUndefined) *aIsUndefined = PR_TRUE; + } + + ScriptEvaluated(); + + // Pop here, after JS_ValueToString and any other possible evaluation. + if (NS_FAILED(stack->Pop(nsnull))) + rv = NS_ERROR_FAILURE; + + return rv; + +} + NS_IMETHODIMP nsJSContext::EvaluateString(const nsString& aScript, void *aScopeObject, @@ -524,6 +641,56 @@ nsJSContext::CompileEventHandler(void *aTarget, nsIAtom *aName, const nsString& return NS_OK; } +NS_IMETHODIMP +nsJSContext::CompileFunction(void* aTarget, + const nsCString& aName, + PRUint32 aArgCount, + const char** aArgArray, + const nsString& aBody, + const char* aURL, + PRUint32 aLineNo, + PRBool aShared, + void** aFunctionObject) +{ + JSPrincipals *jsprin = nsnull; + + nsCOMPtr global = getter_AddRefs(GetGlobalObject()); + if (global) { + // XXXbe why the two-step QI? speed up via a new GetGlobalObjectData func? + nsCOMPtr globalData = do_QueryInterface(global); + if (globalData) { + nsCOMPtr prin; + if (NS_FAILED(globalData->GetPrincipal(getter_AddRefs(prin)))) + return NS_ERROR_FAILURE; + prin->GetJSPrincipals(&jsprin); + } + } + + JSObject *target = (JSObject*)aTarget; + JSFunction* fun = + ::JS_CompileUCFunctionForPrincipals(mContext, target, jsprin, + aName, aArgCount, aArgArray, + (jschar*)aBody.GetUnicode(), + aBody.Length(), + aURL, aLineNo); + + if (jsprin) + JSPRINCIPALS_DROP(mContext, jsprin); + if (!fun) + return NS_ERROR_FAILURE; + + JSObject *handler = ::JS_GetFunctionObject(fun); + if (aFunctionObject) + *aFunctionObject = (void*) handler; + + // Prevent entraining just like CompileEventHandler does? + if (aShared) { + /* Break scope link to avoid entraining shared compilation scope. */ + ::JS_SetParent(mContext, handler, nsnull); + } + return NS_OK; +} + NS_IMETHODIMP nsJSContext::CallEventHandler(void *aTarget, void *aHandler, PRUint32 argc, void *argv, PRBool *aBoolResult) diff --git a/dom/src/base/nsJSEnvironment.h b/dom/src/base/nsJSEnvironment.h index 0f4e1c427d72..038458176c3c 100644 --- a/dom/src/base/nsJSEnvironment.h +++ b/dom/src/base/nsJSEnvironment.h @@ -62,6 +62,15 @@ public: const char* aVersion, nsString& aRetValue, PRBool* aIsUndefined); + NS_IMETHOD EvaluateStringWithValue(const nsString& aScript, + void *aScopeObject, + nsIPrincipal *aPrincipal, + const char *aURL, + PRUint32 aLineNo, + const char* aVersion, + void* aRetValue, + PRBool* aIsUndefined); + NS_IMETHOD CompileScript(const PRUnichar* aText, PRInt32 aTextLength, void *aScopeObject, @@ -85,6 +94,16 @@ public: NS_IMETHOD BindCompiledEventHandler(void *aTarget, nsIAtom *aName, void *aHandler); + NS_IMETHOD CompileFunction(void* aTarget, + const nsCString& aName, + PRUint32 aArgCount, + const char** aArgArray, + const nsString& aBody, + const char* aURL, + PRUint32 aLineNo, + PRBool aShared, + void** aFunctionObject); + NS_IMETHOD SetDefaultLanguageVersion(const char* aVersion); NS_IMETHOD_(nsIScriptGlobalObject*) GetGlobalObject();