--- a/addon-sdk/source/lib/toolkit/loader.js
+++ b/addon-sdk/source/lib/toolkit/loader.js
@@ -23,22 +23,29 @@ module.metadata = {
"stability": "unstable"
};
const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu,
results: Cr, manager: Cm } = Components;
const systemPrincipal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')();
const { loadSubScript } = Cc['@mozilla.org/moz/jssubscript-loader;1'].
getService(Ci.mozIJSSubScriptLoader);
-const { notifyObservers } = Cc['@mozilla.org/observer-service;1'].
+const { addObserver, notifyObservers } = Cc['@mozilla.org/observer-service;1'].
getService(Ci.nsIObserverService);
const { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
const { join: pathJoin, normalize, dirname } = Cu.import("resource://gre/modules/osfile/ospath_unix.jsm");
+XPCOMUtils.defineLazyServiceGetter(this, "resProto",
+ "@mozilla.org/network/protocol;1?name=resource",
+ "nsIResProtocolHandler");
+XPCOMUtils.defineLazyServiceGetter(this, "zipCache",
+ "@mozilla.org/libjar/zip-reader-cache;1",
+ "nsIZipReaderCache");
+
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);
@@ -197,24 +204,150 @@ function serializeStack(frames) {
frame.fileName + ":" +
frame.lineNumber + ":" +
frame.columnNumber + "\n" +
stack;
}, "");
}
Loader.serializeStack = serializeStack;
+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;
+ }
+ }),
+
+ 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();
+ },
+
+ /**
+ * 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") {
// Resolve to a real URI, this will catch any obvious bad paths without
// logging assertions in debug builds, see bug 1135219
- let proto = Cc["@mozilla.org/network/protocol;1?name=resource"].
- getService(Ci.nsIResProtocolHandler);
- uri = proto.resolveURI(nsURI);
+ uri = resProto.resolveURI(nsURI);
}
let stream = NetUtil.newChannel({
uri: NetUtil.newURI(uri, 'UTF-8'),
loadUsingSystemPrincipal: true}
).open2();
let count = stream.available();
let data = NetUtil.readInputStreamToString(stream, count, {
@@ -222,27 +355,25 @@ function readURI(uri) {
});
stream.close();
return data;
}
// Combines all arguments into a resolved, normalized path
-function join(...paths) {
- let joined = pathJoin(...paths);
- let resolved = normalize(joined);
+function join(base, ...paths) {
+ // If this is an absolute URL, we need to normalize only the path portion,
+ // or we wind up stripping too many slashes and producing invalid URLs.
+ let match = /^((?:resource|file|chrome)\:\/\/[^\/]*|jar:[^!]+!)(.*)/.exec(base);
+ if (match) {
+ return match[1] + normalize(pathJoin(match[2], ...paths));
+ }
- // OS.File `normalize` strips out any additional slashes breaking URIs like
- // `resource://`, `resource:///`, `chrome://` or `file:///`, so we work
- // around this putting back the slashes originally given, for such schemes.
- let re = /^(resource|file|chrome)(\:\/{1,3})([^\/])/;
- let matches = joined.match(re);
-
- return resolved.replace(re, (...args) => args[1] + matches[2] + args[3]);
+ return normalize(pathJoin(base, ...paths));
}
Loader.join = join;
// Function takes set of options and returns a JS sandbox. Function may be
// passed set of options:
// - `name`: A string value which identifies the sandbox in about:memory. Will
// throw exception if omitted.
// - `principal`: String URI or `nsIPrincipal` for the sandbox. Defaults to
@@ -454,141 +585,120 @@ const resolve = iced(function resolve(id
return resolved;
});
Loader.resolve = resolve;
// Attempts to load `path` and then `path.js`
// Returns `path` with valid file, or `undefined` otherwise
function resolveAsFile(path) {
- let found;
+ // Append '.js' to path name unless it's another support filetype
+ path = normalizeExt(path);
+ if (urlCache.exists(path)) {
+ return path;
+ }
- // As per node's loader spec,
- // we first should try and load 'path' (with no extension)
- // before trying 'path.js'. We will not support this feature
- // due to performance, but may add it if necessary for adoption.
- try {
- // Append '.js' to path name unless it's another support filetype
- path = normalizeExt(path);
- readURI(path);
- found = path;
- } catch (e) {}
-
- return found;
+ 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 main = getManifestMain(JSON.parse(readURI(path + '/package.json')));
- if (main != null) {
- let tmpPath = join(path, main);
- let found = resolveAsFile(tmpPath);
- if (found)
+ 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) {}
- try {
- let tmpPath = path + '/index.js';
- readURI(tmpPath);
- return tmpPath;
- } catch (e) {}
-
- return null;
+ return resolveAsFile(addTrailingSlash(path) + 'index.js');
}
function resolveRelative(rootURI, modulesDir, id) {
let fullId = join(rootURI, modulesDir, id);
- let resolvedPath;
- if ((resolvedPath = resolveAsFile(fullId)))
+ let resolvedPath = (resolveAsFile(fullId) ||
+ resolveAsDirectory(fullId));
+ if (resolvedPath) {
return stripBase(rootURI, resolvedPath);
-
- if ((resolvedPath = resolveAsDirectory(fullId)))
- return stripBase(rootURI, resolvedPath);
+ }
return null;
}
// From `resolve` module
// https://github.com/substack/node-resolve/blob/master/lib/node-modules-paths.js
-function* getNodeModulePaths(start) {
- // Configurable in node -- do we need this to be configurable?
+function* getNodeModulePaths(rootURI, start) {
let moduleDir = 'node_modules';
let parts = start.split('/');
while (parts.length) {
let leaf = parts.pop();
- if (leaf !== moduleDir)
- yield join(...parts, leaf, moduleDir);
+ let path = join(...parts, leaf, moduleDir);
+ if (leaf !== moduleDir && urlCache.exists(join(rootURI, path))) {
+ yield path;
+ }
}
- yield moduleDir;
+ 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 = Loader.resolve(id, requirer);
// If this is already an absolute URI then there is no resolution to do
- if (isAbsoluteURI(id))
+ 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)))
+ 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))
+ if (isAbsoluteURI(requirer)) {
return null;
+ }
// If manifest has dependencies, attempt to look up node modules
// in the `dependencies` list
- for (let modulesDir of getNodeModulePaths(dirname(requirer))) {
- if ((resolvedPath = resolveRelative(rootURI, modulesDir, id)))
+ for (let modulesDir of 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;
});
-// String (`${rootURI}:${requirer}:${id}`) -> resolvedPath
-Loader.nodeResolverCache = new Map();
-
-const nodeResolveWithCache = iced(function cacheNodeResolutions(id, requirer, { rootURI }) {
- // Compute the cache key based on current arguments.
- let cacheKey = `${rootURI || ""}:${requirer}:${id}`;
-
- // Try to get the result from the cache.
- if (Loader.nodeResolverCache.has(cacheKey)) {
- return Loader.nodeResolverCache.get(cacheKey);
- }
-
- // Resolve and cache if it is not in the cache yet.
- let result = nodeResolve(id, requirer, { rootURI });
- Loader.nodeResolverCache.set(cacheKey, result);
- return result;
-});
-Loader.nodeResolve = nodeResolveWithCache;
+Loader.nodeResolve = nodeResolve;
function addTrailingSlash(path) {
return path.replace(/\/*$/, "/");
}
const resolveURI = iced(function resolveURI(id, mapping) {
// Do not resolve if already a resource URI
if (isAbsoluteURI(id))
@@ -810,19 +920,16 @@ const Module = iced(function Module(id,
uri: { value: uri }
});
});
Loader.Module = Module;
// Takes `loader`, and unload `reason` string and notifies all observers that
// they should cleanup after them-self.
const unload = iced(function unload(loader, reason) {
- // Clear the nodeResolverCache when the loader is unloaded.
- Loader.nodeResolverCache.clear();
-
// subject is a unique object created per loader instance.
// This allows any code to cleanup on loader unload regardless of how
// it was loaded. To handle unload for specific loader subject may be
// asserted against loader.destructor or require('@loader/unload')
// Note: We don not destroy loader's module cache or sandboxes map as
// some modules may do cleanup in subsequent turns of event loop. Destroying
// cache may cause module identity problems in such cases.
let subject = { wrappedJSObject: loader.destructor };
--- a/addon-sdk/source/test/test-native-loader.js
+++ b/addon-sdk/source/test/test-native-loader.js
@@ -3,77 +3,318 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict';
var {
Loader, main, unload, parseStack, resolve, nodeResolve
} = require('toolkit/loader');
var { readURI } = require('sdk/net/url');
var { all } = require('sdk/core/promise');
+var { before, after } = require('sdk/test/utils');
var testOptions = require('@test/options');
var root = module.uri.substr(0, module.uri.lastIndexOf('/'))
// The following adds Debugger constructor to the global namespace.
-const { Cu } = require('chrome');
+const { Cc, Ci, Cu } = require('chrome');
const { addDebuggerToGlobal } = Cu.import('resource://gre/modules/jsdebugger.jsm', {});
addDebuggerToGlobal(this);
+const { NetUtil } = Cu.import('resource://gre/modules/NetUtil.jsm', {});
-exports['test nodeResolve'] = function (assert) {
- let rootURI = root + '/fixtures/native-addon-test/';
- let manifest = {};
- manifest.dependencies = {};
+const resProto = Cc["@mozilla.org/network/protocol;1?name=resource"]
+ .getService(Ci.nsIResProtocolHandler);
+
+const fileRoot = resProto.resolveURI(NetUtil.newURI(root));
+
+let variants = [
+ {
+ description: "unpacked resource:",
+ getRootURI(fixture) {
+ return `${root}/fixtures/${fixture}/`;
+ },
+ },
+ {
+ description: "unpacked file:",
+ getRootURI(fixture) {
+ return `${fileRoot}/fixtures/${fixture}/`;
+ },
+ },
+ {
+ description: "packed resource:",
+ getRootURI(fixture) {
+ return `resource://${fixture}/`;
+ },
+ },
+ {
+ description: "packed jar:",
+ getRootURI(fixture) {
+ return `jar:${fileRoot}/fixtures/${fixture}.xpi!/`;
+ },
+ },
+];
- // Handles extensions
- resolveTest('../package.json', './dir/c.js', './package.json');
- resolveTest('../dir/b.js', './dir/c.js', './dir/b.js');
+let fixtures = [
+ 'native-addon-test',
+ 'native-overrides-test',
+];
+
+for (let variant of variants) {
+ exports[`test nodeResolve (${variant.description})`] = function (assert) {
+ let rootURI = variant.getRootURI('native-addon-test');
+ let manifest = {};
+ manifest.dependencies = {};
+
+ // Handles extensions
+ resolveTest('../package.json', './dir/c.js', './package.json');
+ resolveTest('../dir/b.js', './dir/c.js', './dir/b.js');
+
+ resolveTest('./dir/b', './index.js', './dir/b.js');
+ resolveTest('../index', './dir/b.js', './index.js');
+ resolveTest('../', './dir/b.js', './index.js');
+ resolveTest('./dir/a', './index.js', './dir/a.js', 'Precedence dir/a.js over dir/a/');
+ resolveTest('../utils', './dir/a.js', './utils/index.js', 'Requiring a directory defaults to dir/index.js');
+ resolveTest('../newmodule', './dir/c.js', './newmodule/lib/file.js', 'Uses package.json main in dir to load appropriate "main"');
+ resolveTest('test-math', './utils/index.js', './node_modules/test-math/index.js',
+ 'Dependencies default to their index.js');
+ resolveTest('test-custom-main', './utils/index.js', './node_modules/test-custom-main/lib/custom-entry.js',
+ 'Dependencies use "main" entry');
+ resolveTest('test-math/lib/sqrt', './utils/index.js', './node_modules/test-math/lib/sqrt.js',
+ 'Dependencies\' files can be consumed via "/"');
+
+ resolveTest('sdk/tabs/utils', './index.js', undefined,
+ 'correctly ignores SDK references in paths');
+ resolveTest('fs', './index.js', undefined,
+ 'correctly ignores built in node modules in paths');
- resolveTest('./dir/b', './index.js', './dir/b.js');
- resolveTest('../index', './dir/b.js', './index.js');
- resolveTest('../', './dir/b.js', './index.js');
- resolveTest('./dir/a', './index.js', './dir/a.js', 'Precedence dir/a.js over dir/a/');
- resolveTest('../utils', './dir/a.js', './utils/index.js', 'Requiring a directory defaults to dir/index.js');
- resolveTest('../newmodule', './dir/c.js', './newmodule/lib/file.js', 'Uses package.json main in dir to load appropriate "main"');
- resolveTest('test-math', './utils/index.js', './node_modules/test-math/index.js',
- 'Dependencies default to their index.js');
- resolveTest('test-custom-main', './utils/index.js', './node_modules/test-custom-main/lib/custom-entry.js',
- 'Dependencies use "main" entry');
- resolveTest('test-math/lib/sqrt', './utils/index.js', './node_modules/test-math/lib/sqrt.js',
- 'Dependencies\' files can be consumed via "/"');
+ resolveTest('test-add', './node_modules/test-math/index.js',
+ './node_modules/test-math/node_modules/test-add/index.js',
+ 'Dependencies\' dependencies can be found');
+
+ resolveTest('resource://gre/modules/commonjs/sdk/tabs.js', './index.js', undefined,
+ 'correctly ignores absolute URIs.');
+
+ resolveTest('../tabs', 'resource://gre/modules/commonjs/sdk/addon/bootstrap.js', undefined,
+ 'correctly ignores attempts to resolve from a module at an absolute URI.');
+
+ resolveTest('sdk/tabs', 'resource://gre/modules/commonjs/sdk/addon/bootstrap.js', undefined,
+ 'correctly ignores attempts to resolve from a module at an absolute URI.');
+
+ function resolveTest (id, requirer, expected, msg) {
+ let result = nodeResolve(id, requirer, { manifest: manifest, rootURI: rootURI });
+ assert.equal(result, expected, 'nodeResolve ' + id + ' from ' + requirer + ' ' +msg);
+ }
+ }
+
+ /*
+ // TODO not working in current env
+ exports[`test bundle (${variant.description`] = function (assert, done) {
+ loadAddon('/native-addons/native-addon-test/')
+ };
+ */
+
+ exports[`test native Loader with mappings (${variant.description})`] = function (assert, done) {
+ all([
+ getJSON('/fixtures/native-addon-test/expectedmap.json'),
+ getJSON('/fixtures/native-addon-test/package.json')
+ ]).then(([expectedMap, manifest]) => {
+
+ // Override dummy module and point it to `test-math` to see if the
+ // require is pulling from the mapping
+ expectedMap['./index.js']['./dir/dummy'] = './dir/a.js';
+
+ let rootURI = variant.getRootURI('native-addon-test');
+ let loader = Loader({
+ paths: makePaths(rootURI),
+ rootURI: rootURI,
+ manifest: manifest,
+ requireMap: expectedMap,
+ isNative: true
+ });
+
+ let program = main(loader);
+ assert.equal(program.dummyModule, 'dir/a',
+ 'The lookup uses the information given in the mapping');
+
+ testLoader(program, assert);
+ unload(loader);
+ done();
+ }).then(null, (reason) => console.error(reason));
+ };
+
+ exports[`test native Loader overrides (${variant.description})`] = function*(assert) {
+ const expectedKeys = Object.keys(require("sdk/io/fs")).join(", ");
+ const manifest = yield getJSON('/fixtures/native-overrides-test/package.json');
+ const rootURI = variant.getRootURI('native-overrides-test');
+
+ let loader = Loader({
+ paths: makePaths(rootURI),
+ rootURI: rootURI,
+ manifest: manifest,
+ metadata: manifest,
+ isNative: true
+ });
- resolveTest('sdk/tabs/utils', './index.js', undefined,
- 'correctly ignores SDK references in paths');
- resolveTest('fs', './index.js', undefined,
- 'correctly ignores built in node modules in paths');
+ let program = main(loader);
+ let fooKeys = Object.keys(program.foo).join(", ");
+ let barKeys = Object.keys(program.foo).join(", ");
+ let fsKeys = Object.keys(program.fs).join(", ");
+ let overloadKeys = Object.keys(program.overload.fs).join(", ");
+ let overloadLibKeys = Object.keys(program.overloadLib.fs).join(", ");
+
+ assert.equal(fooKeys, expectedKeys, "foo exports sdk/io/fs");
+ assert.equal(barKeys, expectedKeys, "bar exports sdk/io/fs");
+ assert.equal(fsKeys, expectedKeys, "sdk/io/fs exports sdk/io/fs");
+ assert.equal(overloadKeys, expectedKeys, "overload exports foo which exports sdk/io/fs");
+ assert.equal(overloadLibKeys, expectedKeys, "overload/lib/foo exports foo/lib/foo");
+ assert.equal(program.internal, "test", "internal exports ./lib/internal");
+ assert.equal(program.extra, true, "fs-extra was exported properly");
+
+ assert.equal(program.Tabs, "no tabs exist", "sdk/tabs exports ./lib/tabs from the add-on");
+ assert.equal(program.CoolTabs, "no tabs exist", "sdk/tabs exports ./lib/tabs from the node_modules");
+ assert.equal(program.CoolTabsLib, "a cool tabs implementation", "./lib/tabs true relative path from the node_modules");
+
+ assert.equal(program.ignore, "do not ignore this export", "../ignore override was ignored.");
+
+ unload(loader);
+ };
+
+ exports[`test invalid native Loader overrides cause no errors (${variant.description})`] = function*(assert) {
+ const manifest = yield getJSON('/fixtures/native-overrides-test/package.json');
+ const rootURI = variant.getRootURI('native-overrides-test');
+ const EXPECTED = JSON.stringify({});
- resolveTest('test-add', './node_modules/test-math/index.js',
- './node_modules/test-math/node_modules/test-add/index.js',
- 'Dependencies\' dependencies can be found');
+ let makeLoader = (rootURI, manifest) => Loader({
+ paths: makePaths(rootURI),
+ rootURI: rootURI,
+ manifest: manifest,
+ metadata: manifest,
+ isNative: true
+ });
+
+ manifest.jetpack.overrides = "string";
+ let loader = makeLoader(rootURI, manifest);
+ assert.equal(JSON.stringify(loader.manifest.jetpack.overrides), EXPECTED,
+ "setting jetpack.overrides to a string caused no errors making the loader");
+ unload(loader);
+
+ manifest.jetpack.overrides = true;
+ loader = makeLoader(rootURI, manifest);
+ assert.equal(JSON.stringify(loader.manifest.jetpack.overrides), EXPECTED,
+ "setting jetpack.overrides to a boolean caused no errors making the loader");
+ unload(loader);
+
+ manifest.jetpack.overrides = 5;
+ loader = makeLoader(rootURI, manifest);
+ assert.equal(JSON.stringify(loader.manifest.jetpack.overrides), EXPECTED,
+ "setting jetpack.overrides to a number caused no errors making the loader");
+ unload(loader);
+
+ manifest.jetpack.overrides = null;
+ loader = makeLoader(rootURI, manifest);
+ assert.equal(JSON.stringify(loader.manifest.jetpack.overrides), EXPECTED,
+ "setting jetpack.overrides to null caused no errors making the loader");
+ unload(loader);
+ };
+
+ exports[`test invalid native Loader jetpack key cause no errors (${variant.description})`] = function*(assert) {
+ const manifest = yield getJSON('/fixtures/native-overrides-test/package.json');
+ const rootURI = variant.getRootURI('native-overrides-test');
+ const EXPECTED = JSON.stringify({});
- resolveTest('resource://gre/modules/commonjs/sdk/tabs.js', './index.js', undefined,
- 'correctly ignores absolute URIs.');
+ let makeLoader = (rootURI, manifest) => Loader({
+ paths: makePaths(rootURI),
+ rootURI: rootURI,
+ manifest: manifest,
+ metadata: manifest,
+ isNative: true
+ });
+
+ manifest.jetpack = "string";
+ let loader = makeLoader(rootURI, manifest);
+ assert.equal(JSON.stringify(loader.manifest.jetpack.overrides), EXPECTED,
+ "setting jetpack.overrides to a string caused no errors making the loader");
+ unload(loader);
- resolveTest('../tabs', 'resource://gre/modules/commonjs/sdk/addon/bootstrap.js', undefined,
- 'correctly ignores attempts to resolve from a module at an absolute URI.');
+ manifest.jetpack = true;
+ loader = makeLoader(rootURI, manifest);
+ assert.equal(JSON.stringify(loader.manifest.jetpack.overrides), EXPECTED,
+ "setting jetpack.overrides to a boolean caused no errors making the loader");
+ unload(loader);
+
+ manifest.jetpack = 5;
+ loader = makeLoader(rootURI, manifest);
+ assert.equal(JSON.stringify(loader.manifest.jetpack.overrides), EXPECTED,
+ "setting jetpack.overrides to a number caused no errors making the loader");
+ unload(loader);
+
+ manifest.jetpack = null;
+ loader = makeLoader(rootURI, manifest);
+ assert.equal(JSON.stringify(loader.manifest.jetpack.overrides), EXPECTED,
+ "setting jetpack.overrides to null caused no errors making the loader");
+ unload(loader);
+ };
- resolveTest('sdk/tabs', 'resource://gre/modules/commonjs/sdk/addon/bootstrap.js', undefined,
- 'correctly ignores attempts to resolve from a module at an absolute URI.');
+ exports[`test native Loader without mappings (${variant.description})`] = function (assert, done) {
+ getJSON('/fixtures/native-addon-test/package.json').then(manifest => {
+ let rootURI = variant.getRootURI('native-addon-test');
+ let loader = Loader({
+ paths: makePaths(rootURI),
+ rootURI: rootURI,
+ manifest: manifest,
+ isNative: true
+ });
+
+ let program = main(loader);
+ testLoader(program, assert);
+ unload(loader);
+ done();
+ }).then(null, (reason) => console.error(reason));
+ };
- function resolveTest (id, requirer, expected, msg) {
- let result = nodeResolve(id, requirer, { manifest: manifest, rootURI: rootURI });
- assert.equal(result, expected, 'nodeResolve ' + id + ' from ' + requirer + ' ' +msg);
- }
+ exports[`test require#resolve with relative, dependencies (${variant.description})`] = function(assert, done) {
+ getJSON('/fixtures/native-addon-test/package.json').then(manifest => {
+ let rootURI = variant.getRootURI('native-addon-test');
+ let loader = Loader({
+ paths: makePaths(rootURI),
+ rootURI: rootURI,
+ manifest: manifest,
+ isNative: true
+ });
+
+ let program = main(loader);
+ let fixtureRoot = program.require.resolve("./").replace(/index\.js$/, "");
+
+ assert.equal(variant.getRootURI("native-addon-test"), fixtureRoot, "correct resolution root");
+ assert.equal(program.require.resolve("test-math"), fixtureRoot + "node_modules/test-math/index.js", "works with node_modules");
+ assert.equal(program.require.resolve("./newmodule"), fixtureRoot + "newmodule/lib/file.js", "works with directory mains");
+ assert.equal(program.require.resolve("./dir/a"), fixtureRoot + "dir/a.js", "works with normal relative module lookups");
+ assert.equal(program.require.resolve("modules/Promise.jsm"), "resource://gre/modules/Promise.jsm", "works with path lookups");
+
+ // TODO bug 1050422, handle loading non JS/JSM file paths
+ // assert.equal(program.require.resolve("test-assets/styles.css"), fixtureRoot + "node_modules/test-assets/styles.css",
+ // "works with different file extension lookups in dependencies");
+
+ unload(loader);
+ done();
+ }).then(null, (reason) => console.error(reason));
+ };
}
-/*
-// TODO not working in current env
-exports['test bundle'] = function (assert, done) {
- loadAddon('/native-addons/native-addon-test/')
-};
-*/
+before(exports, () => {
+ for (let fixture of fixtures) {
+ let url = `jar:${root}/fixtures/${fixture}.xpi!/`;
+
+ resProto.setSubstitution(fixture, NetUtil.newURI(url));
+ }
+});
+
+after(exports, () => {
+ for (let fixture of fixtures)
+ resProto.setSubstitution(fixture, null);
+});
exports['test JSM loading'] = function (assert, done) {
getJSON('/fixtures/jsm-package/package.json').then(manifest => {
let rootURI = root + '/fixtures/jsm-package/';
let loader = Loader({
paths: makePaths(rootURI),
rootURI: rootURI,
manifest: manifest,
@@ -95,203 +336,16 @@ exports['test JSM loading'] = function (
assert.equal(path, 10, 'JSM files resolved from path work');
assert.equal(absolute, 20, 'JSM files resolved from full resource:// work');
assert.equal(jsabsolute, 30, 'JS files resolved from full resource:// work');
}).then(done, console.error);
}).then(null, console.error);
};
-exports['test native Loader with mappings'] = function (assert, done) {
- all([
- getJSON('/fixtures/native-addon-test/expectedmap.json'),
- getJSON('/fixtures/native-addon-test/package.json')
- ]).then(([expectedMap, manifest]) => {
-
- // Override dummy module and point it to `test-math` to see if the
- // require is pulling from the mapping
- expectedMap['./index.js']['./dir/dummy'] = './dir/a.js';
-
- let rootURI = root + '/fixtures/native-addon-test/';
- let loader = Loader({
- paths: makePaths(rootURI),
- rootURI: rootURI,
- manifest: manifest,
- requireMap: expectedMap,
- isNative: true
- });
-
- let program = main(loader);
- assert.equal(program.dummyModule, 'dir/a',
- 'The lookup uses the information given in the mapping');
-
- testLoader(program, assert);
- unload(loader);
- done();
- }).then(null, (reason) => console.error(reason));
-};
-
-exports['test native Loader overrides'] = function*(assert) {
- const expectedKeys = Object.keys(require("sdk/io/fs")).join(", ");
- const manifest = yield getJSON('/fixtures/native-overrides-test/package.json');
- const rootURI = root + '/fixtures/native-overrides-test/';
-
- let loader = Loader({
- paths: makePaths(rootURI),
- rootURI: rootURI,
- manifest: manifest,
- metadata: manifest,
- isNative: true
- });
-
- let program = main(loader);
- let fooKeys = Object.keys(program.foo).join(", ");
- let barKeys = Object.keys(program.foo).join(", ");
- let fsKeys = Object.keys(program.fs).join(", ");
- let overloadKeys = Object.keys(program.overload.fs).join(", ");
- let overloadLibKeys = Object.keys(program.overloadLib.fs).join(", ");
-
- assert.equal(fooKeys, expectedKeys, "foo exports sdk/io/fs");
- assert.equal(barKeys, expectedKeys, "bar exports sdk/io/fs");
- assert.equal(fsKeys, expectedKeys, "sdk/io/fs exports sdk/io/fs");
- assert.equal(overloadKeys, expectedKeys, "overload exports foo which exports sdk/io/fs");
- assert.equal(overloadLibKeys, expectedKeys, "overload/lib/foo exports foo/lib/foo");
- assert.equal(program.internal, "test", "internal exports ./lib/internal");
- assert.equal(program.extra, true, "fs-extra was exported properly");
-
- assert.equal(program.Tabs, "no tabs exist", "sdk/tabs exports ./lib/tabs from the add-on");
- assert.equal(program.CoolTabs, "no tabs exist", "sdk/tabs exports ./lib/tabs from the node_modules");
- assert.equal(program.CoolTabsLib, "a cool tabs implementation", "./lib/tabs true relative path from the node_modules");
-
- assert.equal(program.ignore, "do not ignore this export", "../ignore override was ignored.");
-
- unload(loader);
-};
-
-exports['test invalid native Loader overrides cause no errors'] = function*(assert) {
- const manifest = yield getJSON('/fixtures/native-overrides-test/package.json');
- const rootURI = root + '/fixtures/native-overrides-test/';
- const EXPECTED = JSON.stringify({});
-
- let makeLoader = (rootURI, manifest) => Loader({
- paths: makePaths(rootURI),
- rootURI: rootURI,
- manifest: manifest,
- metadata: manifest,
- isNative: true
- });
-
- manifest.jetpack.overrides = "string";
- let loader = makeLoader(rootURI, manifest);
- assert.equal(JSON.stringify(loader.manifest.jetpack.overrides), EXPECTED,
- "setting jetpack.overrides to a string caused no errors making the loader");
- unload(loader);
-
- manifest.jetpack.overrides = true;
- loader = makeLoader(rootURI, manifest);
- assert.equal(JSON.stringify(loader.manifest.jetpack.overrides), EXPECTED,
- "setting jetpack.overrides to a boolean caused no errors making the loader");
- unload(loader);
-
- manifest.jetpack.overrides = 5;
- loader = makeLoader(rootURI, manifest);
- assert.equal(JSON.stringify(loader.manifest.jetpack.overrides), EXPECTED,
- "setting jetpack.overrides to a number caused no errors making the loader");
- unload(loader);
-
- manifest.jetpack.overrides = null;
- loader = makeLoader(rootURI, manifest);
- assert.equal(JSON.stringify(loader.manifest.jetpack.overrides), EXPECTED,
- "setting jetpack.overrides to null caused no errors making the loader");
- unload(loader);
-};
-
-exports['test invalid native Loader jetpack key cause no errors'] = function*(assert) {
- const manifest = yield getJSON('/fixtures/native-overrides-test/package.json');
- const rootURI = root + '/fixtures/native-overrides-test/';
- const EXPECTED = JSON.stringify({});
-
- let makeLoader = (rootURI, manifest) => Loader({
- paths: makePaths(rootURI),
- rootURI: rootURI,
- manifest: manifest,
- metadata: manifest,
- isNative: true
- });
-
- manifest.jetpack = "string";
- let loader = makeLoader(rootURI, manifest);
- assert.equal(JSON.stringify(loader.manifest.jetpack.overrides), EXPECTED,
- "setting jetpack.overrides to a string caused no errors making the loader");
- unload(loader);
-
- manifest.jetpack = true;
- loader = makeLoader(rootURI, manifest);
- assert.equal(JSON.stringify(loader.manifest.jetpack.overrides), EXPECTED,
- "setting jetpack.overrides to a boolean caused no errors making the loader");
- unload(loader);
-
- manifest.jetpack = 5;
- loader = makeLoader(rootURI, manifest);
- assert.equal(JSON.stringify(loader.manifest.jetpack.overrides), EXPECTED,
- "setting jetpack.overrides to a number caused no errors making the loader");
- unload(loader);
-
- manifest.jetpack = null;
- loader = makeLoader(rootURI, manifest);
- assert.equal(JSON.stringify(loader.manifest.jetpack.overrides), EXPECTED,
- "setting jetpack.overrides to null caused no errors making the loader");
- unload(loader);
-};
-
-exports['test native Loader without mappings'] = function (assert, done) {
- getJSON('/fixtures/native-addon-test/package.json').then(manifest => {
- let rootURI = root + '/fixtures/native-addon-test/';
- let loader = Loader({
- paths: makePaths(rootURI),
- rootURI: rootURI,
- manifest: manifest,
- isNative: true
- });
-
- let program = main(loader);
- testLoader(program, assert);
- unload(loader);
- done();
- }).then(null, (reason) => console.error(reason));
-};
-
-exports["test require#resolve with relative, dependencies"] = function(assert, done) {
- getJSON('/fixtures/native-addon-test/package.json').then(manifest => {
- let rootURI = root + '/fixtures/native-addon-test/';
- let loader = Loader({
- paths: makePaths(rootURI),
- rootURI: rootURI,
- manifest: manifest,
- isNative: true
- });
-
- let program = main(loader);
- let fixtureRoot = program.require.resolve("./").replace(/native-addon-test\/(.*)/, "") + "native-addon-test/";
-
- assert.equal(root + "/fixtures/native-addon-test/", fixtureRoot, "correct resolution root");
- assert.equal(program.require.resolve("test-math"), fixtureRoot + "node_modules/test-math/index.js", "works with node_modules");
- assert.equal(program.require.resolve("./newmodule"), fixtureRoot + "newmodule/lib/file.js", "works with directory mains");
- assert.equal(program.require.resolve("./dir/a"), fixtureRoot + "dir/a.js", "works with normal relative module lookups");
- assert.equal(program.require.resolve("modules/Promise.jsm"), "resource://gre/modules/Promise.jsm", "works with path lookups");
-
- // TODO bug 1050422, handle loading non JS/JSM file paths
- // assert.equal(program.require.resolve("test-assets/styles.css"), fixtureRoot + "node_modules/test-assets/styles.css",
- // "works with different file extension lookups in dependencies");
-
- unload(loader);
- done();
- }).then(null, (reason) => console.error(reason));
-};
-
function testLoader (program, assert) {
// Test 'main' entries
// no relative custom main `lib/index.js`
assert.equal(program.customMainModule, 'custom entry file',
'a node_module dependency correctly uses its `main` entry in manifest');
// relative custom main `./lib/index.js`
assert.equal(program.customMainModuleRelative, 'custom entry file relative',
'a node_module dependency correctly uses its `main` entry in manifest with relative ./');