mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 11:25:00 +00:00
Bug 1095660 part 5. Change nsJSUtils::EvaluateString to take an explicit scope chain. r=bholley
This commit is contained in:
parent
d76620aecc
commit
e25768cbf9
@ -166,7 +166,7 @@ nsJSUtils::CompileFunction(AutoJSAPI& jsapi,
|
||||
nsresult
|
||||
nsJSUtils::EvaluateString(JSContext* aCx,
|
||||
const nsAString& aScript,
|
||||
JS::Handle<JSObject*> aScopeObject,
|
||||
JS::Handle<JSObject*> aEvaluationGlobal,
|
||||
JS::CompileOptions& aCompileOptions,
|
||||
const EvaluateOptions& aEvaluateOptions,
|
||||
JS::MutableHandle<JS::Value> aRetValue,
|
||||
@ -175,14 +175,14 @@ nsJSUtils::EvaluateString(JSContext* aCx,
|
||||
const nsPromiseFlatString& flatScript = PromiseFlatString(aScript);
|
||||
JS::SourceBufferHolder srcBuf(flatScript.get(), aScript.Length(),
|
||||
JS::SourceBufferHolder::NoOwnership);
|
||||
return EvaluateString(aCx, srcBuf, aScopeObject, aCompileOptions,
|
||||
return EvaluateString(aCx, srcBuf, aEvaluationGlobal, aCompileOptions,
|
||||
aEvaluateOptions, aRetValue, aOffThreadToken);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsJSUtils::EvaluateString(JSContext* aCx,
|
||||
JS::SourceBufferHolder& aSrcBuf,
|
||||
JS::Handle<JSObject*> aScopeObject,
|
||||
JS::Handle<JSObject*> aEvaluationGlobal,
|
||||
JS::CompileOptions& aCompileOptions,
|
||||
const EvaluateOptions& aEvaluateOptions,
|
||||
JS::MutableHandle<JS::Value> aRetValue,
|
||||
@ -197,6 +197,8 @@ nsJSUtils::EvaluateString(JSContext* aCx,
|
||||
MOZ_ASSERT_IF(!aEvaluateOptions.reportUncaught, aEvaluateOptions.needResult);
|
||||
MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext());
|
||||
MOZ_ASSERT(aSrcBuf.get());
|
||||
MOZ_ASSERT(js::GetGlobalForObjectCrossCompartment(aEvaluationGlobal) ==
|
||||
aEvaluationGlobal);
|
||||
|
||||
// Unfortunately, the JS engine actually compiles scripts with a return value
|
||||
// in a different, less efficient way. Furthermore, it can't JIT them in many
|
||||
@ -206,13 +208,11 @@ nsJSUtils::EvaluateString(JSContext* aCx,
|
||||
// set to false.
|
||||
aRetValue.setUndefined();
|
||||
|
||||
JS::ExposeObjectToActiveJS(aScopeObject);
|
||||
nsAutoMicroTask mt;
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
bool ok = false;
|
||||
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
|
||||
NS_ENSURE_TRUE(ssm->ScriptAllowed(js::GetGlobalForObjectCrossCompartment(aScopeObject)), NS_OK);
|
||||
NS_ENSURE_TRUE(ssm->ScriptAllowed(aEvaluationGlobal), NS_OK);
|
||||
|
||||
mozilla::Maybe<AutoDontReportUncaught> dontReport;
|
||||
if (!aEvaluateOptions.reportUncaught) {
|
||||
@ -221,32 +221,45 @@ nsJSUtils::EvaluateString(JSContext* aCx,
|
||||
dontReport.emplace(aCx);
|
||||
}
|
||||
|
||||
bool ok = true;
|
||||
// Scope the JSAutoCompartment so that we can later wrap the return value
|
||||
// into the caller's cx.
|
||||
{
|
||||
JSAutoCompartment ac(aCx, aScopeObject);
|
||||
JSAutoCompartment ac(aCx, aEvaluationGlobal);
|
||||
|
||||
JS::Rooted<JSObject*> rootedScope(aCx, aScopeObject);
|
||||
if (aOffThreadToken) {
|
||||
// Now make sure to wrap the scope chain into the right compartment.
|
||||
JS::AutoObjectVector scopeChain(aCx);
|
||||
if (!scopeChain.reserve(aEvaluateOptions.scopeChain.length())) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < aEvaluateOptions.scopeChain.length(); ++i) {
|
||||
JS::ExposeObjectToActiveJS(aEvaluateOptions.scopeChain[i]);
|
||||
scopeChain.infallibleAppend(aEvaluateOptions.scopeChain[i]);
|
||||
if (!JS_WrapObject(aCx, scopeChain[i])) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ok && aOffThreadToken) {
|
||||
JS::Rooted<JSScript*>
|
||||
script(aCx, JS::FinishOffThreadScript(aCx, JS_GetRuntime(aCx), *aOffThreadToken));
|
||||
*aOffThreadToken = nullptr; // Mark the token as having been finished.
|
||||
if (script) {
|
||||
if (aEvaluateOptions.needResult) {
|
||||
ok = JS_ExecuteScript(aCx, rootedScope, script, aRetValue);
|
||||
ok = JS_ExecuteScript(aCx, scopeChain, script, aRetValue);
|
||||
} else {
|
||||
ok = JS_ExecuteScript(aCx, rootedScope, script);
|
||||
ok = JS_ExecuteScript(aCx, scopeChain, script);
|
||||
}
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
} else {
|
||||
} else if (ok) {
|
||||
if (aEvaluateOptions.needResult) {
|
||||
ok = JS::Evaluate(aCx, rootedScope, aCompileOptions,
|
||||
aSrcBuf, aRetValue);
|
||||
ok = JS::Evaluate(aCx, scopeChain, aCompileOptions, aSrcBuf, aRetValue);
|
||||
} else {
|
||||
ok = JS::Evaluate(aCx, rootedScope, aCompileOptions,
|
||||
aSrcBuf);
|
||||
ok = JS::Evaluate(aCx, scopeChain, aCompileOptions, aSrcBuf);
|
||||
}
|
||||
}
|
||||
|
||||
@ -290,28 +303,28 @@ nsJSUtils::EvaluateString(JSContext* aCx,
|
||||
nsresult
|
||||
nsJSUtils::EvaluateString(JSContext* aCx,
|
||||
const nsAString& aScript,
|
||||
JS::Handle<JSObject*> aScopeObject,
|
||||
JS::Handle<JSObject*> aEvaluationGlobal,
|
||||
JS::CompileOptions& aCompileOptions,
|
||||
void **aOffThreadToken)
|
||||
{
|
||||
EvaluateOptions options;
|
||||
EvaluateOptions options(aCx);
|
||||
options.setNeedResult(false);
|
||||
JS::RootedValue unused(aCx);
|
||||
return EvaluateString(aCx, aScript, aScopeObject, aCompileOptions,
|
||||
return EvaluateString(aCx, aScript, aEvaluationGlobal, aCompileOptions,
|
||||
options, &unused, aOffThreadToken);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsJSUtils::EvaluateString(JSContext* aCx,
|
||||
JS::SourceBufferHolder& aSrcBuf,
|
||||
JS::Handle<JSObject*> aScopeObject,
|
||||
JS::Handle<JSObject*> aEvaluationGlobal,
|
||||
JS::CompileOptions& aCompileOptions,
|
||||
void **aOffThreadToken)
|
||||
{
|
||||
EvaluateOptions options;
|
||||
EvaluateOptions options(aCx);
|
||||
options.setNeedResult(false);
|
||||
JS::RootedValue unused(aCx);
|
||||
return EvaluateString(aCx, aSrcBuf, aScopeObject, aCompileOptions,
|
||||
return EvaluateString(aCx, aSrcBuf, aEvaluationGlobal, aCompileOptions,
|
||||
options, &unused, aOffThreadToken);
|
||||
}
|
||||
|
||||
|
@ -65,14 +65,17 @@ public:
|
||||
const nsAString& aBody,
|
||||
JSObject** aFunctionObject);
|
||||
|
||||
struct EvaluateOptions {
|
||||
struct MOZ_STACK_CLASS EvaluateOptions {
|
||||
bool coerceToString;
|
||||
bool reportUncaught;
|
||||
bool needResult;
|
||||
JS::AutoObjectVector scopeChain;
|
||||
|
||||
explicit EvaluateOptions() : coerceToString(false)
|
||||
, reportUncaught(true)
|
||||
, needResult(true)
|
||||
explicit EvaluateOptions(JSContext* cx)
|
||||
: coerceToString(false)
|
||||
, reportUncaught(true)
|
||||
, needResult(true)
|
||||
, scopeChain(cx)
|
||||
{}
|
||||
|
||||
EvaluateOptions& setCoerceToString(bool aCoerce) {
|
||||
@ -91,9 +94,12 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// aEvaluationGlobal is the global to evaluate in. The return value
|
||||
// will then be wrapped back into the compartment aCx is in when
|
||||
// this function is called.
|
||||
static nsresult EvaluateString(JSContext* aCx,
|
||||
const nsAString& aScript,
|
||||
JS::Handle<JSObject*> aScopeObject,
|
||||
JS::Handle<JSObject*> aEvaluationGlobal,
|
||||
JS::CompileOptions &aCompileOptions,
|
||||
const EvaluateOptions& aEvaluateOptions,
|
||||
JS::MutableHandle<JS::Value> aRetValue,
|
||||
@ -101,7 +107,7 @@ public:
|
||||
|
||||
static nsresult EvaluateString(JSContext* aCx,
|
||||
JS::SourceBufferHolder& aSrcBuf,
|
||||
JS::Handle<JSObject*> aScopeObject,
|
||||
JS::Handle<JSObject*> aEvaluationGlobal,
|
||||
JS::CompileOptions &aCompileOptions,
|
||||
const EvaluateOptions& aEvaluateOptions,
|
||||
JS::MutableHandle<JS::Value> aRetValue,
|
||||
@ -110,13 +116,13 @@ public:
|
||||
|
||||
static nsresult EvaluateString(JSContext* aCx,
|
||||
const nsAString& aScript,
|
||||
JS::Handle<JSObject*> aScopeObject,
|
||||
JS::Handle<JSObject*> aEvaluationGlobal,
|
||||
JS::CompileOptions &aCompileOptions,
|
||||
void **aOffThreadToken = nullptr);
|
||||
|
||||
static nsresult EvaluateString(JSContext* aCx,
|
||||
JS::SourceBufferHolder& aSrcBuf,
|
||||
JS::Handle<JSObject*> aScopeObject,
|
||||
JS::Handle<JSObject*> aEvaluationGlobal,
|
||||
JS::CompileOptions &aCompileOptions,
|
||||
void **aOffThreadToken = nullptr);
|
||||
|
||||
|
@ -271,7 +271,7 @@ nsresult nsJSThunk::EvaluateScript(nsIChannel *aChannel,
|
||||
JS::CompileOptions options(cx);
|
||||
options.setFileAndLine(mURL.get(), 1)
|
||||
.setVersion(JSVERSION_DEFAULT);
|
||||
nsJSUtils::EvaluateOptions evalOptions;
|
||||
nsJSUtils::EvaluateOptions evalOptions(cx);
|
||||
evalOptions.setCoerceToString(true);
|
||||
rv = nsJSUtils::EvaluateString(cx, NS_ConvertUTF8toUTF16(script),
|
||||
globalJSObject, options, evalOptions, &v);
|
||||
|
@ -1553,7 +1553,12 @@ _evaluate(NPP npp, NPObject* npobj, NPString *script, NPVariant *result)
|
||||
options.setFileAndLine(spec, 0)
|
||||
.setVersion(JSVERSION_DEFAULT);
|
||||
JS::Rooted<JS::Value> rval(cx);
|
||||
nsJSUtils::EvaluateOptions evalOptions;
|
||||
nsJSUtils::EvaluateOptions evalOptions(cx);
|
||||
if (obj != js::GetGlobalForObjectCrossCompartment(obj) &&
|
||||
!evalOptions.scopeChain.append(obj)) {
|
||||
return false;
|
||||
}
|
||||
obj = js::GetGlobalForObjectCrossCompartment(obj);
|
||||
nsresult rv = nsJSUtils::EvaluateString(cx, utf16script, obj, options,
|
||||
evalOptions, &rval);
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "nsXBLPrototypeBinding.h"
|
||||
#include "mozilla/AddonPathService.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/ElementBinding.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "xpcpublic.h"
|
||||
@ -393,10 +394,6 @@ nsXBLProtoImplField::InstallField(JS::Handle<JSObject*> aBoundNode,
|
||||
|
||||
nsAutoMicroTask mt;
|
||||
|
||||
// EvaluateString and JS_DefineUCProperty can both trigger GC, so
|
||||
// protect |result| here.
|
||||
nsresult rv;
|
||||
|
||||
nsAutoCString uriSpec;
|
||||
aBindingDocURI->GetSpec(uriSpec);
|
||||
|
||||
@ -415,30 +412,34 @@ nsXBLProtoImplField::InstallField(JS::Handle<JSObject*> aBoundNode,
|
||||
|
||||
JSAddonId* addonId = MapURIToAddonID(aBindingDocURI);
|
||||
|
||||
// First, enter the xbl scope, wrap the node, and use that as the scope for
|
||||
// the evaluation.
|
||||
Element* boundElement = nullptr;
|
||||
nsresult rv = UNWRAP_OBJECT(Element, aBoundNode, boundElement);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// First, enter the xbl scope, build the element's scope chain, and use
|
||||
// that as the scope chain for the evaluation.
|
||||
JS::Rooted<JSObject*> scopeObject(cx, xpc::GetScopeForXBLExecution(cx, aBoundNode, addonId));
|
||||
NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY);
|
||||
JSAutoCompartment ac(cx, scopeObject);
|
||||
|
||||
JS::Rooted<JSObject*> wrappedNode(cx, aBoundNode);
|
||||
if (!JS_WrapObject(cx, &wrappedNode))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
JS::Rooted<JS::Value> result(cx);
|
||||
JS::CompileOptions options(cx);
|
||||
options.setFileAndLine(uriSpec.get(), mLineNumber)
|
||||
.setVersion(JSVERSION_LATEST);
|
||||
nsJSUtils::EvaluateOptions evalOptions;
|
||||
nsJSUtils::EvaluateOptions evalOptions(cx);
|
||||
if (!nsJSUtils::GetScopeChainForElement(cx, boundElement,
|
||||
evalOptions.scopeChain)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
rv = nsJSUtils::EvaluateString(cx, nsDependentString(mFieldText,
|
||||
mFieldTextLength),
|
||||
wrappedNode, options, evalOptions,
|
||||
&result);
|
||||
scopeObject, options, evalOptions, &result);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
// Now, enter the node's compartment, wrap the eval result, and define it on
|
||||
// the bound node.
|
||||
JSAutoCompartment ac2(cx, aBoundNode);
|
||||
|
@ -2,6 +2,7 @@
|
||||
support-files =
|
||||
file_bug944407.xml
|
||||
file_bug950909.xml
|
||||
file_fieldScopeChain.xml
|
||||
|
||||
[test_bug378518.xul]
|
||||
[test_bug398135.xul]
|
||||
@ -11,3 +12,4 @@ support-files =
|
||||
[test_bug772966.xul]
|
||||
[test_bug944407.xul]
|
||||
[test_bug950909.xul]
|
||||
[test_fieldScopeChain.html]
|
||||
|
8
dom/xbl/test/file_fieldScopeChain.xml
Normal file
8
dom/xbl/test/file_fieldScopeChain.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<bindings xmlns="http://www.mozilla.org/xbl"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml">
|
||||
<binding id="foo">
|
||||
<implementation>
|
||||
<field name="bar">baz</field>
|
||||
</implementation>
|
||||
</binding>
|
||||
</bindings>
|
34
dom/xbl/test/test_fieldScopeChain.html
Normal file
34
dom/xbl/test/test_fieldScopeChain.html
Normal file
@ -0,0 +1,34 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1095660
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug </title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug **/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
window.baz = 1;
|
||||
document.baz = 2;
|
||||
addLoadEvent(function() {
|
||||
is(document.querySelector("pre").bar, 2,
|
||||
"Should have document on field scope chain");
|
||||
SimpleTest.finish();
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1095660">Mozilla Bug </a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test" style="-moz-binding: url(file_fieldScopeChain.xml#foo)">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue
Block a user