mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-11-10 05:08:36 +02:00
***
Bug 1514594: Part 3a - Change ChromeUtils.import to return an exports object; not pollute global. r=mccr8
This changes the behavior of ChromeUtils.import() to return an exports object,
rather than a module global, in all cases except when `null` is passed as a
second argument, and changes the default behavior not to pollute the global
scope with the module's exports. Thus, the following code written for the old
model:
ChromeUtils.import("resource://gre/modules/Services.jsm");
is approximately the same as the following, in the new model:
var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
Since the two behaviors are mutually incompatible, this patch will land with a
scripted rewrite to update all existing callers to use the new model rather
than the old.
***
Bug 1514594: Part 3b - Mass rewrite all JS code to use the new ChromeUtils.import API. rs=Gijs
This was done using the followng script:
https://bitbucket.org/kmaglione/m-c-rewrites/src/tip/processors/cu-import-exports.jsm
***
Bug 1514594: Part 3c - Update ESLint plugin for ChromeUtils.import API changes. r=Standard8
Differential Revision: https://phabricator.services.mozilla.com/D16747
***
Bug 1514594: Part 3d - Remove/fix hundreds of duplicate imports from sync tests. r=Gijs
Differential Revision: https://phabricator.services.mozilla.com/D16748
***
Bug 1514594: Part 3e - Remove no-op ChromeUtils.import() calls. r=Gijs
Differential Revision: https://phabricator.services.mozilla.com/D16749
***
Bug 1514594: Part 3f.1 - Cleanup various test corner cases after mass rewrite. r=Gijs
***
Bug 1514594: Part 3f.2 - Cleanup various non-test corner cases after mass rewrite. r=Gijs
Differential Revision: https://phabricator.services.mozilla.com/D16750
--HG--
extra : rebase_source : 359574ee3064c90f33bf36c2ebe3159a24cc8895
extra : histedit_source : b93c8f42808b1599f9122d7842d2c0b3e656a594%2C64a3a4e3359dc889e2ab2b49461bab9e27fc10a7
174 lines
5.3 KiB
JavaScript
174 lines
5.3 KiB
JavaScript
"use strict";
|
|
|
|
const global = this;
|
|
|
|
const {ExtensionCommon} = ChromeUtils.import("resource://gre/modules/ExtensionCommon.jsm");
|
|
|
|
var {
|
|
BaseContext,
|
|
EventManager,
|
|
} = ExtensionCommon;
|
|
|
|
class StubContext extends BaseContext {
|
|
constructor() {
|
|
let fakeExtension = {id: "test@web.extension"};
|
|
super("testEnv", fakeExtension);
|
|
this.sandbox = Cu.Sandbox(global);
|
|
}
|
|
|
|
get cloneScope() {
|
|
return this.sandbox;
|
|
}
|
|
|
|
get principal() {
|
|
return Cu.getObjectPrincipal(this.sandbox);
|
|
}
|
|
}
|
|
|
|
|
|
add_task(async function test_post_unload_promises() {
|
|
let context = new StubContext();
|
|
|
|
let fail = result => {
|
|
ok(false, `Unexpected callback: ${result}`);
|
|
};
|
|
|
|
// Make sure promises resolve normally prior to unload.
|
|
let promises = [
|
|
context.wrapPromise(Promise.resolve()),
|
|
context.wrapPromise(Promise.reject({message: ""})).catch(() => {}),
|
|
];
|
|
|
|
await Promise.all(promises);
|
|
|
|
// Make sure promises that resolve after unload do not trigger
|
|
// resolution handlers.
|
|
|
|
context.wrapPromise(Promise.resolve("resolved"))
|
|
.then(fail);
|
|
|
|
context.wrapPromise(Promise.reject({message: "rejected"}))
|
|
.then(fail, fail);
|
|
|
|
context.unload();
|
|
|
|
// The `setTimeout` ensures that we return to the event loop after
|
|
// promise resolution, which means we're guaranteed to return after
|
|
// any micro-tasks that get enqueued by the resolution handlers above.
|
|
await new Promise(resolve => setTimeout(resolve, 0));
|
|
});
|
|
|
|
|
|
add_task(async function test_post_unload_listeners() {
|
|
let context = new StubContext();
|
|
|
|
let fire;
|
|
let manager = new EventManager({
|
|
context,
|
|
name: "EventManager",
|
|
register: _fire => {
|
|
fire = () => {
|
|
_fire.async();
|
|
};
|
|
return () => {};
|
|
},
|
|
});
|
|
|
|
let fail = event => {
|
|
ok(false, `Unexpected event: ${event}`);
|
|
};
|
|
|
|
// Check that event listeners isn't called after it has been removed.
|
|
manager.addListener(fail);
|
|
|
|
let promise = new Promise(resolve => manager.addListener(resolve));
|
|
|
|
fire();
|
|
|
|
// The `fireSingleton` call ia dispatched asynchronously, so it won't
|
|
// have fired by this point. The `fail` listener that we remove now
|
|
// should not be called, even though the event has already been
|
|
// enqueued.
|
|
manager.removeListener(fail);
|
|
|
|
// Wait for the remaining listener to be called, which should always
|
|
// happen after the `fail` listener would normally be called.
|
|
await promise;
|
|
|
|
// Check that the event listener isn't called after the context has
|
|
// unloaded.
|
|
manager.addListener(fail);
|
|
|
|
// The `fire` callback always dispatches events
|
|
// asynchronously, so we need to test that any pending event callbacks
|
|
// aren't fired after the context unloads. We also need to test that
|
|
// any `fire` calls that happen *after* the context is unloaded also
|
|
// do not trigger callbacks.
|
|
fire();
|
|
Promise.resolve().then(fire);
|
|
|
|
context.unload();
|
|
|
|
// The `setTimeout` ensures that we return to the event loop after
|
|
// promise resolution, which means we're guaranteed to return after
|
|
// any micro-tasks that get enqueued by the resolution handlers above.
|
|
await new Promise(resolve => setTimeout(resolve, 0));
|
|
});
|
|
|
|
class Context extends BaseContext {
|
|
constructor(principal) {
|
|
let fakeExtension = {id: "test@web.extension"};
|
|
super("testEnv", fakeExtension);
|
|
Object.defineProperty(this, "principal", {
|
|
value: principal,
|
|
configurable: true,
|
|
});
|
|
this.sandbox = Cu.Sandbox(principal, {wantXrays: false});
|
|
}
|
|
|
|
get cloneScope() {
|
|
return this.sandbox;
|
|
}
|
|
}
|
|
|
|
let ssm = Services.scriptSecurityManager;
|
|
const PRINCIPAL1 = ssm.createCodebasePrincipalFromOrigin("http://www.example.org");
|
|
const PRINCIPAL2 = ssm.createCodebasePrincipalFromOrigin("http://www.somethingelse.org");
|
|
|
|
// Test that toJSON() works in the json sandbox
|
|
add_task(async function test_stringify_toJSON() {
|
|
let context = new Context(PRINCIPAL1);
|
|
let obj = Cu.evalInSandbox("({hidden: true, toJSON() { return {visible: true}; } })", context.sandbox);
|
|
|
|
let stringified = context.jsonStringify(obj);
|
|
let expected = JSON.stringify({visible: true});
|
|
equal(stringified, expected, "Stringified object with toJSON() method is as expected");
|
|
});
|
|
|
|
// Test that stringifying in inaccessible property throws
|
|
add_task(async function test_stringify_inaccessible() {
|
|
let context = new Context(PRINCIPAL1);
|
|
let sandbox = context.sandbox;
|
|
let sandbox2 = Cu.Sandbox(PRINCIPAL2);
|
|
|
|
Cu.waiveXrays(sandbox).subobj = Cu.evalInSandbox("({ subobject: true })", sandbox2);
|
|
let obj = Cu.evalInSandbox("({ local: true, nested: subobj })", sandbox);
|
|
Assert.throws(() => {
|
|
context.jsonStringify(obj);
|
|
}, /Permission denied to access property "toJSON"/);
|
|
});
|
|
|
|
add_task(async function test_stringify_accessible() {
|
|
// Test that an accessible property from another global is included
|
|
let principal = Cu.getObjectPrincipal(Cu.Sandbox([PRINCIPAL1, PRINCIPAL2]));
|
|
let context = new Context(principal);
|
|
let sandbox = context.sandbox;
|
|
let sandbox2 = Cu.Sandbox(PRINCIPAL2);
|
|
|
|
Cu.waiveXrays(sandbox).subobj = Cu.evalInSandbox("({ subobject: true })", sandbox2);
|
|
let obj = Cu.evalInSandbox("({ local: true, nested: subobj })", sandbox);
|
|
let stringified = context.jsonStringify(obj);
|
|
|
|
let expected = JSON.stringify({local: true, nested: {subobject: true}});
|
|
equal(stringified, expected, "Stringified object with accessible property is as expected");
|
|
});
|