Bug 1141796: Don't inject the SDK console into pages loaded in tabs. r=erikvold
authorDave Townsend <dtownsend@oxymoronical.com>
Mon, 16 Mar 2015 11:31:14 -0700
changeset 233848 b8b1d58af8c670684b18660ad5f6d6af50031047
parent 233847 6c269cbf361c75a3d6c48ab3777b8462e0649c8f
child 233849 9b2c3bee895c3ff77931591c20e06a77b44df333
push id28425
push usercbook@mozilla.com
push dateTue, 17 Mar 2015 10:33:06 +0000
treeherdermozilla-central@a194e8023090 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerserikvold
bugs1141796
milestone39.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1141796: Don't inject the SDK console into pages loaded in tabs. r=erikvold
addon-sdk/moz.build
addon-sdk/source/lib/sdk/content/sandbox.js
addon-sdk/source/lib/sdk/remote/child.js
addon-sdk/source/lib/sdk/remote/core.js
addon-sdk/source/lib/sdk/remote/parent.js
addon-sdk/source/test/addons/e10s-remote/main.js
addon-sdk/source/test/addons/e10s-remote/remote-module.js
addon-sdk/source/test/addons/remote/main.js
addon-sdk/source/test/addons/remote/remote-module.js
addon-sdk/source/test/test-page-mod.js
--- a/addon-sdk/moz.build
+++ b/addon-sdk/moz.build
@@ -392,16 +392,17 @@ EXTRA_JS_MODULES.commonjs.sdk.preference
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk['private-browsing'] += [
     'source/lib/sdk/private-browsing/utils.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.remote += [
     'source/lib/sdk/remote/child.js',
+    'source/lib/sdk/remote/core.js',
     'source/lib/sdk/remote/parent.js',
     'source/lib/sdk/remote/utils.js',
 ]
 
 EXTRA_JS_MODULES.commonjs.sdk.stylesheet += [
     'source/lib/sdk/stylesheet/style.js',
     'source/lib/sdk/stylesheet/utils.js',
 ]
--- a/addon-sdk/source/lib/sdk/content/sandbox.js
+++ b/addon-sdk/source/lib/sdk/content/sandbox.js
@@ -16,16 +16,17 @@ const { Ci, Cu, Cc } = require('chrome')
 const timer = require('../timers');
 const { URL } = require('../url');
 const { sandbox, evaluate, load } = require('../loader/sandbox');
 const { merge } = require('../util/object');
 const { getTabForContentWindow } = require('../tabs/utils');
 const { getInnerId } = require('../window/utils');
 const { PlainTextConsole } = require('../console/plain-text');
 const { data } = require('../self');
+const { isChildLoader } = require('../remote/core');
 // WeakMap of sandboxes so we can access private values
 const sandboxes = new WeakMap();
 
 /* Trick the linker in order to ensure shipping these files in the XPI.
   require('./content-worker.js');
   Then, retrieve URL of these files in the XPI:
 */
 let prefix = module.uri.split('sandbox.js')[0];
