Skip to content

Commit

Permalink
Bug 1013997 - Use only one compartment for all devtools modules. r=mo…
Browse files Browse the repository at this point in the history
…ssop, r=past
  • Loading branch information
ochameau committed Jul 7, 2014
1 parent b26ade1 commit 5d93b9a
Show file tree
Hide file tree
Showing 22 changed files with 135 additions and 50 deletions.
4 changes: 3 additions & 1 deletion addon-sdk/source/lib/sdk/base64.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ module.metadata = {
const { Cu } = require("chrome");

// Passing an empty object as second argument to avoid scope's pollution
const { atob, btoa } = Cu.import("resource://gre/modules/Services.jsm", {});
// (devtools loader injects these symbols as global and prevent using
// const here)
var { atob, btoa } = Cu.import("resource://gre/modules/Services.jsm", {});

function isUTF8(charset) {
let type = typeof charset;
Expand Down
61 changes: 48 additions & 13 deletions addon-sdk/source/lib/toolkit/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -283,17 +283,31 @@ const load = iced(function load(loader, module) {
}
});

let sandbox = sandboxes[module.uri] = Sandbox({
name: module.uri,
prototype: create(globals, descriptors),
wantXrays: false,
wantGlobalProperties: module.id == "sdk/indexed-db" ? ["indexedDB"] : [],
invisibleToDebugger: loader.invisibleToDebugger,
metadata: {
addonID: loader.id,
URI: module.uri
}
});
let sandbox;
if (loader.sharedGlobalSandbox &&
loader.sharedGlobalBlacklist.indexOf(module.id) == -1) {
// Create a new object in this sandbox, that will be used as
// the scope object for this particular module
sandbox = new loader.sharedGlobalSandbox.Object();
// Inject all expected globals in the scope object
getOwnPropertyNames(globals).forEach(function(name) {
descriptors[name] = getOwnPropertyDescriptor(globals, name)
});
define(sandbox, descriptors);
} else {
sandbox = Sandbox({
name: module.uri,
prototype: create(globals, descriptors),
wantXrays: false,
wantGlobalProperties: module.id == "sdk/indexed-db" ? ["indexedDB"] : [],
invisibleToDebugger: loader.invisibleToDebugger,
metadata: {
addonID: loader.id,
URI: module.uri
}
});
}
sandboxes[module.uri] = sandbox;

try {
evaluate(sandbox, module.uri);
Expand Down Expand Up @@ -691,8 +705,8 @@ const Loader = iced(function Loader(options) {
});

let {
modules, globals, resolve, paths, rootURI,
manifest, requireMap, isNative, metadata
modules, globals, resolve, paths, rootURI, manifest, requireMap, isNative,
metadata, sharedGlobal, sharedGlobalBlacklist
} = override({
paths: {},
modules: {},
Expand All @@ -702,6 +716,7 @@ const Loader = iced(function Loader(options) {
resolve: options.isNative ?
exports.nodeResolve :
exports.resolve,
sharedGlobalBlacklist: ["sdk/indexed-db"]
}, options);

// We create an identity object that will be dispatched on an unload
Expand Down Expand Up @@ -738,6 +753,24 @@ const Loader = iced(function Loader(options) {
return result;
}, {});

let sharedGlobalSandbox;
if (sharedGlobal) {
// Create the unique sandbox we will be using for all modules,
// so that we prevent creating a new comportment per module.
// The side effect is that all modules will share the same
// global objects.
sharedGlobalSandbox = Sandbox({
name: "Addon-SDK",
wantXrays: false,
wantGlobalProperties: [],
invisibleToDebugger: options.invisibleToDebugger || false,
metadata: {
addonID: options.id,
URI: "Addon-SDK"
}
});
}

// Loader object is just a representation of a environment
// state. We freeze it and mark make it's properties non-enumerable
// as they are pure implementation detail that no one should rely upon.
Expand All @@ -748,6 +781,8 @@ const Loader = iced(function Loader(options) {
// Map of module objects indexed by module URIs.
modules: { enumerable: false, value: modules },
metadata: { enumerable: false, value: metadata },
sharedGlobalSandbox: { enumerable: false, value: sharedGlobalSandbox },
sharedGlobalBlacklist: { enumerable: false, value: sharedGlobalBlacklist },
// Map of module sandboxes indexed by module URIs.
sandboxes: { enumerable: false, value: {} },
resolve: { enumerable: false, value: resolve },
Expand Down
23 changes: 23 additions & 0 deletions addon-sdk/source/test/test-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -343,4 +343,27 @@ exports['test console global by default'] = function (assert) {
function fakeConsole () {};
};

exports['test shared globals'] = function(assert) {
let uri = root + '/fixtures/loader/cycles/';
let loader = Loader({ paths: { '': uri }, sharedGlobal: true,
sharedGlobalBlacklist: ['b'] });

let program = main(loader, 'main');

// As it is hard to verify what is the global of an object
// (due to wrappers) we check that we see the `foo` symbol
// being manually injected into the shared global object
loader.sharedGlobalSandbox.foo = true;

let m = loader.sandboxes[uri + 'main.js'];
let a = loader.sandboxes[uri + 'a.js'];
let b = loader.sandboxes[uri + 'b.js'];

assert.ok(Cu.getGlobalForObject(m).foo, "main is shared");
assert.ok(Cu.getGlobalForObject(a).foo, "a is shared");
assert.ok(!Cu.getGlobalForObject(b).foo, "b isn't shared");

unload(loader);
}

require('test').run(exports);
3 changes: 1 addition & 2 deletions browser/devtools/app-manager/app-projects.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,13 @@ const { indexedDB } = require("sdk/indexed-db");
* a unique `location` object.
*/

const global = this;
const IDB = {
_db: null,

open: function () {
let deferred = promise.defer();

let request = global.indexedDB.open("AppProjects", 5);
let request = indexedDB.open("AppProjects", 5);
request.onerror = function(event) {
deferred.reject("Unable to open AppProjects indexedDB. " +
"Error code: " + event.target.errorCode);
Expand Down
1 change: 1 addition & 0 deletions browser/devtools/framework/toolbox-options.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

const {Cu, Cc, Ci} = require("chrome");
const Services = require("Services");
const promise = require("promise");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "gDevTools", "resource:///modules/devtools/gDevTools.jsm");

Expand Down
1 change: 1 addition & 0 deletions browser/devtools/projecteditor/lib/shells.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const { EventTarget } = require("sdk/event/target");
const { emit } = require("sdk/event/core");
const { EditorTypeForResource } = require("projecteditor/editors");
const NetworkHelper = require("devtools/toolkit/webconsole/network-helper");
const promise = require("promise");

/**
* The Shell is the object that manages the editor for a single resource.
Expand Down
1 change: 1 addition & 0 deletions browser/devtools/webide/modules/runtimes.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const {Simulator} = Cu.import("resource://gre/modules/devtools/Simulator.jsm");
const {ConnectionManager, Connection} = require("devtools/client/connection-manager");
const {DebuggerServer} = require("resource://gre/modules/devtools/dbg-server.jsm");
const discovery = require("devtools/toolkit/discovery/discovery");
const promise = require("promise");

const Strings = Services.strings.createBundle("chrome://webide/content/webide.properties");

Expand Down
4 changes: 2 additions & 2 deletions toolkit/devtools/DevToolsUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@

/* General utilities used throughout devtools. */

// hasChrome is provided as a global by the loader. It is true if we are running
// on the main thread, and false if we are running on a worker thread.
var { Ci, Cu } = require("chrome");
var Services = require("Services");
var promise = require("promise");
var { setTimeout } = require("Timer");

/**
* Turn the error |aError| into a string, without fail.
Expand Down
43 changes: 25 additions & 18 deletions toolkit/devtools/Loader.jsm
Original file line number Diff line number Diff line change
Expand Up @@ -44,36 +44,42 @@ let Timer = Cu.import("resource://gre/modules/Timer.jsm", {});

let loaderGlobals = {
isWorker: false,
Debugger: Debugger,
promise: promise,
reportError: Cu.reportError,
setInterval: Timer.setInterval,
setTimeout: Timer.setTimeout,
clearInterval: Timer.clearInterval,
clearTimeout: Timer.clearTimeout,
xpcInspector: xpcInspector,

btoa: btoa,
console: console,
_Iterator: Iterator,
ChromeWorker: ChromeWorker,
loader: {
lazyGetter: XPCOMUtils.defineLazyGetter.bind(XPCOMUtils),
lazyImporter: XPCOMUtils.defineLazyModuleGetter.bind(XPCOMUtils),
lazyServiceGetter: XPCOMUtils.defineLazyServiceGetter.bind(XPCOMUtils)
},
};

let loaderModules = {
"Debugger": Debugger,
"Services": Object.create(Services),
"Timer": Object.create(Timer),
"toolkit/loader": loader,
"xpcInspector": xpcInspector,
"promise": promise,
};
try {
let { indexedDB } = Cu.Sandbox(this, {wantGlobalProperties:["indexedDB"]});
loaderModules.indexedDB = indexedDB;
} catch(e) {
// On xpcshell, we can't instantiate indexedDB without crashing
}

let sharedGlobalBlacklist = ["sdk/indexed-db", "devtools/toolkit/qrcode/decoder/index"];

// Used when the tools should be loaded from the Firefox package itself (the default)
function BuiltinProvider() {}
BuiltinProvider.prototype = {
load: function() {
this.loader = new loader.Loader({
id: "fx-devtools",
modules: {
"Services": Object.create(Services),
"toolkit/loader": loader,
},
modules: loaderModules,
paths: {
// When you add a line to this mapping, don't forget to make a
// corresponding addition to the SrcdirProvider mapping below as well.
Expand Down Expand Up @@ -103,7 +109,9 @@ BuiltinProvider.prototype = {
"xpcshell-test": "resource://test"
},
globals: loaderGlobals,
invisibleToDebugger: this.invisibleToDebugger
invisibleToDebugger: this.invisibleToDebugger,
sharedGlobal: true,
sharedGlobalBlacklist: sharedGlobalBlacklist
});

return promise.resolve(undefined);
Expand Down Expand Up @@ -153,10 +161,7 @@ SrcdirProvider.prototype = {
let sourceMapURI = this.fileURI(OS.Path.join(toolkitDir), "SourceMap.jsm");
this.loader = new loader.Loader({
id: "fx-devtools",
modules: {
"Services": Object.create(Services),
"toolkit/loader": loader,
},
modules: loaderModules,
paths: {
"": "resource://gre/modules/commonjs/",
"main": mainURI,
Expand All @@ -181,7 +186,9 @@ SrcdirProvider.prototype = {
"source-map": sourceMapURI,
},
globals: loaderGlobals,
invisibleToDebugger: this.invisibleToDebugger
invisibleToDebugger: this.invisibleToDebugger,
sharedGlobal: true,
sharedGlobalBlacklist: sharedGlobalBlacklist
});

return this._writeManifest(devtoolsDir).then(null, Cu.reportError);
Expand Down
1 change: 1 addition & 0 deletions toolkit/devtools/event-emitter.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ module.exports = EventEmitter;

const { Cu, components } = require("chrome");
const Services = require("Services");
const promise = require("promise");

/**
* Decorate an object with event emitter functionality.
Expand Down
6 changes: 5 additions & 1 deletion toolkit/devtools/server/actors/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,16 @@
"use strict";

const Services = require("Services");
const { Cc, Ci, Cu, components } = require("chrome");
const { Cc, Ci, Cu, components, ChromeWorker } = require("chrome");
const { ActorPool } = require("devtools/server/actors/common");
const { DebuggerServer } = require("devtools/server/main");
const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
const { dbg_assert, dumpn, update } = DevToolsUtils;
const { SourceMapConsumer, SourceMapGenerator } = require("source-map");
const promise = require("promise");
const Debugger = require("Debugger");
const xpcInspector = require("xpcInspector");

const { defer, resolve, reject, all } = require("devtools/toolkit/deprecated-sync-thenables");
const { CssLogic } = require("devtools/styleinspector/css-logic");

Expand Down
2 changes: 1 addition & 1 deletion toolkit/devtools/server/actors/storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -1184,7 +1184,7 @@ StorageActors.createActor({
principal = Services.scriptSecurityManager.getCodebasePrincipal(uri);
}

return indexedDB.openForPrincipal(principal, name);
return require("indexedDB").openForPrincipal(principal, name);
},

/**
Expand Down
4 changes: 1 addition & 3 deletions toolkit/devtools/server/actors/tracer.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
const { Cu } = require("chrome");
const { DebuggerServer } = require("devtools/server/main");
const { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});

Cu.import("resource://gre/modules/jsdebugger.jsm");
addDebuggerToGlobal(this);
const Debugger = require("Debugger");

// TODO bug 943125: remove this polyfill and use Debugger.Frame.prototype.depth
// once it is implemented.
Expand Down
1 change: 1 addition & 0 deletions toolkit/devtools/server/actors/webconsole.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const { Cc, Ci, Cu } = require("chrome");
const { DebuggerServer, ActorPool } = require("devtools/server/main");
const { EnvironmentActor, LongStringActor, ObjectActor, ThreadActor } = require("devtools/server/actors/script");
const { update } = require("devtools/toolkit/DevToolsUtils");
const Debugger = require("Debugger");

Cu.import("resource://gre/modules/XPCOMUtils.jsm");

Expand Down
10 changes: 10 additions & 0 deletions toolkit/devtools/server/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
let { dumpn, dumpv, dbg_assert } = DevToolsUtils;
let Services = require("Services");
let EventEmitter = require("devtools/toolkit/event-emitter");
let Debugger = require("Debugger");

// Until all Debugger server code is converted to SDK modules,
// imports Components.* alias from chrome module.
Expand Down Expand Up @@ -843,6 +844,15 @@ if (this.exports) {
// Needed on B2G (See header note)
this.DebuggerServer = DebuggerServer;

// When using DebuggerServer.addActors, some symbols are expected to be in
// the scope of the added actor even before the corresponding modules are
// loaded, so let's explicitly bind the expected symbols here.
let includes = ["Components", "Ci", "Cu", "require", "Services", "DebuggerServer",
"ActorPool", "DevToolsUtils"];
includes.forEach(name => {
DebuggerServer[name] = this[name];
});

// Export ActorPool for requirers of main.js
if (this.exports) {
exports.ActorPool = ActorPool;
Expand Down
1 change: 1 addition & 0 deletions toolkit/devtools/server/tests/unit/testactors.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const { ActorPool, appendExtraActors, createExtraActors } = require("devtools/se
const { RootActor } = require("devtools/server/actors/root");
const { ThreadActor } = require("devtools/server/actors/script");
const { DebuggerServer } = require("devtools/server/main");
const promise = require("promise");

var gTestGlobals = [];
DebuggerServer.addTestGlobal = function(aGlobal) {
Expand Down
4 changes: 2 additions & 2 deletions toolkit/devtools/tests/unit/test_invisible_loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ function visible_loader() {
loader.require("devtools/css-color");

let dbg = new Debugger();
let sandbox = loader._provider.loader.sandboxes[COLOR_URI];
let sandbox = loader._provider.loader.sharedGlobalSandbox;

try {
dbg.addDebuggee(sandbox);
Expand All @@ -37,7 +37,7 @@ function invisible_loader() {
loader.require("devtools/css-color");

let dbg = new Debugger();
let sandbox = loader._provider.loader.sandboxes[COLOR_URI];
let sandbox = loader._provider.loader.sharedGlobalSandbox;

try {
dbg.addDebuggee(sandbox);
Expand Down
2 changes: 1 addition & 1 deletion toolkit/devtools/transport/packets.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const { Cc, Ci, Cu } = require("chrome");
const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
const { dumpn, dumpv } = DevToolsUtils;
const StreamUtils = require("devtools/toolkit/transport/stream-utils");
const EventEmitter = require("devtools/toolkit/event-emitter");
const promise = require("promise");

DevToolsUtils.defineLazyGetter(this, "unicodeConverter", () => {
const unicodeConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
Expand Down
1 change: 1 addition & 0 deletions toolkit/devtools/transport/stream-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const Services = require("Services");
const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
const { dumpv } = DevToolsUtils;
const EventEmitter = require("devtools/toolkit/event-emitter");
const promise = require("promise");

DevToolsUtils.defineLazyGetter(this, "IOUtil", () => {
return Cc["@mozilla.org/io-util;1"].getService(Ci.nsIIOUtil);
Expand Down
Loading

0 comments on commit 5d93b9a

Please sign in to comment.