Move the per-window idle inits in gBrowserInit._schedulePerWindowIdleTasks to a new browser-window-idle-tasks category, continuing Bug 2036714, Bug 2039199 and Bug 2045665. The category is invoked with idleDispatch: true. callModulesFromCategory now drops a consumer when its jsGlobal is a window that has closed by the time the idle task runs. Without this, a window that closes before its idle inits run gets re-initialized after its unload teardown and leaks (browser_windowclose.js / browser_tabdetach.js). Teardown moves to the browser-window-unload categories, and the download init moves into DownloadsCommon.initializeForWindow. Differential Revision: https://phabricator.services.mozilla.com/D310625
747 lines
23 KiB
JavaScript
747 lines
23 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
const { BrowserUtils } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/BrowserUtils.sys.mjs"
|
|
);
|
|
|
|
const { sinon } = ChromeUtils.importESModule(
|
|
"resource://testing-common/Sinon.sys.mjs"
|
|
);
|
|
|
|
const { EnterprisePolicyTesting } = ChromeUtils.importESModule(
|
|
"resource://testing-common/EnterprisePolicyTesting.sys.mjs"
|
|
);
|
|
|
|
const { Region } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/Region.sys.mjs"
|
|
);
|
|
|
|
const { updateAppInfo } = ChromeUtils.importESModule(
|
|
"resource://testing-common/AppInfo.sys.mjs"
|
|
);
|
|
|
|
// Helper to run tests for specific regions
|
|
function setupRegions(home, current) {
|
|
Region._setHomeRegion(home || "");
|
|
Region._setCurrentRegion(current || "");
|
|
}
|
|
|
|
function setLanguage(language) {
|
|
Services.locale.availableLocales = [language];
|
|
Services.locale.requestedLocales = [language];
|
|
}
|
|
|
|
/**
|
|
* Calls to this need to revert these changes by undoing them at the end of the test,
|
|
* using:
|
|
*
|
|
* await EnterprisePolicyTesting.setupPolicyEngineWithJson("");
|
|
*/
|
|
async function setupEnterprisePolicy() {
|
|
// set app info name as it's needed to set a policy and is not defined by default
|
|
// in xpcshell tests
|
|
updateAppInfo({
|
|
name: "XPCShell",
|
|
});
|
|
|
|
// set up an arbitrary enterprise policy
|
|
await EnterprisePolicyTesting.setupPolicyEngineWithJson({
|
|
policies: {
|
|
EnableTrackingProtection: {
|
|
Value: true,
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
add_task(async function test_shouldShowVPNPromo() {
|
|
function setPromoEnabled(enabled) {
|
|
Services.prefs.setBoolPref("browser.vpn_promo.enabled", enabled);
|
|
}
|
|
|
|
const allowedRegion = "US";
|
|
const disallowedRegion = "SY";
|
|
const illegalRegion = "CN";
|
|
const unsupportedRegion = "LY";
|
|
const regionNotInDefaultPref = "QQ";
|
|
|
|
// Show promo when enabled in allowed regions
|
|
setupRegions(allowedRegion, allowedRegion);
|
|
Assert.ok(BrowserUtils.shouldShowVPNPromo());
|
|
|
|
// Don't show when not enabled
|
|
setPromoEnabled(false);
|
|
Assert.ok(!BrowserUtils.shouldShowVPNPromo());
|
|
|
|
// Don't show in disallowed home regions, even when enabled
|
|
setPromoEnabled(true);
|
|
setupRegions(disallowedRegion);
|
|
Assert.ok(!BrowserUtils.shouldShowVPNPromo());
|
|
|
|
// Don't show in illegal home regions, even when enabled
|
|
setupRegions(illegalRegion);
|
|
Assert.ok(!BrowserUtils.shouldShowVPNPromo());
|
|
|
|
// Don't show in disallowed current regions, even when enabled
|
|
setupRegions(allowedRegion, disallowedRegion);
|
|
Assert.ok(!BrowserUtils.shouldShowVPNPromo());
|
|
|
|
// Don't show in illegal current regions, even when enabled
|
|
setupRegions(allowedRegion, illegalRegion);
|
|
Assert.ok(!BrowserUtils.shouldShowVPNPromo());
|
|
|
|
// Show if home region is supported, even if current region is not supported (but isn't disallowed or illegal)
|
|
setupRegions(allowedRegion, unsupportedRegion);
|
|
Assert.ok(BrowserUtils.shouldShowVPNPromo());
|
|
|
|
// Show VPN if current region is supported, even if home region is unsupported (but isn't disallowed or illegal)
|
|
setupRegions(unsupportedRegion, allowedRegion); // revert changes to regions
|
|
Assert.ok(BrowserUtils.shouldShowVPNPromo());
|
|
|
|
// Make sure we are getting the list of allowed regions from the right
|
|
// place.
|
|
setupRegions(regionNotInDefaultPref);
|
|
Services.prefs.setStringPref(
|
|
"browser.contentblocking.report.vpn_regions",
|
|
"qq"
|
|
);
|
|
Assert.ok(BrowserUtils.shouldShowVPNPromo());
|
|
Services.prefs.clearUserPref("browser.contentblocking.report.vpn_regions");
|
|
|
|
if (AppConstants.platform !== "android") {
|
|
// Services.policies isn't shipped on Android
|
|
// Don't show VPN if there's an active enterprise policy
|
|
setupRegions(allowedRegion, allowedRegion);
|
|
await setupEnterprisePolicy();
|
|
|
|
Assert.ok(!BrowserUtils.shouldShowVPNPromo());
|
|
|
|
// revert policy changes made earlier
|
|
await EnterprisePolicyTesting.setupPolicyEngineWithJson("");
|
|
}
|
|
});
|
|
|
|
add_task(async function test_sendToDeviceEmailsSupported() {
|
|
const allowedLanguage = "en-US";
|
|
const disallowedLanguage = "ar";
|
|
|
|
// Return true if language is en-US
|
|
setLanguage(allowedLanguage);
|
|
Assert.ok(BrowserUtils.sendToDeviceEmailsSupported());
|
|
|
|
// Return false if language is ar
|
|
setLanguage(disallowedLanguage);
|
|
Assert.ok(!BrowserUtils.sendToDeviceEmailsSupported());
|
|
});
|
|
|
|
add_task(async function test_shouldShowPinPromo() {
|
|
Preferences.set("browser.promo.pin.enabled", true);
|
|
// Show pin promo type by default when promo is enabled
|
|
Assert.ok(BrowserUtils.shouldShowPromo(BrowserUtils.PromoType.PIN));
|
|
|
|
// Don't show when there is an enterprise policy active
|
|
if (AppConstants.platform !== "android") {
|
|
// Services.policies isn't shipped on Android
|
|
await setupEnterprisePolicy();
|
|
|
|
Assert.ok(!BrowserUtils.shouldShowPromo(BrowserUtils.PromoType.PIN));
|
|
|
|
// revert policy changes made earlier
|
|
await EnterprisePolicyTesting.setupPolicyEngineWithJson("");
|
|
}
|
|
|
|
// Don't show when promo disabled by pref
|
|
Preferences.set("browser.promo.pin.enabled", false);
|
|
Assert.ok(!BrowserUtils.shouldShowPromo(BrowserUtils.PromoType.PIN));
|
|
|
|
Services.prefs.clearUserBranch("browser.promo.pin");
|
|
});
|
|
|
|
add_task(async function test_shouldShowRelayPromo() {
|
|
// This test assumes by default no uri is configured.
|
|
Preferences.set("identity.fxaccounts.autoconfig.uri", "");
|
|
Assert.ok(BrowserUtils.shouldShowPromo(BrowserUtils.PromoType.RELAY));
|
|
|
|
// Don't show when there is an enterprise policy active
|
|
if (AppConstants.platform !== "android") {
|
|
// Services.policies isn't shipped on Android
|
|
await setupEnterprisePolicy();
|
|
|
|
Assert.ok(!BrowserUtils.shouldShowPromo(BrowserUtils.PromoType.RELAY));
|
|
|
|
// revert policy changes made earlier
|
|
await EnterprisePolicyTesting.setupPolicyEngineWithJson("");
|
|
}
|
|
|
|
// Don't show if a custom FxA instance is configured
|
|
Preferences.set("identity.fxaccounts.autoconfig.uri", "https://x");
|
|
Assert.ok(!BrowserUtils.shouldShowPromo(BrowserUtils.PromoType.RELAY));
|
|
|
|
Preferences.reset("identity.fxaccounts.autoconfig.uri");
|
|
});
|
|
|
|
add_task(function test_getShareableURL() {
|
|
// Some test suites, specifically android, don't have this setup properly -- so we add it manually
|
|
if (!Preferences.get("services.sync.engine.tabs.filteredSchemes")) {
|
|
Preferences.set(
|
|
"services.sync.engine.tabs.filteredSchemes",
|
|
"about|resource|chrome|file|blob|moz-extension|data"
|
|
);
|
|
}
|
|
// Empty shouldn't be sendable
|
|
Assert.ok(!BrowserUtils.getShareableURL(""));
|
|
// Valid
|
|
let good = Services.io.newURI("https://mozilla.org");
|
|
Assert.ok(BrowserUtils.getShareableURL(good).equals(good));
|
|
// Invalid
|
|
Assert.ok(
|
|
!BrowserUtils.getShareableURL(Services.io.newURI("file://path/to/pdf.pdf"))
|
|
);
|
|
|
|
// Invalid
|
|
Assert.ok(
|
|
!BrowserUtils.getShareableURL(
|
|
Services.io.newURI(
|
|
"data:application/json;base64,ewogICJ0eXBlIjogIm1haW4i=="
|
|
)
|
|
)
|
|
);
|
|
|
|
// Reader mode:
|
|
if (AppConstants.platform !== "android") {
|
|
let readerUrl = Services.io.newURI(
|
|
"about:reader?url=" + encodeURIComponent("http://foo.com/")
|
|
);
|
|
Assert.equal(
|
|
BrowserUtils.getShareableURL(readerUrl).spec,
|
|
"http://foo.com/"
|
|
);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Verify that category manager calling modules are loaded on-demand,
|
|
* and that caching doesn't break adding more modules as category entries
|
|
* at runtime.
|
|
*/
|
|
add_task(async function test_callModulesFromCategory() {
|
|
const MODULE1 = "resource://test/my_catman_1.sys.mjs";
|
|
const MODULE2 = "resource://test/my_catman_2.sys.mjs";
|
|
const CATEGORY = "test-modules-from-catman";
|
|
const OBSTOPIC1 = CATEGORY + "-notification";
|
|
const OBSTOPIC2 = CATEGORY + "-other-notification";
|
|
|
|
// The two modules both fire different observer topics to allow us to ensure
|
|
// they have been called. This helper just makes it easier to get only
|
|
// that return value as a result of a promise, as `topicObserved` also
|
|
// returns the "subject" of the observer notification, which we don't care about.
|
|
let rvFromModule = topic =>
|
|
TestUtils.topicObserved(topic).then(([_subj, data]) => data);
|
|
|
|
// Start off with nothing in a category:
|
|
Assert.equal(
|
|
Cu.isESModuleLoaded(MODULE1),
|
|
false,
|
|
"First module should not be loaded."
|
|
);
|
|
let catEntries = Array.from(Services.catMan.enumerateCategory(CATEGORY));
|
|
Assert.deepEqual(catEntries, [], "Should be no entries for this category.");
|
|
|
|
try {
|
|
// There's nothing in this category right now so this should be a no-op.
|
|
BrowserUtils.callModulesFromCategory({ categoryName: CATEGORY }, "Hello");
|
|
} catch (ex) {
|
|
Assert.ok(false, `Should not have thrown but received an exception ${ex}`);
|
|
}
|
|
|
|
// Now add an item, check that calling it now works.
|
|
//
|
|
// Note that category manager observer notifications are async (they get
|
|
// dispatched as runnables) and so we have to wait for it to make sure that
|
|
// BrowserUtils has had a chance of being told new entries have arrived.
|
|
let catManUpdated = TestUtils.topicObserved("xpcom-category-entry-added");
|
|
|
|
Services.catMan.addCategoryEntry(
|
|
CATEGORY,
|
|
MODULE1,
|
|
`Module1.test`,
|
|
false,
|
|
false
|
|
);
|
|
catEntries = Array.from(Services.catMan.enumerateCategory(CATEGORY));
|
|
Assert.equal(catEntries.length, 1);
|
|
|
|
// See note above.
|
|
await catManUpdated;
|
|
|
|
Assert.equal(
|
|
Cu.isESModuleLoaded(MODULE1),
|
|
false,
|
|
"First module should still not be loaded."
|
|
);
|
|
|
|
// This entry will cause an observer topic to notify, so ensure that happens.
|
|
let moduleResult = rvFromModule(OBSTOPIC1);
|
|
BrowserUtils.callModulesFromCategory({ categoryName: CATEGORY }, "Hello");
|
|
Assert.equal(
|
|
Cu.isESModuleLoaded(MODULE1),
|
|
true,
|
|
"First module should be loaded sync."
|
|
);
|
|
Assert.equal("Hello", await moduleResult, "Should have been called.");
|
|
|
|
// Now add another item, check that both are called.
|
|
catManUpdated = TestUtils.topicObserved("xpcom-category-entry-added");
|
|
Services.catMan.addCategoryEntry(
|
|
CATEGORY,
|
|
MODULE2,
|
|
`Module2.othertest`,
|
|
false,
|
|
false
|
|
);
|
|
await catManUpdated;
|
|
|
|
moduleResult = Promise.all([
|
|
rvFromModule(OBSTOPIC1),
|
|
rvFromModule(OBSTOPIC2),
|
|
]);
|
|
|
|
BrowserUtils.callModulesFromCategory({ categoryName: CATEGORY }, "Hello");
|
|
Assert.deepEqual(
|
|
["Hello", "Hello"],
|
|
await moduleResult,
|
|
"Both modules should have been called."
|
|
);
|
|
|
|
// Now remove the first module again, check that only the second one notifies.
|
|
catManUpdated = TestUtils.topicObserved("xpcom-category-entry-removed");
|
|
Services.catMan.deleteCategoryEntry(CATEGORY, MODULE1, false);
|
|
await catManUpdated;
|
|
let ob = () => Assert.ok(false, "I shouldn't be called.");
|
|
Services.obs.addObserver(ob, OBSTOPIC1);
|
|
|
|
moduleResult = rvFromModule(OBSTOPIC2);
|
|
BrowserUtils.callModulesFromCategory({ categoryName: CATEGORY }, "Hello");
|
|
Assert.equal(
|
|
"Hello",
|
|
await moduleResult,
|
|
"Second module should still be called."
|
|
);
|
|
|
|
let idleResult = null;
|
|
let idlePromise = TestUtils.topicObserved(OBSTOPIC2).then(([_subj, data]) => {
|
|
idleResult = data;
|
|
return data;
|
|
});
|
|
BrowserUtils.callModulesFromCategory(
|
|
{ categoryName: CATEGORY, idleDispatch: true },
|
|
"Hello"
|
|
);
|
|
Assert.equal(idleResult, null, "Idle calls should not happen immediately.");
|
|
Assert.equal("Hello", await idlePromise, "Idle calls should run eventually.");
|
|
|
|
Services.obs.removeObserver(ob, OBSTOPIC1);
|
|
|
|
// Now clean up our category for later tests.
|
|
Services.catMan.deleteCategory(CATEGORY);
|
|
});
|
|
|
|
// With idleDispatch, a consumer whose jsGlobal is a window that has closed by
|
|
// the time the idle task runs should be dropped, so we don't initialize (and
|
|
// leak) a closing window.
|
|
add_task(
|
|
async function test_callModulesFromCategory_idleDispatch_closed_window() {
|
|
const CATEGORY = "test-js-global-catman-closed-window";
|
|
const MODULE = "chrome://browser/content/fake-catman-test.js";
|
|
|
|
let catManUpdated = TestUtils.topicObserved("xpcom-category-entry-added");
|
|
Services.catMan.addCategoryEntry(
|
|
CATEGORY,
|
|
MODULE,
|
|
"FakeObj.doThing",
|
|
false,
|
|
false
|
|
);
|
|
await catManUpdated;
|
|
|
|
let sandbox = sinon.createSandbox();
|
|
|
|
// A window that has closed: its consumer should be dropped.
|
|
let closedWindow = { closed: true, FakeObj: { doThing: sandbox.spy() } };
|
|
await BrowserUtils.callModulesFromCategory(
|
|
{ categoryName: CATEGORY, idleDispatch: true, jsGlobal: closedWindow },
|
|
"win"
|
|
);
|
|
sinon.assert.notCalled(closedWindow.FakeObj.doThing);
|
|
|
|
// A window that is still open: its consumer should run as usual.
|
|
let openWindow = { closed: false, FakeObj: { doThing: sandbox.spy() } };
|
|
await BrowserUtils.callModulesFromCategory(
|
|
{ categoryName: CATEGORY, idleDispatch: true, jsGlobal: openWindow },
|
|
"win"
|
|
);
|
|
sinon.assert.calledOnce(openWindow.FakeObj.doThing);
|
|
sinon.assert.calledWithExactly(openWindow.FakeObj.doThing, "win");
|
|
|
|
sandbox.restore();
|
|
Services.catMan.deleteCategory(CATEGORY);
|
|
}
|
|
);
|
|
|
|
// Test that errors are reported but do not throw at the callsite,
|
|
// and that custom error handlers are invoked.
|
|
add_task(async function test_callModulesFromCategory_errors() {
|
|
const OTHER_CAT = "someothercat";
|
|
const MODULE1 = "resource://test/my_catman_1.sys.mjs";
|
|
|
|
// Add an item that doesn't exist, and check that although we report errors,
|
|
// the callsite doesn't throw.
|
|
let catManUpdated = TestUtils.topicObserved("xpcom-category-entry-added");
|
|
Services.catMan.addCategoryEntry(
|
|
OTHER_CAT,
|
|
MODULE1,
|
|
`Module1.nonExistantFunction`,
|
|
false,
|
|
false
|
|
);
|
|
await catManUpdated;
|
|
let catEntries = Array.from(Services.catMan.enumerateCategory(OTHER_CAT));
|
|
Assert.equal(catEntries.length, 1);
|
|
|
|
let consolePromise = TestUtils.consoleMessageObserved(m => {
|
|
let firstArg = m.wrappedJSObject.arguments?.[0]?.message;
|
|
return typeof firstArg == "string" && firstArg.includes("not a function");
|
|
});
|
|
BrowserUtils.callModulesFromCategory(
|
|
{
|
|
categoryName: OTHER_CAT,
|
|
},
|
|
"Hello"
|
|
);
|
|
let reportedError = await consolePromise;
|
|
let firstArg = reportedError.wrappedJSObject.arguments?.[0]?.message;
|
|
Assert.stringContains(
|
|
firstArg,
|
|
MODULE1,
|
|
"Error message should include module URL."
|
|
);
|
|
Services.catMan.deleteCategoryEntry(OTHER_CAT, MODULE1, false);
|
|
|
|
// Check that custom exception handling from extant methods works:
|
|
catManUpdated = TestUtils.topicObserved("xpcom-category-entry-added");
|
|
Services.catMan.addCategoryEntry(
|
|
OTHER_CAT,
|
|
MODULE1,
|
|
`Module1.throwingFunction`,
|
|
false,
|
|
false
|
|
);
|
|
await catManUpdated;
|
|
Assert.equal(catEntries.length, 1);
|
|
let exHandler = Promise.withResolvers();
|
|
BrowserUtils.callModulesFromCategory({
|
|
categoryName: OTHER_CAT,
|
|
failureHandler: exHandler.resolve,
|
|
});
|
|
let caughtException = await exHandler.promise;
|
|
Assert.stringContains(
|
|
caughtException.message,
|
|
"Uh oh",
|
|
"Exceptions should be handled."
|
|
);
|
|
|
|
// Now clean up our category for later tests.
|
|
Services.catMan.deleteCategory(OTHER_CAT);
|
|
});
|
|
|
|
/**
|
|
* Test that callModulesFromCategory returns a Promise that resolves when all
|
|
* category tasks have settled.
|
|
*/
|
|
add_task(async function test_callModulesFromCategory_returns_promise() {
|
|
const CATEGORY = "test-modules-from-catman";
|
|
const MODULE1 = "resource://test/my_catman_1.sys.mjs";
|
|
const OBSTOPIC1 = CATEGORY + "-notification";
|
|
|
|
let catManUpdated = TestUtils.topicObserved("xpcom-category-entry-added");
|
|
Services.catMan.addCategoryEntry(
|
|
CATEGORY,
|
|
MODULE1,
|
|
`Module1.test`,
|
|
false,
|
|
false
|
|
);
|
|
await catManUpdated;
|
|
|
|
let moduleResult = TestUtils.topicObserved(OBSTOPIC1).then(
|
|
([_subj, data]) => data
|
|
);
|
|
|
|
let result = BrowserUtils.callModulesFromCategory(
|
|
{ categoryName: CATEGORY },
|
|
"Hello"
|
|
);
|
|
|
|
Assert.ok(result.then, "Should return a Promise");
|
|
|
|
let settledResults = await result;
|
|
Assert.ok(Array.isArray(settledResults), "Should return an array of results");
|
|
Assert.equal(settledResults.length, 1, "Should have one result");
|
|
Assert.equal(
|
|
settledResults[0].status,
|
|
"fulfilled",
|
|
"Task should have been fulfilled"
|
|
);
|
|
|
|
Assert.equal(await moduleResult, "Hello", "Module should have been called");
|
|
|
|
// Now clean up our category for later tests.
|
|
Services.catMan.deleteCategory(CATEGORY);
|
|
});
|
|
|
|
// Test a category with both a plain .js entry and an ESM entry, for both
|
|
// single-window and multi-window scenarios.
|
|
add_task(async function test_callModulesFromCategory_multiple_window() {
|
|
const CATEGORY = "test-js-global-catman";
|
|
// A fake .js URL — the file doesn't need to exist since we rely on
|
|
// jsGlobal having the object already (via lazy getter in production).
|
|
const MODULE_JS = "chrome://browser/content/fake-catman-test.js";
|
|
const MODULE_ESM = "resource://test/my_catman_1.sys.mjs";
|
|
const OBSTOPIC = "test-modules-from-catman-notification";
|
|
|
|
let catManUpdated = TestUtils.topicObserved("xpcom-category-entry-added");
|
|
Services.catMan.addCategoryEntry(
|
|
CATEGORY,
|
|
MODULE_JS,
|
|
"FakeObj.doThing",
|
|
false,
|
|
false
|
|
);
|
|
await catManUpdated;
|
|
|
|
catManUpdated = TestUtils.topicObserved("xpcom-category-entry-added");
|
|
Services.catMan.addCategoryEntry(
|
|
CATEGORY,
|
|
MODULE_ESM,
|
|
"Module1.test",
|
|
false,
|
|
false
|
|
);
|
|
await catManUpdated;
|
|
|
|
let sandbox = sinon.createSandbox();
|
|
|
|
// Window 1: both the .js and ESM entries should be called.
|
|
let fakeGlobal1 = { FakeObj: { doThing: sandbox.spy() } };
|
|
|
|
let esmResult = TestUtils.topicObserved(OBSTOPIC).then(([, data]) => data);
|
|
BrowserUtils.callModulesFromCategory(
|
|
{ categoryName: CATEGORY, jsGlobal: fakeGlobal1 },
|
|
"window1"
|
|
);
|
|
sinon.assert.calledOnce(fakeGlobal1.FakeObj.doThing);
|
|
sinon.assert.calledWithExactly(fakeGlobal1.FakeObj.doThing, "window1");
|
|
Assert.equal(await esmResult, "window1", "ESM entry called for window 1.");
|
|
|
|
// Window 2 (multi-window): the .js entry must use the new jsGlobal, while
|
|
// the ESM entry continues to use the same singleton module instance.
|
|
let fakeGlobal2 = { FakeObj: { doThing: sandbox.spy() } };
|
|
|
|
esmResult = TestUtils.topicObserved(OBSTOPIC).then(([, data]) => data);
|
|
BrowserUtils.callModulesFromCategory(
|
|
{ categoryName: CATEGORY, jsGlobal: fakeGlobal2 },
|
|
"window2"
|
|
);
|
|
sinon.assert.calledOnce(fakeGlobal2.FakeObj.doThing);
|
|
sinon.assert.calledWithExactly(fakeGlobal2.FakeObj.doThing, "window2");
|
|
sinon.assert.calledOnce(fakeGlobal1.FakeObj.doThing); // not called again
|
|
Assert.equal(await esmResult, "window2", "ESM entry called for window 2.");
|
|
|
|
sandbox.restore();
|
|
Services.catMan.deleteCategory(CATEGORY);
|
|
});
|
|
|
|
// Test error paths for the jsGlobal / plain-.js-script code path.
|
|
add_task(async function test_callModulesFromCategory_jsGlobal_errors() {
|
|
const CATEGORY = "test-js-global-catman-errors";
|
|
const MODULE = "chrome://browser/content/fake-catman-test.js";
|
|
|
|
let catManUpdated = TestUtils.topicObserved("xpcom-category-entry-added");
|
|
Services.catMan.addCategoryEntry(
|
|
CATEGORY,
|
|
MODULE,
|
|
"FakeObj.doThing",
|
|
false,
|
|
false
|
|
);
|
|
await catManUpdated;
|
|
|
|
// Omitting jsGlobal for a .js entry should log an error.
|
|
let consolePromise = TestUtils.consoleMessageObserved(m => {
|
|
let firstArg = m.wrappedJSObject.arguments?.[0];
|
|
return typeof firstArg == "string" && firstArg.includes(CATEGORY);
|
|
});
|
|
BrowserUtils.callModulesFromCategory({ categoryName: CATEGORY }, "hello");
|
|
await consolePromise;
|
|
|
|
// Providing a jsGlobal that lacks the expected object should log an error.
|
|
consolePromise = TestUtils.consoleMessageObserved(m => {
|
|
let firstArg = m.wrappedJSObject.arguments?.[0];
|
|
return typeof firstArg == "string" && firstArg.includes(CATEGORY);
|
|
});
|
|
BrowserUtils.callModulesFromCategory(
|
|
{ categoryName: CATEGORY, jsGlobal: {} },
|
|
"hello"
|
|
);
|
|
await consolePromise;
|
|
|
|
Services.catMan.deleteCategory(CATEGORY);
|
|
});
|
|
|
|
add_task(async function test_willLoadInBackground() {
|
|
const TEST_DATA = [
|
|
// where: "tab" and loadInBackgroundPref is on.
|
|
{
|
|
loadInBackgroundPref: false,
|
|
where: "tab",
|
|
expected: false,
|
|
},
|
|
{
|
|
loadInBackgroundPref: false,
|
|
where: "tab",
|
|
params: { inBackground: true },
|
|
expected: true,
|
|
},
|
|
{
|
|
loadInBackgroundPref: false,
|
|
where: "tab",
|
|
params: { inBackground: false },
|
|
expected: false,
|
|
},
|
|
{
|
|
loadInBackgroundPref: false,
|
|
where: "tab",
|
|
params: { forceForeground: true },
|
|
expected: false,
|
|
},
|
|
{
|
|
loadInBackgroundPref: false,
|
|
where: "tab",
|
|
params: { forceForeground: false },
|
|
expected: false,
|
|
},
|
|
// where: "tab" and loadInBackgroundPref is off.
|
|
{
|
|
loadInBackgroundPref: true,
|
|
where: "tab",
|
|
expected: true,
|
|
},
|
|
{
|
|
loadInBackgroundPref: true,
|
|
where: "tab",
|
|
params: { inBackground: true },
|
|
expected: true,
|
|
},
|
|
{
|
|
loadInBackgroundPref: true,
|
|
where: "tab",
|
|
params: { inBackground: false },
|
|
expected: false,
|
|
},
|
|
{
|
|
loadInBackgroundPref: true,
|
|
where: "tab",
|
|
params: { forceForeground: true },
|
|
expected: false,
|
|
},
|
|
{
|
|
loadInBackgroundPref: true,
|
|
where: "tab",
|
|
params: { forceForeground: false },
|
|
expected: true,
|
|
},
|
|
// where: "tabshifted" and loadInBackgroundPref is on.
|
|
{
|
|
loadInBackgroundPref: false,
|
|
where: "tabshifted",
|
|
expected: true,
|
|
},
|
|
{
|
|
loadInBackgroundPref: false,
|
|
where: "tabshifted",
|
|
params: { inBackground: true },
|
|
expected: false,
|
|
},
|
|
{
|
|
loadInBackgroundPref: false,
|
|
where: "tabshifted",
|
|
params: { inBackground: false },
|
|
expected: true,
|
|
},
|
|
{
|
|
loadInBackgroundPref: false,
|
|
where: "tabshifted",
|
|
params: { forceForeground: true },
|
|
expected: true,
|
|
},
|
|
{
|
|
loadInBackgroundPref: false,
|
|
where: "tabshifted",
|
|
params: { forceForeground: false },
|
|
expected: true,
|
|
},
|
|
// where: "tabshifted" and loadInBackgroundPref is off.
|
|
{
|
|
loadInBackgroundPref: true,
|
|
where: "tabshifted",
|
|
expected: false,
|
|
},
|
|
{
|
|
loadInBackgroundPref: true,
|
|
where: "tabshifted",
|
|
params: { inBackground: true },
|
|
expected: false,
|
|
},
|
|
{
|
|
loadInBackgroundPref: true,
|
|
where: "tabshifted",
|
|
params: { inBackground: false },
|
|
expected: true,
|
|
},
|
|
{
|
|
loadInBackgroundPref: true,
|
|
where: "tabshifted",
|
|
params: { forceForeground: true },
|
|
expected: true,
|
|
},
|
|
{
|
|
loadInBackgroundPref: true,
|
|
where: "tabshifted",
|
|
params: { forceForeground: false },
|
|
expected: false,
|
|
},
|
|
// where: other,
|
|
{
|
|
loadInBackgroundPref: true,
|
|
where: "current",
|
|
params: { inBackground: true },
|
|
expected: false,
|
|
},
|
|
];
|
|
|
|
for (const { loadInBackgroundPref, where, params, expected } of TEST_DATA) {
|
|
info(
|
|
`Test for ${JSON.stringify({ loadInBackgroundPref, where, params, expected })}`
|
|
);
|
|
Services.prefs.setBoolPref(
|
|
"browser.tabs.loadInBackground",
|
|
loadInBackgroundPref
|
|
);
|
|
|
|
Assert.equal(BrowserUtils.willLoadInBackground(where, params), expected);
|
|
|
|
Services.prefs.clearUserPref("browser.tabs.loadInBackground");
|
|
}
|
|
});
|