зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1392602 - Remove jetpack specifics from DevTools loader. r=jdescottes
* Remove notion of "main" module * Remove notion of "isNative" and only use Loader.resolve algorithm MozReview-Commit-ID: 7GOJELAh375 --HG-- extra : rebase_source : 4dc2499626ddcdeb6848584ff679c8e48994b11b
This commit is contained in:
Родитель
71e4f1790c
Коммит
3541b824e8
|
@ -20,18 +20,9 @@ const { normalize, dirname } = Cu.import("resource://gre/modules/osfile/ospath_u
|
|||
XPCOMUtils.defineLazyServiceGetter(this, "resProto",
|
||||
"@mozilla.org/network/protocol;1?name=resource",
|
||||
"nsIResProtocolHandler");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "zipCache",
|
||||
"@mozilla.org/libjar/zip-reader-cache;1",
|
||||
"nsIZipReaderCache");
|
||||
|
||||
const { defineLazyGetter } = XPCOMUtils;
|
||||
|
||||
defineLazyGetter(this, "XulApp", () => {
|
||||
let xulappURI = module.uri.replace("toolkit/loader.js",
|
||||
"sdk/system/xul-app.jsm");
|
||||
return Cu.import(xulappURI, {});
|
||||
});
|
||||
|
||||
// Define some shortcuts.
|
||||
const bind = Function.call.bind(Function.bind);
|
||||
const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
|
||||
|
@ -41,45 +32,6 @@ function* getOwnIdentifiers(x) {
|
|||
yield* Object.getOwnPropertySymbols(x);
|
||||
}
|
||||
|
||||
const NODE_MODULES = new Set([
|
||||
"assert",
|
||||
"buffer_ieee754",
|
||||
"buffer",
|
||||
"child_process",
|
||||
"cluster",
|
||||
"console",
|
||||
"constants",
|
||||
"crypto",
|
||||
"_debugger",
|
||||
"dgram",
|
||||
"dns",
|
||||
"domain",
|
||||
"events",
|
||||
"freelist",
|
||||
"fs",
|
||||
"http",
|
||||
"https",
|
||||
"_linklist",
|
||||
"module",
|
||||
"net",
|
||||
"os",
|
||||
"path",
|
||||
"punycode",
|
||||
"querystring",
|
||||
"readline",
|
||||
"repl",
|
||||
"stream",
|
||||
"string_decoder",
|
||||
"sys",
|
||||
"timers",
|
||||
"tls",
|
||||
"tty",
|
||||
"url",
|
||||
"util",
|
||||
"vm",
|
||||
"zlib",
|
||||
]);
|
||||
|
||||
const COMPONENT_ERROR = '`Components` is not available in this context.\n' +
|
||||
'Functionality provided by Components may be available in an SDK\n' +
|
||||
'module: https://developer.mozilla.org/en-US/Add-ons/SDK \n\n' +
|
||||
|
@ -189,154 +141,6 @@ function serializeStack(frames) {
|
|||
}, "");
|
||||
}
|
||||
|
||||
class DefaultMap extends Map {
|
||||
constructor(createItem, items = undefined) {
|
||||
super(items);
|
||||
|
||||
this.createItem = createItem;
|
||||
}
|
||||
|
||||
get(key) {
|
||||
if (!this.has(key)) {
|
||||
this.set(key, this.createItem(key));
|
||||
}
|
||||
|
||||
return super.get(key);
|
||||
}
|
||||
}
|
||||
|
||||
const urlCache = {
|
||||
/**
|
||||
* Returns a list of fully-qualified URLs for entries within the zip
|
||||
* file at the given URI which are either directories or files with a
|
||||
* .js or .json extension.
|
||||
*
|
||||
* @param {nsIJARURI} uri
|
||||
* @param {string} baseURL
|
||||
* The original base URL, prior to resolution.
|
||||
*
|
||||
* @returns {Set<string>}
|
||||
*/
|
||||
getZipFileContents(uri, baseURL) {
|
||||
// Make sure the path has a trailing slash, and strip off the leading
|
||||
// slash, so that we can easily check whether it is a path prefix.
|
||||
let basePath = addTrailingSlash(uri.JAREntry).slice(1);
|
||||
let file = uri.JARFile.QueryInterface(Ci.nsIFileURL).file;
|
||||
|
||||
let enumerator = zipCache.getZip(file).findEntries("(*.js|*.json|*/)");
|
||||
|
||||
let results = new Set();
|
||||
for (let entry of XPCOMUtils.IterStringEnumerator(enumerator)) {
|
||||
if (entry.startsWith(basePath)) {
|
||||
let path = entry.slice(basePath.length);
|
||||
|
||||
results.add(baseURL + path);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
},
|
||||
|
||||
zipContentsCache: new DefaultMap(baseURL => {
|
||||
let uri = NetUtil.newURI(baseURL);
|
||||
|
||||
if (baseURL.startsWith("resource:")) {
|
||||
uri = NetUtil.newURI(resProto.resolveURI(uri));
|
||||
}
|
||||
|
||||
if (uri instanceof Ci.nsIJARURI) {
|
||||
return urlCache.getZipFileContents(uri, baseURL);
|
||||
}
|
||||
|
||||
return null;
|
||||
}),
|
||||
|
||||
filesCache: new DefaultMap(url => {
|
||||
try {
|
||||
let uri = NetUtil.newURI(url).QueryInterface(Ci.nsIFileURL);
|
||||
|
||||
return uri.file.exists();
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}),
|
||||
|
||||
resolutionCache: new DefaultMap(fullId => {
|
||||
return (resolveAsFile(fullId) ||
|
||||
resolveAsDirectory(fullId));
|
||||
}),
|
||||
|
||||
nodeModulesCache: new Map(),
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference]),
|
||||
|
||||
observe() {
|
||||
// Clear any module resolution caches when the startup cache is flushed,
|
||||
// since it probably means we're loading new copies of extensions.
|
||||
this.zipContentsCache.clear();
|
||||
this.filesCache.clear();
|
||||
this.resolutionCache.clear();
|
||||
this.nodeModulesCache.clear();
|
||||
},
|
||||
|
||||
getNodeModulePaths(rootURI, start) {
|
||||
let url = join(rootURI, start);
|
||||
|
||||
if (this.nodeModulesCache.has(url))
|
||||
return this.nodeModulesCache.get(url);
|
||||
|
||||
let result = Array.from(getNodeModulePaths(rootURI, start));
|
||||
this.nodeModulesCache.set(url, result);
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the base URL for the given URL, if one can be determined. For
|
||||
* a resource: URL, this is the root of the resource package. For a jar:
|
||||
* URL, it is the root of the JAR file. Otherwise, null is returned.
|
||||
*
|
||||
* @param {string} url
|
||||
* @returns {string?}
|
||||
*/
|
||||
getBaseURL(url) {
|
||||
// By using simple string matching for the common case of resource: URLs
|
||||
// backed by jar: URLs, we can avoid creating any nsIURI objects for the
|
||||
// common case where the JAR contents are already cached.
|
||||
if (url.startsWith("resource://")) {
|
||||
return /^resource:\/\/[^\/]+\//.exec(url)[0];
|
||||
}
|
||||
|
||||
let uri = NetUtil.newURI(url);
|
||||
if (uri instanceof Ci.nsIJARURI) {
|
||||
return `jar:${uri.JARFile.spec}!/`;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns true if the target of the given URL exists as a local file,
|
||||
* or as an entry in a local zip file.
|
||||
*
|
||||
* @param {string} url
|
||||
* @returns {boolean}
|
||||
*/
|
||||
exists(url) {
|
||||
if (!/\.(?:js|json)$/.test(url)) {
|
||||
url = addTrailingSlash(url);
|
||||
}
|
||||
|
||||
let baseURL = this.getBaseURL(url);
|
||||
let scripts = baseURL && this.zipContentsCache.get(baseURL);
|
||||
if (scripts) {
|
||||
return scripts.has(url);
|
||||
}
|
||||
|
||||
return this.filesCache.get(url);
|
||||
},
|
||||
}
|
||||
addObserver(urlCache, "startupcache-invalidate", true);
|
||||
|
||||
function readURI(uri) {
|
||||
let nsURI = NetUtil.newURI(uri);
|
||||
if (nsURI.scheme == "resource") {
|
||||
|
@ -553,13 +357,6 @@ const load = iced(function load(loader, module) {
|
|||
module = loadModuleHook(module, require);
|
||||
}
|
||||
|
||||
if (loader.checkCompatibility) {
|
||||
let err = XulApp.incompatibility(module);
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// Only freeze the exports object if we created it ourselves. Modules
|
||||
// which completely replace the exports object and still want it
|
||||
// frozen need to freeze it themselves.
|
||||
|
@ -600,111 +397,6 @@ const resolve = iced(function resolve(id, base) {
|
|||
return resolved;
|
||||
});
|
||||
|
||||
// Attempts to load `path` and then `path.js`
|
||||
// Returns `path` with valid file, or `undefined` otherwise
|
||||
function resolveAsFile(path) {
|
||||
// Append '.js' to path name unless it's another support filetype
|
||||
path = normalizeExt(path);
|
||||
if (urlCache.exists(path)) {
|
||||
return path;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Attempts to load `path/package.json`'s `main` entry,
|
||||
// followed by `path/index.js`, or `undefined` otherwise
|
||||
function resolveAsDirectory(path) {
|
||||
try {
|
||||
// If `path/package.json` exists, parse the `main` entry
|
||||
// and attempt to load that
|
||||
let manifestPath = addTrailingSlash(path) + 'package.json';
|
||||
|
||||
let main = (urlCache.exists(manifestPath) &&
|
||||
getManifestMain(JSON.parse(readURI(manifestPath))));
|
||||
if (main) {
|
||||
let found = resolveAsFile(join(path, main));
|
||||
if (found) {
|
||||
return found
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
return resolveAsFile(addTrailingSlash(path) + 'index.js');
|
||||
}
|
||||
|
||||
function resolveRelative(rootURI, modulesDir, id) {
|
||||
let fullId = join(rootURI, modulesDir, id);
|
||||
|
||||
let resolvedPath = urlCache.resolutionCache.get(fullId);
|
||||
if (resolvedPath) {
|
||||
return './' + resolvedPath.slice(rootURI.length);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// From `resolve` module
|
||||
// https://github.com/substack/node-resolve/blob/master/lib/node-modules-paths.js
|
||||
function* getNodeModulePaths(rootURI, start) {
|
||||
let moduleDir = 'node_modules';
|
||||
|
||||
let parts = start.split('/');
|
||||
while (parts.length) {
|
||||
let leaf = parts.pop();
|
||||
let path = [...parts, leaf, moduleDir].join("/");
|
||||
if (leaf !== moduleDir && urlCache.exists(join(rootURI, path))) {
|
||||
yield path;
|
||||
}
|
||||
}
|
||||
|
||||
if (urlCache.exists(join(rootURI, moduleDir))) {
|
||||
yield moduleDir;
|
||||
}
|
||||
}
|
||||
|
||||
// Node-style module lookup
|
||||
// Takes an id and path and attempts to load a file using node's resolving
|
||||
// algorithm.
|
||||
// `id` should already be resolved relatively at this point.
|
||||
// http://nodejs.org/api/modules.html#modules_all_together
|
||||
const nodeResolve = iced(function nodeResolve(id, requirer, { rootURI }) {
|
||||
// Resolve again
|
||||
id = resolve(id, requirer);
|
||||
|
||||
// If this is already an absolute URI then there is no resolution to do
|
||||
if (isAbsoluteURI(id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// we assume that extensions are correct, i.e., a directory doesnt't have '.js'
|
||||
// and a js file isn't named 'file.json.js'
|
||||
let resolvedPath;
|
||||
|
||||
if ((resolvedPath = resolveRelative(rootURI, "", id))) {
|
||||
return resolvedPath;
|
||||
}
|
||||
|
||||
// If the requirer is an absolute URI then the node module resolution below
|
||||
// won't work correctly as we prefix everything with rootURI
|
||||
if (isAbsoluteURI(requirer)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If manifest has dependencies, attempt to look up node modules
|
||||
// in the `dependencies` list
|
||||
for (let modulesDir of urlCache.getNodeModulePaths(rootURI, dirname(requirer))) {
|
||||
if ((resolvedPath = resolveRelative(rootURI, modulesDir, id))) {
|
||||
return resolvedPath;
|
||||
}
|
||||
}
|
||||
|
||||
// We would not find lookup for things like `sdk/tabs`, as that's part of
|
||||
// the alias mapping. If during `generateMap`, the runtime lookup resolves
|
||||
// with `resolveURI` -- if during runtime, then `resolve` will throw.
|
||||
return null;
|
||||
});
|
||||
|
||||
function addTrailingSlash(path) {
|
||||
return path.replace(/\/*$/, "/");
|
||||
}
|
||||
|
@ -822,14 +514,6 @@ const Require = iced(function Require(loader, requirer) {
|
|||
manifest, rootURI, isNative, requireHook
|
||||
} = loader;
|
||||
|
||||
if (isSystemURI(requirer.uri)) {
|
||||
// Built-in modules don't require the expensive module resolution
|
||||
// algorithm used by SDK add-ons, so give them the more efficient standard
|
||||
// resolve instead.
|
||||
isNative = false;
|
||||
loaderResolve = resolve;
|
||||
}
|
||||
|
||||
function require(id) {
|
||||
if (!id) // Throw if `id` is not passed.
|
||||
throw Error('You must provide a module name when calling require() from '
|
||||
|
@ -910,51 +594,7 @@ const Require = iced(function Require(loader, requirer) {
|
|||
|
||||
let requirement, uri;
|
||||
|
||||
// TODO should get native Firefox modules before doing node-style lookups
|
||||
// to save on loading time
|
||||
if (isNative) {
|
||||
let { overrides } = manifest.jetpack;
|
||||
for (let key in overrides) {
|
||||
// ignore any overrides using relative keys
|
||||
if (/^[.\/]/.test(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the override is for x -> y,
|
||||
// then using require("x/lib/z") to get reqire("y/lib/z")
|
||||
// should also work
|
||||
if (id == key || id.startsWith(key + "/")) {
|
||||
id = overrides[key] + id.substr(key.length);
|
||||
id = id.replace(/^[.\/]+/, "");
|
||||
}
|
||||
}
|
||||
|
||||
// For native modules, we want to check if it's a module specified
|
||||
// in 'modules', like `chrome`, or `@loader` -- if it exists,
|
||||
// just set the uri to skip resolution
|
||||
if (!requirement && modules[id])
|
||||
uri = requirement = id;
|
||||
|
||||
if (!requirement && !NODE_MODULES.has(id)) {
|
||||
// If `isNative` defined, this is using the new, native-style
|
||||
// loader, not cuddlefish, so lets resolve using node's algorithm
|
||||
// and get back a path that needs to be resolved via paths mapping
|
||||
// in `resolveURI`
|
||||
requirement = loaderResolve(id, requirer.id, {
|
||||
manifest: manifest,
|
||||
rootURI: rootURI
|
||||
});
|
||||
}
|
||||
|
||||
// If not found in the map, not a node module, and wasn't able to be
|
||||
// looked up, it's something
|
||||
// found in the paths most likely, like `sdk/tabs`, which should
|
||||
// be resolved relatively if needed using traditional resolve
|
||||
if (!requirement) {
|
||||
requirement = isRelative(id) ? resolve(id, requirer.id) : id;
|
||||
}
|
||||
}
|
||||
else if (modules[id]) {
|
||||
if (modules[id]) {
|
||||
uri = requirement = id;
|
||||
}
|
||||
else if (requirer) {
|
||||
|
@ -998,21 +638,9 @@ const Require = iced(function Require(loader, requirer) {
|
|||
};
|
||||
};
|
||||
|
||||
// Make `require.main === module` evaluate to true in main module scope.
|
||||
require.main = loader.main === requirer ? requirer : undefined;
|
||||
return iced(require);
|
||||
});
|
||||
|
||||
const main = iced(function main(loader, id) {
|
||||
// If no main entry provided, and native loader is used,
|
||||
// read the entry in the manifest
|
||||
if (!id && loader.isNative)
|
||||
id = getManifestMain(loader.manifest);
|
||||
let uri = resolveURI(id, loader.mapping);
|
||||
let module = loader.main = loader.modules[uri] = Module(id, uri);
|
||||
return loader.load(loader, module).exports;
|
||||
});
|
||||
|
||||
// Makes module object that is made available to CommonJS modules when they
|
||||
// are evaluated, along with `exports` and `require`.
|
||||
const Module = iced(function Module(id, uri) {
|
||||
|
@ -1124,10 +752,6 @@ function Loader(options) {
|
|||
for (let id of Object.keys(builtinModuleExports)) {
|
||||
// We resolve `uri` from `id` since modules are cached by `uri`.
|
||||
let uri = resolveURI(id, mapping);
|
||||
// In native loader, the mapping will not contain values for
|
||||
// pseudomodules -- store them as their ID rather than the URI
|
||||
if (isNative && !uri)
|
||||
uri = id;
|
||||
let module = Module(id, uri);
|
||||
|
||||
// Lazily expose built-in modules in order to
|
||||
|
@ -1177,7 +801,6 @@ function Loader(options) {
|
|||
mappingCache: { enumerable: false, value: new Map() },
|
||||
// Map of module objects indexed by module URIs.
|
||||
modules: { enumerable: false, value: modules },
|
||||
metadata: { enumerable: false, value: metadata },
|
||||
useSharedGlobalSandbox: { enumerable: false, value: !!sharedGlobal },
|
||||
sharedGlobalSandbox: { enumerable: false, value: sharedGlobalSandbox },
|
||||
sharedGlobalBlocklist: { enumerable: false, value: sharedGlobalBlocklist },
|
||||
|
@ -1191,28 +814,10 @@ function Loader(options) {
|
|||
invisibleToDebugger: { enumerable: false,
|
||||
value: options.invisibleToDebugger || false },
|
||||
load: { enumerable: false, value: options.load || load },
|
||||
checkCompatibility: { enumerable: false, value: checkCompatibility },
|
||||
requireHook: { enumerable: false, value: options.requireHook },
|
||||
loadModuleHook: { enumerable: false, value: options.loadModuleHook },
|
||||
// Main (entry point) module, it can be set only once, since loader
|
||||
// instance can have only one main module.
|
||||
main: new function() {
|
||||
let main;
|
||||
return {
|
||||
enumerable: false,
|
||||
get: function() { return main; },
|
||||
// Only set main if it has not being set yet!
|
||||
set: function(module) { main = main || module; }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (isNative) {
|
||||
returnObj.isNative = { enumerable: false, value: true };
|
||||
returnObj.manifest = { enumerable: false, value: manifest };
|
||||
returnObj.rootURI = { enumerable: false, value: normalizeRootURI(rootURI) };
|
||||
}
|
||||
|
||||
return freeze(Object.create(null, returnObj));
|
||||
};
|
||||
|
||||
|
@ -1223,10 +828,3 @@ var isJSMURI = uri => uri.endsWith('.jsm');
|
|||
var isJSURI = uri => uri.endsWith('.js');
|
||||
var isAbsoluteURI = uri => /^(resource|chrome|file|jar):/.test(uri);
|
||||
var isRelative = id => id.startsWith(".");
|
||||
|
||||
// Default `main` entry to './index.js' and ensure is relative,
|
||||
// since node allows 'lib/index.js' without relative `./`
|
||||
function getManifestMain(manifest) {
|
||||
let main = manifest.main || './index.js';
|
||||
return isRelative(main) ? main : './' + main;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче