Bug 1013997 - Use only one compartment for all devtools modules. r=mossop, r=past

This commit is contained in:
Alexandre Poirot 2014-07-07 06:07:00 -04:00
parent 84f485d505
commit f2b703b3b6
22 changed files with 135 additions and 50 deletions

View File

@ -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;

View File

@ -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);
@ -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: {},
@ -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
@ -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.
@ -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 },

View File

@ -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);

View File

@ -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);

View File

@ -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");

View File

@ -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.

View File

@ -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");

View File

@ -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.

View File

@ -44,19 +44,11 @@ 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),
@ -64,16 +56,30 @@ let loaderGlobals = {
},
};
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.
@ -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);
@ -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,
@ -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);

View File

@ -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.

View File

@ -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");

View File

@ -1184,7 +1184,7 @@ StorageActors.createActor({
principal = Services.scriptSecurityManager.getCodebasePrincipal(uri);
}
return indexedDB.openForPrincipal(principal, name);
return require("indexedDB").openForPrincipal(principal, name);
},
/**

View File

@ -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.

View File

@ -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");

View File

@ -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.
@ -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;

View File

@ -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) {

View File

@ -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);
@ -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);

View File

@ -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"]

View File

@ -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);

View File

@ -6,6 +6,7 @@ const { ActorPool, appendExtraActors, createExtraActors } =
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) {

View File

@ -29,6 +29,7 @@ const { dumpn, dumpv } = DevToolsUtils;
const StreamUtils = require("devtools/toolkit/transport/stream-utils");
const { Packet, JSONPacket, BulkPacket } =
require("devtools/toolkit/transport/packets");
const promise = require("promise");
DevToolsUtils.defineLazyGetter(this, "Pipe", () => {
return CC("@mozilla.org/pipe;1", "nsIPipe", "init");

View File

@ -311,18 +311,16 @@ if (typeof Components === "object") {
createSandbox: createSandbox,
globals: {
"isWorker": true,
"Debugger": Debugger,
"setInterval": Timer.setInterval,
"setTimeout": Timer.setTimeout,
"clearInterval": Timer.clearInterval,
"clearTimeout": Timer.clearTimeout,
"xpcInspector": xpcInspector,
"reportError": Cu.reportError,
},
loadInSandbox: loadInSandbox,
modules: {
"Services": {},
"chrome": chrome,
"promise": Promise,
"Debugger": Debugger,
"xpcInspector": xpcInspector,
"Timer": Object.create(Timer)
},
paths: {
"": "resource://gre/modules/commonjs/",