@@ -42,16 +43,29 @@ const EXPANDED_PRINCIPALS = permissions[
 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';
 
+// Tests whether this window is loaded in a tab
+function isWindowInTab(window) {
+  if (isChildLoader) {
+    let { frames } = require('../remote/child');
+    let frame = frames.getFrameForWindow(window.top);
+    return frame.isTab;
+  }
+  else {
+    // The deprecated sync worker API still does everything in the main process
+    return getTabForContentWindow(window);
+  }
+}
+
 const WorkerSandbox = Class({
   implements: [ EventTarget ],
 
   /**
    * Emit a message to the worker content sandbox
    */
   emit: function emit(type, ...args) {
     // JSON.stringify is buggy with cross-sandbox values,
@@ -197,17 +211,17 @@ const WorkerSandbox = Class({
           configurable: true
         }
       );
     }
 
     // Inject our `console` into target document if worker doesn't have a tab
     // (e.g Panel, PageWorker, Widget).
     // `worker.tab` can't be used because bug 804935.
-    if (!getTabForContentWindow(window)) {
+    if (!isWindowInTab(window)) {
       let win = getUnsafeWindow(window);
 
       // export our chrome console to content window, as described here:
       // https://developer.mozilla.org/en-US/docs/Components.utils.createObjectIn
       let con = Cu.createObjectIn(win);
 
       let genPropDesc = function genPropDesc(fun) {
         return { enumerable: true, configurable: true, writable: true,
--- a/addon-sdk/source/lib/sdk/remote/child.js
+++ b/addon-sdk/source/lib/sdk/remote/child.js
@@ -1,13 +1,17 @@
 /* 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 { isChildLoader } = require('./core');
+if (!isChildLoader)
+  throw new Error("Cannot load sdk/remote/child in a main process loader.");
+
 const { Ci, Cc } = require('chrome');
 const runtime = require('../system/runtime');
 const { Class } = require('../core/heritage');
 const { Namespace } = require('../core/namespace');
 const { omit } = require('../util/object');
 const { when } = require('../system/unload');
 const { EventTarget } = require('../event/target');
 const { emit } = require('../event/core');
new file mode 100644
--- /dev/null
+++ b/addon-sdk/source/lib/sdk/remote/core.js
@@ -0,0 +1,8 @@
+/* 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 options = require("@loader/options");
+
+exports.isChildLoader = options.childLoader;
--- a/addon-sdk/source/lib/sdk/remote/parent.js
+++ b/addon-sdk/source/lib/sdk/remote/parent.js
@@ -1,13 +1,17 @@
 /* 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 { isChildLoader } = require('./core');
+if (isChildLoader)
+  throw new Error("Cannot load sdk/remote/parent in a child loader.");
+
 const { Cu, Ci, Cc } = require('chrome');
 const runtime = require('../system/runtime');
 
 const MAIN_PROCESS = Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
 
 if (runtime.processType != MAIN_PROCESS) {
   throw new Error('Cannot use sdk/remote/parent in a child process.');
 }
@@ -48,16 +52,17 @@ childOptions.modules = {};
 try {
   childOptions.modules["@l10n/data"] = require("@l10n/data");
 }
 catch (e) {
   // There may be no l10n data
 }
 const loaderID = getNewLoaderID();
 childOptions.loaderID = loaderID;
+childOptions.childLoader = true;
 
 const ppmm = Cc['@mozilla.org/parentprocessmessagemanager;1'].
              getService(Ci.nsIMessageBroadcaster);
 const gmm = Cc['@mozilla.org/globalmessagemanager;1'].
             getService(Ci.nsIMessageBroadcaster);
 
 const ns = Namespace();
 
--- a/addon-sdk/source/test/addons/e10s-remote/main.js
+++ b/addon-sdk/source/test/addons/e10s-remote/main.js
@@ -487,15 +487,44 @@ exports["test processID"] = function*(as
     let [p, ID] = yield promiseEvent(process.port, 'sdk/test/processid');
     if (process.isRemote) {
       assert.notEqual(ID, processID, "Remote processes should have a different process ID");
     }
     else {
       assert.equal(ID, processID, "Remote processes should have the same process ID");
     }
   }
+
+  loader.unload();
 }
 
+// Check that sdk/remote/parent and sdk/remote/child can only be loaded in the
+// appropriate loaders
+exports["test cannot load in wrong loader"] = function*(assert) {
+  let loader = new Loader(module);
+  let { processes } = yield waitForProcesses(loader);
+
+  try {
+    require('sdk/remote/child');
+    assert.fail("Should not have been able to load sdk/remote/child");
+  }
+  catch (e) {
+    assert.ok(/Cannot load sdk\/remote\/child in a main process loader/.test(e),
+              "Should have seen the right exception.");
+  }
+
+  for (let process of processes) {
+    processes.port.emit('sdk/test/parentload');
+    let [_, isChildLoader, loaded, message] = yield promiseEvent(processes.port, 'sdk/test/parentload');
+    assert.ok(isChildLoader, "Process should see itself in a child loader.");
+    assert.ok(!loaded, "Process couldn't load sdk/remote/parent.");
+    assert.ok(/Cannot load sdk\/remote\/parent in a child loader/.test(message),
+              "Should have seen the right exception.");
+  }
+
+  loader.unload();
+};
+
 after(exports, function*(name, assert) {
   yield cleanUI();
 });
 
 require('sdk/test/runner').runTestsFromModule(module);
--- a/addon-sdk/source/test/addons/e10s-remote/remote-module.js
+++ b/addon-sdk/source/test/addons/e10s-remote/remote-module.js
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const { when } = require('sdk/system/unload');
 const { process, frames } = require('sdk/remote/child');
 const { loaderID } = require('@loader/options');
 const { processID } = require('sdk/system/runtime');
 const system = require('sdk/system/events');
 const { Cu } = require('chrome');
+const { isChildLoader } = require('sdk/remote/core');
 
 function log(str) {
   console.log("remote[" + loaderID + "][" + processID + "]: " + str);
 }
 
 log("module loaded");
 
 process.port.emit('sdk/test/load');
@@ -76,16 +77,34 @@ frames.port.on('sdk/test/sendevent', (fr
 
   system.on("Test:Reply", listener);
   let event = new frame.content.CustomEvent("Test:Event");
   doc.dispatchEvent(event);
   system.off("Test:Reply", listener);
   frame.port.emit('sdk/test/eventsent');
 });
 
+process.port.on('sdk/test/parentload', () => {
+  let loaded = false;
+  let message = "";
+  try {
+    require('sdk/remote/parent');
+    loaded = true;
+  }
+  catch (e) {
+    message = "" + e;
+  }
+
+  process.port.emit('sdk/test/parentload',
+    isChildLoader,
+    loaded,
+    message
+  )
+});
+
 function listener(event) {
   // Use the raw observer service here since it will be usable even if the
   // loader has unloaded
   let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
   Services.obs.notifyObservers(null, "Test:Reply", "");
 }
 
 frames.port.on('sdk/test/registerframesevent', (frame) => {
--- a/addon-sdk/source/test/addons/remote/main.js
+++ b/addon-sdk/source/test/addons/remote/main.js
@@ -487,15 +487,44 @@ exports["test processID"] = function*(as
     let [p, ID] = yield promiseEvent(process.port, 'sdk/test/processid');
     if (process.isRemote) {
       assert.notEqual(ID, processID, "Remote processes should have a different process ID");
     }
     else {
       assert.equal(ID, processID, "Remote processes should have the same process ID");
     }
   }
+
+  loader.unload();
 }
 
+// Check that sdk/remote/parent and sdk/remote/child can only be loaded in the
+// appropriate loaders
+exports["test cannot load in wrong loader"] = function*(assert) {
+  let loader = new Loader(module);
+  let { processes } = yield waitForProcesses(loader);
+
+  try {
+    require('sdk/remote/child');
+    assert.fail("Should not have been able to load sdk/remote/child");
+  }
+  catch (e) {
+    assert.ok(/Cannot load sdk\/remote\/child in a main process loader/.test(e),
+              "Should have seen the right exception.");
+  }
+
+  for (let process of processes) {
+    processes.port.emit('sdk/test/parentload');
+    let [_, isChildLoader, loaded, message] = yield promiseEvent(processes.port, 'sdk/test/parentload');
+    assert.ok(isChildLoader, "Process should see itself in a child loader.");
+    assert.ok(!loaded, "Process couldn't load sdk/remote/parent.");
+    assert.ok(/Cannot load sdk\/remote\/parent in a child loader/.test(message),
+              "Should have seen the right exception.");
+  }
+
+  loader.unload();
+};
+
 after(exports, function*(name, assert) {
   yield cleanUI();
 });
 
 require('sdk/test/runner').runTestsFromModule(module);
--- a/addon-sdk/source/test/addons/remote/remote-module.js
+++ b/addon-sdk/source/test/addons/remote/remote-module.js
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const { when } = require('sdk/system/unload');
 const { process, frames } = require('sdk/remote/child');
 const { loaderID } = require('@loader/options');
 const { processID } = require('sdk/system/runtime');
 const system = require('sdk/system/events');
 const { Cu } = require('chrome');
+const { isChildLoader } = require('sdk/remote/core');
 
 function log(str) {
   console.log("remote[" + loaderID + "][" + processID + "]: " + str);
 }
 
 log("module loaded");
 
 process.port.emit('sdk/test/load');
@@ -76,16 +77,34 @@ frames.port.on('sdk/test/sendevent', (fr
 
   system.on("Test:Reply", listener);
   let event = new frame.content.CustomEvent("Test:Event");
   doc.dispatchEvent(event);
   system.off("Test:Reply", listener);
   frame.port.emit('sdk/test/eventsent');
 });
 
+process.port.on('sdk/test/parentload', () => {
+  let loaded = false;
+  let message = "";
+  try {
+    require('sdk/remote/parent');
+    loaded = true;
+  }
+  catch (e) {
+    message = "" + e;
+  }
+
+  process.port.emit('sdk/test/parentload',
+    isChildLoader,
+    loaded,
+    message
+  )
+});
+
 function listener(event) {
   // Use the raw observer service here since it will be usable even if the
   // loader has unloaded
   let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
   Services.obs.notifyObservers(null, "Test:Reply", "");
 }
 
 frames.port.on('sdk/test/registerframesevent', (frame) => {
--- a/addon-sdk/source/test/test-page-mod.js
+++ b/addon-sdk/source/test/test-page-mod.js
@@ -2035,9 +2035,32 @@ exports.testUnloadWontAttach = function(
       closeTab(tab);
       done();
     }
   });
 
   let tab = openTab(getMostRecentBrowserWindow(), TEST_URL);
 }
 
+// Tests that the SDK console isn't injected into documents loaded in tabs
+exports.testDontInjectConsole = function(assert, done) {
+  const TEST_URL = 'data:text/html;charset=utf-8,consoleinject';
+
+  let loader = Loader(module);
+
+  let mod = PageMod({
+    include: TEST_URL,
+    contentScript: Isolate(function() {
+      // This relies on the fact that the SDK console doesn't have assert defined
+      self.postMessage((typeof unsafeWindow.console.assert) == "function");
+    }),
+    onMessage: isNativeConsole => {
+      assert.ok(isNativeConsole, "Shouldn't have injected the SDK console.");
+      mod.destroy();
+      closeTab(tab);
+      done();
+    }
+  });
+
+  let tab = openTab(getMostRecentBrowserWindow(), TEST_URL);
+}
+
 require('sdk/test').run(exports);