mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1164564 - Clean up the worker loader;r=jlong
This commit is contained in:
parent
9b7d668a4b
commit
794ef605d5
@ -120,7 +120,7 @@ exports.zip = function zip(a, b) {
|
||||
*/
|
||||
exports.executeSoon = function executeSoon(aFn) {
|
||||
if (isWorker) {
|
||||
require("Timer").setTimeout(aFn, 0);
|
||||
setImmediate(aFn);
|
||||
} else {
|
||||
Services.tm.mainThread.dispatch({
|
||||
run: exports.makeInfallible(aFn)
|
||||
|
@ -13,7 +13,7 @@
|
||||
// interchangable on the main thread, making them easier to test.
|
||||
//
|
||||
// On the worker thread, some of these modules, in particular those that rely on
|
||||
// the use of Components and for which the worker debugger doesn't provide an
|
||||
// the use of Components, and for which the worker debugger doesn't provide an
|
||||
// alternative API, will be replaced by vacuous objects. Consequently, they can
|
||||
// still be required, but any attempts to use them will lead to an exception.
|
||||
|
||||
@ -22,18 +22,37 @@ this.EXPORTED_SYMBOLS = ["WorkerDebuggerLoader", "worker"];
|
||||
// Some notes on module ids and URLs:
|
||||
//
|
||||
// An id is either a relative id or an absolute id. An id is relative if and
|
||||
// only if it starts with a dot. An absolute id is a normalized id if and only if
|
||||
// it contains no redundant components. Every normalized id is a URL.
|
||||
// only if it starts with a dot. An absolute id is a normalized id if and only
|
||||
// if it contains no redundant components.
|
||||
//
|
||||
// A URL is either an absolute URL or a relative URL. A URL is absolute if and
|
||||
// only if it starts with a scheme name followed by a colon and 2 slashes.
|
||||
// Every normalized id is a URL. A URL is either an absolute URL or a relative
|
||||
// URL. A URL is absolute if and only if it starts with a scheme name followed
|
||||
// by a colon and 2 or 3 slashes.
|
||||
|
||||
// Resolve the given relative id to an absolute id.
|
||||
/**
|
||||
* Convert the given relative id to an absolute id.
|
||||
*
|
||||
* @param String id
|
||||
* The relative id to be resolved.
|
||||
* @param String baseId
|
||||
* The absolute base id to resolve the relative id against.
|
||||
*
|
||||
* @return String
|
||||
* An absolute id
|
||||
*/
|
||||
function resolveId(id, baseId) {
|
||||
return baseId + "/../" + id;
|
||||
};
|
||||
|
||||
// Convert the given absolute id to a normalized id.
|
||||
/**
|
||||
* Convert the given absolute id to a normalized id.
|
||||
*
|
||||
* @param String id
|
||||
* The absolute id to be normalized.
|
||||
*
|
||||
* @return String
|
||||
* A normalized id.
|
||||
*/
|
||||
function normalizeId(id) {
|
||||
// An id consists of an optional root and a path. A root consists of either
|
||||
// a scheme name followed by 2 or 3 slashes, or a single slash. Slashes in the
|
||||
@ -49,8 +68,7 @@ function normalizeId(id) {
|
||||
case "..":
|
||||
if (stack.length === 0) {
|
||||
if (root !== undefined) {
|
||||
throw new Error("can't convert absolute id " + id + " to " +
|
||||
"normalized id because it's going past root!");
|
||||
throw new Error("Can't normalize absolute id '" + id + "'!");
|
||||
} else {
|
||||
stack.push("..");
|
||||
}
|
||||
@ -71,7 +89,15 @@ function normalizeId(id) {
|
||||
return (root ? root : "") + stack.join("/");
|
||||
}
|
||||
|
||||
// Create a module object with the given id.
|
||||
/**
|
||||
* Create a module object with the given normalized id.
|
||||
*
|
||||
* @param String
|
||||
* The normalized id of the module to be created.
|
||||
*
|
||||
* @return Object
|
||||
* A module with the given id.
|
||||
*/
|
||||
function createModule(id) {
|
||||
return Object.create(null, {
|
||||
// CommonJS specifies the id property to be non-configurable and
|
||||
@ -94,31 +120,42 @@ function createModule(id) {
|
||||
});
|
||||
};
|
||||
|
||||
// Create a CommonJS loader with the following options:
|
||||
// - createSandbox:
|
||||
// A function that will be used to create sandboxes. It takes the name and
|
||||
// prototype of the sandbox to be created, and should return the newly
|
||||
// created sandbox as result. This option is mandatory.
|
||||
// - globals:
|
||||
// A map of built-in globals that will be exposed to every module. Defaults
|
||||
// to the empty map.
|
||||
// - loadInSandbox:
|
||||
// A function that will be used to load scripts in sandboxes. It takes the
|
||||
// URL from which and the sandbox in which the script is to be loaded, and
|
||||
// should not return a result. This option is mandatory.
|
||||
// - modules:
|
||||
// A map of built-in modules that will be added to the module cache.
|
||||
// Defaults to the empty map.
|
||||
// - paths:
|
||||
// A map of paths to base URLs that will be used to resolve relative URLs to
|
||||
// absolute URLS. Defaults to the empty map.
|
||||
// - resolve:
|
||||
// A function that will be used to resolve relative ids to absolute ids. It
|
||||
// takes the relative id of the module to be required and the normalized id
|
||||
// of the requiring module as arguments, and should return the absolute id
|
||||
// of the module to be required as result. Defaults to resolveId above.
|
||||
/**
|
||||
* Create a CommonJS loader with the following options:
|
||||
* - createSandbox:
|
||||
* A function that will be used to create sandboxes. It should take the name
|
||||
* and prototype of the sandbox to be created, and return the newly created
|
||||
* sandbox as result. This option is required.
|
||||
* - globals:
|
||||
* A map of names to built-in globals that will be exposed to every module.
|
||||
* Defaults to the empty map.
|
||||
* - loadSubScript:
|
||||
* A function that will be used to load scripts in sandboxes. It should take
|
||||
* the URL from and the sandbox in which the script is to be loaded, and not
|
||||
* return a result. This option is required.
|
||||
* - modules:
|
||||
* A map from normalized ids to built-in modules that will be added to the
|
||||
* module cache. Defaults to the empty map.
|
||||
* - paths:
|
||||
* A map of paths to base URLs that will be used to resolve relative URLs to
|
||||
* absolute URLS. Defaults to the empty map.
|
||||
* - resolve:
|
||||
* A function that will be used to resolve relative ids to absolute ids. It
|
||||
* should take the relative id of a module to be required and the absolute
|
||||
* id of the requiring module as arguments, and return the absolute id of
|
||||
* the module to be required as result. Defaults to resolveId above.
|
||||
*/
|
||||
function WorkerDebuggerLoader(options) {
|
||||
// Resolve the given relative URL to an absolute URL.
|
||||
/**
|
||||
* Convert the given relative URL to an absolute URL, using the map of paths
|
||||
* given below.
|
||||
*
|
||||
* @param String url
|
||||
* The relative URL to be resolved.
|
||||
*
|
||||
* @return String
|
||||
* An absolute URL.
|
||||
*/
|
||||
function resolveURL(url) {
|
||||
let found = false;
|
||||
for (let [path, baseURL] of paths) {
|
||||
@ -129,19 +166,25 @@ function WorkerDebuggerLoader(options) {
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
throw new Error("can't resolve relative URL " + url + " to absolute " +
|
||||
"URL!");
|
||||
throw new Error("Can't resolve relative URL '" + url + "'!");
|
||||
}
|
||||
|
||||
// If the url has no extension, use ".js" by default.
|
||||
return url.endsWith(".js") ? url : url + ".js";
|
||||
}
|
||||
|
||||
// Load the given module with the given url.
|
||||
/**
|
||||
* Load the given module with the given url.
|
||||
*
|
||||
* @param Object module
|
||||
* The module object to be loaded.
|
||||
* @param String url
|
||||
* The URL to load the module from.
|
||||
*/
|
||||
function loadModule(module, url) {
|
||||
// CommonJS specifies 3 free variables named require, exports, and module,
|
||||
// that must be exposed to every module, so define these as properties on
|
||||
// the sandbox prototype. Additional built-in globals are exposed by making
|
||||
// CommonJS specifies 3 free variables: require, exports, and module. These
|
||||
// must be exposed to every module, so define these as properties on the
|
||||
// sandbox prototype. Additional built-in globals are exposed by making
|
||||
// the map of built-in globals the prototype of the sandbox prototype.
|
||||
let prototype = Object.create(globals);
|
||||
prototype.Components = {};
|
||||
@ -151,29 +194,37 @@ function WorkerDebuggerLoader(options) {
|
||||
|
||||
let sandbox = createSandbox(url, prototype);
|
||||
try {
|
||||
loadInSandbox(url, sandbox);
|
||||
loadSubScript(url, sandbox);
|
||||
} catch (error) {
|
||||
if (/^Error opening input stream/.test(String(error))) {
|
||||
throw new Error("can't load module " + module.id + " with url " + url +
|
||||
"!");
|
||||
throw new Error("Can't load module '" + module.id + "' with url '" +
|
||||
url + "'!");
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
// The value of exports may have been changed by the module script, so only
|
||||
// freeze it if it is still an object.
|
||||
// The value of exports may have been changed by the module script, so
|
||||
// freeze it if and only if it is still an object.
|
||||
if (typeof module.exports === "object" && module.exports !== null) {
|
||||
Object.freeze(module.exports);
|
||||
}
|
||||
};
|
||||
|
||||
// Create a require function for the given requirer. If no requirer is given,
|
||||
// create a require function for top-level modules instead.
|
||||
/**
|
||||
* Create a require function for the given module. If no module is given,
|
||||
* create a require function for the top-level module instead.
|
||||
*
|
||||
* @param Object requirer
|
||||
* The module for which the require function is to be created.
|
||||
*
|
||||
* @return Function
|
||||
* A require function for the given module.
|
||||
*/
|
||||
function createRequire(requirer) {
|
||||
return function require(id) {
|
||||
// Make sure an id was passed.
|
||||
if (id === undefined) {
|
||||
throw new Error("can't require module without id!");
|
||||
throw new Error("Can't require module without id!");
|
||||
}
|
||||
|
||||
// Built-in modules are cached by id rather than URL, so try to find the
|
||||
@ -183,11 +234,11 @@ function WorkerDebuggerLoader(options) {
|
||||
// Failed to find the module to be required by id, so convert the id to
|
||||
// a URL and try again.
|
||||
|
||||
// If the id is relative, resolve it to an absolute id.
|
||||
// If the id is relative, convert it to an absolute id.
|
||||
if (id.startsWith(".")) {
|
||||
if (requirer === undefined) {
|
||||
throw new Error("can't require top-level module with relative id " +
|
||||
id + "!");
|
||||
throw new Error("Can't require top-level module with relative id " +
|
||||
"'" + id + "'!");
|
||||
}
|
||||
id = resolve(id, requirer.id);
|
||||
}
|
||||
@ -207,7 +258,7 @@ function WorkerDebuggerLoader(options) {
|
||||
module = modules[url];
|
||||
if (module === undefined) {
|
||||
// Failed to find the module to be required in the cache, so create
|
||||
// a new module, load it with the given URL, and add it to the cache.
|
||||
// a new module, load it from the given URL, and add it to the cache.
|
||||
|
||||
// Add modules to the cache early so that any recursive calls to
|
||||
// require for the same module will return the partially-loaded module
|
||||
@ -219,7 +270,7 @@ function WorkerDebuggerLoader(options) {
|
||||
} catch (error) {
|
||||
// If the module failed to load, remove it from the cache so that
|
||||
// subsequent calls to require for the same module will trigger a
|
||||
// new load instead of returning a partially-loaded module from
|
||||
// new load, instead of returning a partially-loaded module from
|
||||
// the cache.
|
||||
delete modules[url];
|
||||
throw error;
|
||||
@ -235,11 +286,11 @@ function WorkerDebuggerLoader(options) {
|
||||
|
||||
let createSandbox = options.createSandbox;
|
||||
let globals = options.globals || Object.create(null);
|
||||
let loadInSandbox = options.loadInSandbox;
|
||||
let loadSubScript = options.loadSubScript;
|
||||
|
||||
// Create the module cache by converting each entry in the map of built-in
|
||||
// modules to a module object with its exports property set to a frozen
|
||||
// version of the original entry.
|
||||
// Create the module cache, by converting each entry in the map from
|
||||
// normalized ids to built-in modules to a module object, with the exports
|
||||
// property of each module set to a frozen version of the original entry.
|
||||
let modules = options.modules || {};
|
||||
for (let id in modules) {
|
||||
let module = createModule(id);
|
||||
@ -247,10 +298,10 @@ function WorkerDebuggerLoader(options) {
|
||||
modules[id] = module;
|
||||
}
|
||||
|
||||
// Convert the map of paths to baseURLs into an array for use by resolveURL.
|
||||
// The array is sorted from longest to shortest path so the longest path will
|
||||
// always be the first to be found.
|
||||
let paths = options.paths || {};
|
||||
// Convert the map of paths to base URLs into an array for use by resolveURL.
|
||||
// The array is sorted from longest to shortest path to ensure that the
|
||||
// longest path is always the first to be found.
|
||||
let paths = options.paths || Object.create(null);
|
||||
paths = Object.keys(paths)
|
||||
.sort((a, b) => b.length - a.length)
|
||||
.map(path => [path, paths[path]]);
|
||||
@ -262,95 +313,157 @@ function WorkerDebuggerLoader(options) {
|
||||
|
||||
this.WorkerDebuggerLoader = WorkerDebuggerLoader;
|
||||
|
||||
const chrome = { CC: undefined, Cc: undefined, ChromeWorker: undefined,
|
||||
Cm: undefined, Ci: undefined, Cu: undefined,
|
||||
Cr: undefined, components: undefined };
|
||||
// The following APIs rely on the use of Components, and the worker debugger
|
||||
// does not provide alternative definitions for them. Consequently, they are
|
||||
// stubbed out both on the main thread and worker threads.
|
||||
|
||||
// The default instance of the worker debugger loader is defined differently
|
||||
// depending on whether it is loaded from the main thread or a worker thread.
|
||||
if (typeof Components === "object") {
|
||||
const { Constructor: CC, classes: Cc, manager: Cm, interfaces: Ci,
|
||||
results: Cr, utils: Cu } = Components;
|
||||
let PromiseDebugging = {
|
||||
getState: function () {
|
||||
throw new Error("PromiseDebugging is not available in workers!");
|
||||
}
|
||||
};
|
||||
|
||||
const principal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')();
|
||||
let chrome = {
|
||||
CC: undefined,
|
||||
Cc: undefined,
|
||||
ChromeWorker: undefined,
|
||||
Cm: undefined,
|
||||
Ci: undefined,
|
||||
Cu: undefined,
|
||||
Cr: undefined,
|
||||
components: undefined
|
||||
};
|
||||
|
||||
// Create a sandbox with the given name and prototype.
|
||||
const createSandbox = function (name, prototype) {
|
||||
return Cu.Sandbox(principal, {
|
||||
invisibleToDebugger: true,
|
||||
sandboxName: name,
|
||||
sandboxPrototype: prototype,
|
||||
wantComponents: false,
|
||||
wantXrays: false
|
||||
let loader = {
|
||||
lazyGetter: function (object, name, lambda) {
|
||||
Object.defineProperty(object, name, {
|
||||
get: function () {
|
||||
delete object[name];
|
||||
return object[name] = lambda.apply(object);
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
});
|
||||
};
|
||||
},
|
||||
lazyImporter: function () {
|
||||
throw new Error("Can't import JSM from worker thread!");
|
||||
},
|
||||
lazyServiceGetter: function () {
|
||||
throw new Error("Can't import XPCOM service from worker thread!");
|
||||
},
|
||||
lazyRequireGetter: function (obj, property, module, destructure) {
|
||||
Object.defineProperty(obj, property, {
|
||||
get: () => destructure ? worker.require(module)[property]
|
||||
: worker.require(module || property)
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const loadSubScript = Cc['@mozilla.org/moz/jssubscript-loader;1'].
|
||||
getService(Ci.mozIJSSubScriptLoader).loadSubScript;
|
||||
// The following APIs are defined differently depending on whether we are on the
|
||||
// main thread or a worker thread. On the main thread, we use the Components
|
||||
// object to implement them. On worker threads, we use the APIs provided by
|
||||
// the worker debugger.
|
||||
|
||||
// Load a script from the given URL in the given sandbox.
|
||||
const loadInSandbox = function (url, sandbox) {
|
||||
loadSubScript(url, sandbox, "UTF-8");
|
||||
};
|
||||
let {
|
||||
Debugger,
|
||||
createSandbox,
|
||||
dump,
|
||||
loadSubScript,
|
||||
reportError,
|
||||
setImmediate,
|
||||
xpcInspector
|
||||
} = (function () {
|
||||
if (typeof Components === "object") { // Main thread
|
||||
let {
|
||||
Constructor: CC,
|
||||
classes: Cc,
|
||||
manager: Cm,
|
||||
interfaces: Ci,
|
||||
results: Cr,
|
||||
utils: Cu
|
||||
} = Components;
|
||||
|
||||
// Define the Debugger object in a sandbox to ensure that the this passed to
|
||||
// addDebuggerToGlobal is a global.
|
||||
let sandbox = Cu.Sandbox(principal, {});
|
||||
Cu.evalInSandbox(
|
||||
"Components.utils.import('resource://gre/modules/jsdebugger.jsm');" +
|
||||
"addDebuggerToGlobal(this);",
|
||||
sandbox
|
||||
);
|
||||
const Debugger = sandbox.Debugger;
|
||||
let principal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')();
|
||||
|
||||
const Timer = Cu.import("resource://gre/modules/Timer.jsm", {});
|
||||
const xpcInspector = Cc["@mozilla.org/jsinspector;1"].
|
||||
// To ensure that the this passed to addDebuggerToGlobal is a global, the
|
||||
// Debugger object needs to be defined in a sandbox.
|
||||
let sandbox = Cu.Sandbox(principal, {});
|
||||
Cu.evalInSandbox(
|
||||
"Components.utils.import('resource://gre/modules/jsdebugger.jsm');" +
|
||||
"addDebuggerToGlobal(this);",
|
||||
sandbox
|
||||
);
|
||||
let Debugger = sandbox.Debugger;
|
||||
|
||||
let createSandbox = function(name, prototype) {
|
||||
return Cu.Sandbox(principal, {
|
||||
invisibleToDebugger: true,
|
||||
sandboxName: name,
|
||||
sandboxPrototype: prototype,
|
||||
wantComponents: false,
|
||||
wantXrays: false
|
||||
});
|
||||
};
|
||||
|
||||
let subScriptLoader = Cc['@mozilla.org/moz/jssubscript-loader;1'].
|
||||
getService(Ci.mozIJSSubScriptLoader);
|
||||
|
||||
let loadSubScript = function (url, sandbox) {
|
||||
subScriptLoader.loadSubScript(url, sandbox, "UTF-8");
|
||||
};
|
||||
|
||||
let reportError = Cu.reportError;
|
||||
|
||||
let Timer = Cu.import("resource://gre/modules/Timer.jsm", {});
|
||||
|
||||
let setImmediate = function (callback) {
|
||||
Timer.setTimeout(callback, 0);
|
||||
}
|
||||
|
||||
let xpcInspector = Cc["@mozilla.org/jsinspector;1"].
|
||||
getService(Ci.nsIJSInspector);
|
||||
|
||||
let worker = this.worker = new WorkerDebuggerLoader({
|
||||
createSandbox: createSandbox,
|
||||
globals: {
|
||||
"isWorker": true,
|
||||
"reportError": Cu.reportError,
|
||||
"loader": {
|
||||
lazyGetter: function (aObject, aName, aLambda) {
|
||||
Object.defineProperty(aObject, aName, {
|
||||
get: function () {
|
||||
delete aObject[aName];
|
||||
return aObject[aName] = aLambda.apply(aObject);
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
});
|
||||
},
|
||||
lazyImporter: function () { throw new Error("Can't import JSM from worker debugger server") },
|
||||
lazyServiceGetter: function () { throw new Error("Can't import XPCOM from worker debugger server") },
|
||||
lazyRequireGetter: function (obj, property, module, destructure) {
|
||||
Object.defineProperty(obj, property, {
|
||||
get: () => destructure
|
||||
? worker.require(module)[property]
|
||||
: worker.require(module || property)
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
loadInSandbox: loadInSandbox,
|
||||
modules: {
|
||||
"Services": {},
|
||||
"chrome": chrome,
|
||||
"promise": Promise,
|
||||
"Debugger": Debugger,
|
||||
"xpcInspector": xpcInspector,
|
||||
"Timer": Object.create(Timer),
|
||||
"PromiseDebugging": PromiseDebugging
|
||||
},
|
||||
paths: {
|
||||
"": "resource://gre/modules/commonjs/",
|
||||
"devtools": "resource:///modules/devtools",
|
||||
"devtools/server": "resource://gre/modules/devtools/server",
|
||||
"devtools/toolkit": "resource://gre/modules/devtools",
|
||||
"source-map": "resource://gre/modules/devtools/source-map",
|
||||
"xpcshell-test": "resource://test",
|
||||
}
|
||||
});
|
||||
}
|
||||
return {
|
||||
Debugger,
|
||||
createSandbox,
|
||||
dump,
|
||||
loadSubScript,
|
||||
reportError,
|
||||
setImmediate,
|
||||
xpcInspector
|
||||
};
|
||||
} else { // Worker thread
|
||||
throw new Error("Not yet implemented!");
|
||||
}
|
||||
}).call(this);
|
||||
|
||||
// Create the default instance of the worker loader, using the APIs we defined
|
||||
// above.
|
||||
|
||||
this.worker = new WorkerDebuggerLoader({
|
||||
createSandbox: createSandbox,
|
||||
globals: {
|
||||
"isWorker": true,
|
||||
"dump": dump,
|
||||
"loader": loader,
|
||||
"reportError": reportError,
|
||||
"setImmediate": setImmediate
|
||||
},
|
||||
loadSubScript: loadSubScript,
|
||||
modules: {
|
||||
"Debugger": Debugger,
|
||||
"PromiseDebugging": PromiseDebugging,
|
||||
"Services": Object.create(null),
|
||||
"chrome": chrome,
|
||||
"promise": Promise,
|
||||
"xpcInspector": xpcInspector
|
||||
},
|
||||
paths: {
|
||||
"": "resource://gre/modules/commonjs/",
|
||||
"devtools": "resource:///modules/devtools",
|
||||
"devtools/server": "resource://gre/modules/devtools/server",
|
||||
"devtools/toolkit": "resource://gre/modules/devtools",
|
||||
"source-map": "resource://gre/modules/devtools/source-map",
|
||||
"xpcshell-test": "resource://test"
|
||||
}
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user