diff --git a/docshell/test/browser/browser_loadDisallowInherit.js b/docshell/test/browser/browser_loadDisallowInherit.js
index d623c138201..22a5edfe63a 100644
--- a/docshell/test/browser/browser_loadDisallowInherit.js
+++ b/docshell/test/browser/browser_loadDisallowInherit.js
@@ -47,9 +47,7 @@ function test() {
let urls = [
"data:text/html,
hi",
- // We used to test javascript: here as well, but now that we no longer run
- // javascript: in a sandbox, we end up not running it at all in the
- // DISALLOW_INHERIT_OWNER case, so never actually do a load for it at all.
+ "javascript:1;"
];
function nextTest() {
diff --git a/dom/base/nsIScriptChannel.idl b/dom/base/nsIScriptChannel.idl
index 0e57313f7ac..32bdc6b604c 100644
--- a/dom/base/nsIScriptChannel.idl
+++ b/dom/base/nsIScriptChannel.idl
@@ -34,9 +34,10 @@ interface nsIScriptChannel : nsISupports
const unsigned long NO_EXECUTION = 0;
/**
- * There used to be an EXECUTE_IN_SANDBOX = 1 value. It has been removed, but
- * we're not changing the value of EXECUTE_NORMAL to avoid breaking compat.
+ * Execute in a sandbox, no matter how the various principals involved are
+ * related to each other.
*/
+ const unsigned long EXECUTE_IN_SANDBOX = 1;
/**
* Execute against the target environment if the principals allow it.
diff --git a/dom/src/jsurl/crashtests/1018583.html b/dom/src/jsurl/crashtests/1018583.html
deleted file mode 100644
index 8ede367c6bf..00000000000
--- a/dom/src/jsurl/crashtests/1018583.html
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/dom/src/jsurl/crashtests/crashtests.list b/dom/src/jsurl/crashtests/crashtests.list
index b23499d4c1d..51f92d51b52 100644
--- a/dom/src/jsurl/crashtests/crashtests.list
+++ b/dom/src/jsurl/crashtests/crashtests.list
@@ -2,4 +2,3 @@ load 341963-1.html
load 344874-1.html
load 344996-1.xhtml
load 457050-1.html
-load 1018583.html
diff --git a/dom/src/jsurl/nsJSProtocolHandler.cpp b/dom/src/jsurl/nsJSProtocolHandler.cpp
index c8101ab83d8..78a3fd8de35 100644
--- a/dom/src/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/src/jsurl/nsJSProtocolHandler.cpp
@@ -237,6 +237,9 @@ nsresult nsJSThunk::EvaluateScript(nsIChannel *aChannel,
if (NS_FAILED(rv))
return rv;
+ bool useSandbox =
+ (aExecutionPolicy == nsIScriptChannel::EXECUTE_IN_SANDBOX);
+
// New script entry point required, due to the "Create a script" step of
// http://www.whatwg.org/specs/web-apps/current-work/#javascript-protocol
AutoEntryScript entryScript(innerGlobal, true,
@@ -245,37 +248,90 @@ nsresult nsJSThunk::EvaluateScript(nsIChannel *aChannel,
JS::Rooted globalJSObject(cx, innerGlobal->GetGlobalJSObject());
NS_ENSURE_TRUE(globalJSObject, NS_ERROR_UNEXPECTED);
- //-- Don't execute unless the script principal subsumes the
- // principal of the context.
- nsIPrincipal* objectPrincipal = nsContentUtils::ObjectPrincipal(globalJSObject);
+ if (!useSandbox) {
+ //-- Don't outside a sandbox unless the script principal subsumes the
+ // principal of the context.
+ nsIPrincipal* objectPrincipal = nsContentUtils::ObjectPrincipal(globalJSObject);
- bool subsumes;
- rv = principal->Subsumes(objectPrincipal, &subsumes);
- if (NS_FAILED(rv))
- return rv;
+ bool subsumes;
+ rv = principal->Subsumes(objectPrincipal, &subsumes);
+ if (NS_FAILED(rv))
+ return rv;
- if (!subsumes) {
- return NS_ERROR_DOM_RETVAL_UNDEFINED;
+ useSandbox = !subsumes;
}
JS::Rooted v (cx, JS::UndefinedValue());
// Finally, we have everything needed to evaluate the expression.
- JS::CompileOptions options(cx);
- options.setFileAndLine(mURL.get(), 1)
- .setVersion(JSVERSION_DEFAULT);
- nsJSUtils::EvaluateOptions evalOptions;
- evalOptions.setCoerceToString(true);
- rv = nsJSUtils::EvaluateString(cx, NS_ConvertUTF8toUTF16(script),
- globalJSObject, options, evalOptions, &v);
+ if (useSandbox) {
+ // We were asked to use a sandbox, or the channel owner isn't allowed
+ // to execute in this context. Evaluate the javascript URL in a
+ // sandbox to prevent it from accessing data it doesn't have
+ // permissions to access.
- // If there's an error on cx as a result of that call, report
- // it now -- either we're just running under the event loop,
- // so we shouldn't propagate JS exceptions out of here, or we
- // can't be sure that our caller is JS (and if it's not we'll
- // lose the error), or it might be JS that then proceeds to
- // cause an error of its own (which will also make us lose
- // this error).
- ::JS_ReportPendingException(cx);
+ // First check to make sure it's OK to evaluate this script to
+ // start with. For example, script could be disabled.
+ if (!securityManager->ScriptAllowed(globalJSObject)) {
+ // Treat this as returning undefined from the script. That's what
+ // nsJSContext does.
+ return NS_ERROR_DOM_RETVAL_UNDEFINED;
+ }
+
+ nsIXPConnect *xpc = nsContentUtils::XPConnect();
+
+ nsCOMPtr sandbox;
+ // Important: Use a null principal here
+ rv = xpc->CreateSandbox(cx, nullptr, getter_AddRefs(sandbox));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // The nsXPConnect sandbox API gives us a wrapper to the sandbox for
+ // our current compartment. Because our current context doesn't necessarily
+ // subsume that of the sandbox, we want to unwrap and enter the sandbox's
+ // compartment. It's a shame that the APIs here are so clunkly. :-(
+ JS::Rooted sandboxObj(cx, sandbox->GetJSObject());
+ NS_ENSURE_STATE(sandboxObj);
+ sandboxObj = js::UncheckedUnwrap(sandboxObj);
+ JSAutoCompartment ac(cx, sandboxObj);
+
+ // Push our JSContext on the context stack so the EvalInSandboxObject call (and
+ // JS_ReportPendingException, if relevant) will use the principal of cx.
+ nsCxPusher pusher;
+ pusher.Push(cx);
+ rv = xpc->EvalInSandboxObject(NS_ConvertUTF8toUTF16(script),
+ /* filename = */ nullptr, cx,
+ sandboxObj, true, &v);
+
+ // Propagate and report exceptions that happened in the
+ // sandbox.
+ if (JS_IsExceptionPending(cx)) {
+ JS_ReportPendingException(cx);
+ }
+ } else {
+ // No need to use the sandbox, evaluate the script directly in
+ // the given scope.
+ JS::CompileOptions options(cx);
+ options.setFileAndLine(mURL.get(), 1)
+ .setVersion(JSVERSION_DEFAULT);
+ nsJSUtils::EvaluateOptions evalOptions;
+ evalOptions.setCoerceToString(true);
+ rv = nsJSUtils::EvaluateString(cx, NS_ConvertUTF8toUTF16(script),
+ globalJSObject, options, evalOptions, &v);
+
+ // If there's an error on cx as a result of that call, report
+ // it now -- either we're just running under the event loop,
+ // so we shouldn't propagate JS exceptions out of here, or we
+ // can't be sure that our caller is JS (and if it's not we'll
+ // lose the error), or it might be JS that then proceeds to
+ // cause an error of its own (which will also make us lose
+ // this error).
+ ::JS_ReportPendingException(cx);
+ }
+
+ // If we took the sandbox path above, v might be in the sandbox
+ // compartment.
+ if (!JS_WrapValue(cx, &v)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
if (NS_FAILED(rv) || !(v.isString() || v.isUndefined())) {
return NS_ERROR_MALFORMED_URI;
@@ -378,7 +434,7 @@ nsJSChannel::nsJSChannel() :
mLoadFlags(LOAD_NORMAL),
mActualLoadFlags(LOAD_NORMAL),
mPopupState(openOverridden),
- mExecutionPolicy(NO_EXECUTION),
+ mExecutionPolicy(EXECUTE_IN_SANDBOX),
mIsAsync(true),
mIsActive(false),
mOpenedStreamChannel(false)
diff --git a/layout/reftests/bugs/376484-1-ref.html b/layout/reftests/bugs/376484-1-ref.html
index 2cded6bc487..5bc00deb06a 100644
--- a/layout/reftests/bugs/376484-1-ref.html
+++ b/layout/reftests/bugs/376484-1-ref.html
@@ -2,9 +2,9 @@
This is text
-This is more text
+This is more text
This is yet more text
-This is even more text
+This is even more text