diff --git a/addon-sdk/source/lib/sdk/content/sandbox.js b/addon-sdk/source/lib/sdk/content/sandbox.js
index e8c9a1533c6..7fb88838848 100644
--- a/addon-sdk/source/lib/sdk/content/sandbox.js
+++ b/addon-sdk/source/lib/sdk/content/sandbox.js
@@ -38,6 +38,12 @@ const metadata = require('@loader/options').metadata;
const permissions = (metadata && metadata['permissions']) || {};
const EXPANDED_PRINCIPALS = permissions['cross-domain-content'] || [];
+const waiveSecurityMembrane = !!permissions['unsafe-content-script'];
+
+const nsIScriptSecurityManager = Ci.nsIScriptSecurityManager;
+const secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].
+ getService(Ci.nsIScriptSecurityManager);
+
const JS_VERSION = '1.8';
const WorkerSandbox = Class({
@@ -96,8 +102,10 @@ const WorkerSandbox = Class({
this.emit = this.emit.bind(this);
this.emitSync = this.emitSync.bind(this);
- // Eventually use expanded principal sandbox feature, if some are given.
- //
+ // Use expanded principal for content-script if the content is a
+ // regular web content for better isolation.
+ // (This behavior can be turned off for now with the unsafe-content-script
+ // flag to give addon developers time for making the necessary changes)
// But prevent it when the Worker isn't used for a content script but for
// injecting `addon` object into a Panel, Widget, ... scope.
// That's because:
@@ -110,12 +118,17 @@ const WorkerSandbox = Class({
// domain principal.
let principals = window;
let wantGlobalProperties = [];
- if (EXPANDED_PRINCIPALS.length > 0 && !requiresAddonGlobal(worker)) {
- principals = EXPANDED_PRINCIPALS.concat(window);
- // We have to replace XHR constructor of the content document
- // with a custom cross origin one, automagically added by platform code:
- delete proto.XMLHttpRequest;
- wantGlobalProperties.push('XMLHttpRequest');
+ let isSystemPrincipal = secMan.isSystemPrincipal(
+ window.document.nodePrincipal);
+ if (!isSystemPrincipal && !requiresAddonGlobal(worker)) {
+ if (EXPANDED_PRINCIPALS.length > 0) {
+ // We have to replace XHR constructor of the content document
+ // with a custom cross origin one, automagically added by platform code:
+ delete proto.XMLHttpRequest;
+ wantGlobalProperties.push('XMLHttpRequest');
+ }
+ if (!waiveSecurityMembrane)
+ principals = EXPANDED_PRINCIPALS.concat(window);
}
// Instantiate trusted code in another Sandbox in order to prevent content
@@ -129,6 +142,7 @@ const WorkerSandbox = Class({
sandboxPrototype: proto,
wantXrays: true,
wantGlobalProperties: wantGlobalProperties,
+ wantExportHelpers: !waiveSecurityMembrane,
sameZoneAs: window,
metadata: {
SDKContentScript: true,
diff --git a/addon-sdk/source/test/addons/unsafe-content-script/main.js b/addon-sdk/source/test/addons/unsafe-content-script/main.js
new file mode 100644
index 00000000000..81a2359af4b
--- /dev/null
+++ b/addon-sdk/source/test/addons/unsafe-content-script/main.js
@@ -0,0 +1,61 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+const { create: makeFrame } = require("sdk/frame/utils");
+const { window } = require("sdk/addon/window");
+const { Loader } = require('sdk/test/loader');
+const loader = Loader(module);
+const Worker = loader.require("sdk/content/worker").Worker;
+
+exports.testMembranelessMode = function(assert, done) {
+
+ let url = "data:text/html;charset=utf-8," + encodeURIComponent(
+ ''
+ );
+
+ let element = makeFrame(window.document, {
+ nodeName: "iframe",
+ type: "content",
+ allowJavascript: true,
+ allowPlugins: true,
+ allowAuth: true,
+ uri: url
+ });
+
+ element.addEventListener("DOMContentLoaded", onDOMReady, false);
+
+ function onDOMReady() {
+ let worker = Worker({
+ window: element.contentWindow,
+ contentScript:
+ 'new ' + function () {
+ var assert = function assert(v, msg) {
+ self.port.emit("assert", { assertion: v, msg: msg });
+ }
+ var done = function done() {
+ self.port.emit("done");
+ }
+ window.wrappedJSObject.fuu = { bar: 42 };
+ window.wrappedJSObject.assert = assert;
+ window.wrappedJSObject.runTest();
+ done();
+ }
+ });
+ worker.port.on("done", function () {
+ element.parentNode.removeChild(element);
+ done();
+ });
+ worker.port.on("assert", function (data) {
+ assert.ok(data.assertion, data.msg);
+ });
+ }
+};
+
+require("sdk/test/runner").runTestsFromModule(module);
diff --git a/addon-sdk/source/test/addons/unsafe-content-script/package.json b/addon-sdk/source/test/addons/unsafe-content-script/package.json
new file mode 100644
index 00000000000..72c5b512f81
--- /dev/null
+++ b/addon-sdk/source/test/addons/unsafe-content-script/package.json
@@ -0,0 +1,6 @@
+{
+ "id": "content-permissions",
+ "permissions": {
+ "unsafe-content-script": true
+ }
+}
diff --git a/addon-sdk/source/test/test-content-script.js b/addon-sdk/source/test/test-content-script.js
index 38acfc1ca16..7bfa3123c91 100644
--- a/addon-sdk/source/test/test-content-script.js
+++ b/addon-sdk/source/test/test-content-script.js
@@ -842,4 +842,30 @@ exports["test MutationObvserver"] = createProxyTest(html, function (helper) {
});
+let html = '';
+exports["test nsEp for content-script"] = createProxyTest(html, function (helper) {
+
+ helper.createWorker(
+ 'let glob = this; new ' + function ContentScriptScope() {
+
+ exportFunction(assert, unsafeWindow, { defineAs: "assert" });
+ window.wrappedJSObject.assert(true, "assert exported");
+ window.wrappedJSObject.exportedObj = { prop: 42 };
+ window.wrappedJSObject.accessCheck();
+ done();
+ }
+ );
+
+});
+
require("test").run(exports);