669 lines
18 KiB
JavaScript
669 lines
18 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
* http://creativecommons.org/publicdomain/zero/1.0/
|
|
*/
|
|
"use strict";
|
|
|
|
const { ExtensionUtils } = ChromeUtils.importESModule(
|
|
"resource://gre/modules/ExtensionUtils.sys.mjs"
|
|
);
|
|
|
|
ChromeUtils.defineLazyGetter(this, "resourceProtocol", () =>
|
|
Services.io
|
|
.getProtocolHandler("resource")
|
|
.QueryInterface(Ci.nsIResProtocolHandler)
|
|
);
|
|
|
|
Services.prefs.setBoolPref(PREF_EM_CHECK_UPDATE_SECURITY, false);
|
|
|
|
const ID = "langpack-und@test.mozilla.org";
|
|
|
|
const profileDir = gProfD.clone();
|
|
profileDir.append("extensions");
|
|
|
|
// Langpacks versions follow the following convention:
|
|
// <firefox major>.<firefox minor>.YYYYMMDD.HHmmss
|
|
// with no leading zeros allowed (as enforced per version format, see MDN doc page at https://mzl.la/3M6L15y).
|
|
//
|
|
// See https://searchfox.org/mozilla-central/rev/26790fe/python/mozbuild/mozbuild/action/langpack_manifest.py#388-398
|
|
var server = AddonTestUtils.createHttpServer({ hosts: ["example.com"] });
|
|
AddonTestUtils.registerJSON(server, "/test_update_langpack.json", {
|
|
addons: {
|
|
"langpack-und@test.mozilla.org": {
|
|
updates: [
|
|
{
|
|
version: "58.0.20230105.121014",
|
|
applications: {
|
|
gecko: {
|
|
strict_min_version: "58.0",
|
|
strict_max_version: "58.*",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
version: "60.0.20230207.112555",
|
|
update_link:
|
|
"http://example.com/addons/langpack-und@test.mozilla.org.xpi",
|
|
applications: {
|
|
gecko: {
|
|
strict_min_version: "60.0",
|
|
strict_max_version: "60.*",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
version: "60.1.20230309.91233",
|
|
update_link:
|
|
"http://example.com/addons/dotrelease/langpack-und@test.mozilla.org.xpi",
|
|
applications: {
|
|
gecko: {
|
|
strict_min_version: "60.0",
|
|
strict_max_version: "60.*",
|
|
},
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
});
|
|
|
|
// A second update url, which is included in the last of the langpack
|
|
// version from the previous one (and used to cover the staging of a
|
|
// langpack from one dotrelease to another).
|
|
AddonTestUtils.registerJSON(server, "/test_update_langpack2.json", {
|
|
addons: {
|
|
"langpack-und@test.mozilla.org": {
|
|
updates: [
|
|
{
|
|
version: "60.2.20230319.94511",
|
|
update_link:
|
|
"http://example.com/addons/dotrelease2/langpack-und@test.mozilla.org.xpi",
|
|
applications: {
|
|
gecko: {
|
|
strict_min_version: "60.0",
|
|
strict_max_version: "60.*",
|
|
},
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
});
|
|
|
|
function promisePostponeInstall(install) {
|
|
return new Promise((resolve, reject) => {
|
|
let listener = {
|
|
onDownloadEnded: () => {
|
|
install.postpone();
|
|
},
|
|
onInstallFailed: () => {
|
|
install.removeListener(listener);
|
|
reject(new Error("extension installation should not have failed"));
|
|
},
|
|
onInstallEnded: () => {
|
|
install.removeListener(listener);
|
|
reject(
|
|
new Error(
|
|
`extension installation should not have ended for ${install.addon.id}`
|
|
)
|
|
);
|
|
},
|
|
onInstallPostponed: () => {
|
|
install.removeListener(listener);
|
|
resolve();
|
|
},
|
|
};
|
|
|
|
install.addListener(listener);
|
|
});
|
|
}
|
|
|
|
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "58");
|
|
|
|
const ADDONS = {
|
|
langpack_1: {
|
|
"browser/localization/und/browser.ftl":
|
|
"message-browser = Value from Browser\n",
|
|
"localization/und/toolkit_test.ftl": "message-id1 = Value 1\n",
|
|
"chrome/und/locale/und/global/test.properties":
|
|
"message = Value from .properties\n",
|
|
"manifest.json": {
|
|
name: "und Language Pack",
|
|
version: "58.0.20230105.121014",
|
|
manifest_version: 2,
|
|
browser_specific_settings: {
|
|
gecko: {
|
|
id: ID,
|
|
strict_min_version: "58.0",
|
|
strict_max_version: "58.*",
|
|
update_url: "http://example.com/test_update_langpack.json",
|
|
},
|
|
},
|
|
sources: {
|
|
browser: {
|
|
base_path: "browser/",
|
|
},
|
|
},
|
|
langpack_id: "und",
|
|
languages: {
|
|
und: {
|
|
chrome_resources: {
|
|
global: "chrome/und/locale/und/global/",
|
|
},
|
|
version: "20171001190118",
|
|
},
|
|
},
|
|
author: "Mozilla Localization Task Force",
|
|
description: "Language pack for Testy for und",
|
|
},
|
|
},
|
|
};
|
|
|
|
// clone the extension so we can create an update.
|
|
const langpack_update = JSON.parse(JSON.stringify(ADDONS.langpack_1));
|
|
langpack_update["manifest.json"].version = "60.0.20230207.112555";
|
|
langpack_update["manifest.json"].browser_specific_settings.gecko = {
|
|
id: ID,
|
|
strict_min_version: "60.0",
|
|
strict_max_version: "60.*",
|
|
update_url: "http://example.com/test_update_langpack.json",
|
|
};
|
|
|
|
const langpack_update_dotrelease = JSON.parse(
|
|
JSON.stringify(ADDONS.langpack_1)
|
|
);
|
|
langpack_update_dotrelease["manifest.json"].version = "60.1.20230309.91233";
|
|
langpack_update_dotrelease["manifest.json"].browser_specific_settings.gecko = {
|
|
id: ID,
|
|
strict_min_version: "60.0",
|
|
strict_max_version: "60.*",
|
|
update_url: "http://example.com/test_update_langpack2.json",
|
|
};
|
|
|
|
// Another langpack for another dot release part of the same major version as the previous one.
|
|
const langpack_update_dotrelease2 = JSON.parse(
|
|
JSON.stringify(ADDONS.langpack_1)
|
|
);
|
|
langpack_update_dotrelease2["manifest.json"].version = "60.2.20230319.94511";
|
|
langpack_update_dotrelease2["manifest.json"].browser_specific_settings.gecko = {
|
|
id: ID,
|
|
strict_min_version: "60.0",
|
|
strict_max_version: "60.*",
|
|
update_url: "http://example.com/test_update_langpack2.json",
|
|
};
|
|
|
|
let xpi = AddonTestUtils.createTempXPIFile(langpack_update);
|
|
server.registerFile(`/addons/${ID}.xpi`, xpi);
|
|
|
|
let xpiDotRelease = AddonTestUtils.createTempXPIFile(
|
|
langpack_update_dotrelease
|
|
);
|
|
server.registerFile(`/addons/dotrelease/${ID}.xpi`, xpiDotRelease);
|
|
|
|
let xpiDotRelease2 = AddonTestUtils.createTempXPIFile(
|
|
langpack_update_dotrelease2
|
|
);
|
|
server.registerFile(`/addons/dotrelease2/${ID}.xpi`, xpiDotRelease2);
|
|
|
|
function promiseLangpackStartup() {
|
|
return new Promise(resolve => {
|
|
const EVENT = "webextension-langpack-startup";
|
|
Services.obs.addObserver(function observer() {
|
|
Services.obs.removeObserver(observer, EVENT);
|
|
resolve();
|
|
}, EVENT);
|
|
});
|
|
}
|
|
|
|
add_task(async function setup() {
|
|
Services.prefs.clearUserPref("extensions.startupScanScopes");
|
|
});
|
|
|
|
/**
|
|
* This is a basic life-cycle test which verifies that
|
|
* the language pack registers and unregisters correct
|
|
* languages at various stages.
|
|
*/
|
|
add_task(async function test_basic_lifecycle() {
|
|
await promiseStartupManager();
|
|
|
|
// Make sure that `und` locale is not installed.
|
|
equal(
|
|
L10nRegistry.getInstance().getAvailableLocales().includes("und"),
|
|
false,
|
|
"und not installed"
|
|
);
|
|
equal(
|
|
Services.locale.availableLocales.includes("und"),
|
|
false,
|
|
"und not available"
|
|
);
|
|
|
|
let [, { addon }] = await Promise.all([
|
|
promiseLangpackStartup(),
|
|
AddonTestUtils.promiseInstallXPI(ADDONS.langpack_1),
|
|
]);
|
|
|
|
// Now make sure that `und` locale is available.
|
|
equal(
|
|
L10nRegistry.getInstance().getAvailableLocales().includes("und"),
|
|
true,
|
|
"und is installed"
|
|
);
|
|
equal(
|
|
Services.locale.availableLocales.includes("und"),
|
|
true,
|
|
"und is available"
|
|
);
|
|
|
|
await addon.disable();
|
|
|
|
// It is not available after the langpack has been disabled.
|
|
equal(
|
|
L10nRegistry.getInstance().getAvailableLocales().includes("und"),
|
|
false,
|
|
"und not installed"
|
|
);
|
|
equal(
|
|
Services.locale.availableLocales.includes("und"),
|
|
false,
|
|
"und not available"
|
|
);
|
|
|
|
// This quirky code here allows us to handle a scenario where enabling the
|
|
// addon is synchronous or asynchronous.
|
|
await Promise.all([promiseLangpackStartup(), addon.enable()]);
|
|
|
|
// After re-enabling it, the `und` locale is available again.
|
|
equal(
|
|
L10nRegistry.getInstance().getAvailableLocales().includes("und"),
|
|
true,
|
|
"und is installed"
|
|
);
|
|
equal(
|
|
Services.locale.availableLocales.includes("und"),
|
|
true,
|
|
"und is available"
|
|
);
|
|
|
|
await addon.uninstall();
|
|
|
|
// After the langpack has been uninstalled, no more `und` in locales.
|
|
equal(
|
|
L10nRegistry.getInstance().getAvailableLocales().includes("und"),
|
|
false,
|
|
"und not installed"
|
|
);
|
|
equal(
|
|
Services.locale.availableLocales.includes("und"),
|
|
false,
|
|
"und not available"
|
|
);
|
|
});
|
|
|
|
/**
|
|
* This test verifies that registries are able to load and synchronously return
|
|
* correct strings available in the language pack.
|
|
*/
|
|
add_task(async function test_locale_registries() {
|
|
let [, { addon }] = await Promise.all([
|
|
promiseLangpackStartup(),
|
|
AddonTestUtils.promiseInstallXPI(ADDONS.langpack_1),
|
|
]);
|
|
|
|
{
|
|
// Toolkit string
|
|
let bundles = L10nRegistry.getInstance().generateBundlesSync(
|
|
["und"],
|
|
["toolkit_test.ftl"]
|
|
);
|
|
let bundle0 = bundles.next().value;
|
|
ok(bundle0);
|
|
equal(bundle0.hasMessage("message-id1"), true);
|
|
}
|
|
|
|
{
|
|
// Browser string
|
|
let bundles = L10nRegistry.getInstance().generateBundlesSync(
|
|
["und"],
|
|
["browser.ftl"]
|
|
);
|
|
let bundle0 = bundles.next().value;
|
|
ok(bundle0);
|
|
equal(bundle0.hasMessage("message-browser"), true);
|
|
}
|
|
|
|
{
|
|
// Test chrome package
|
|
let reqLocs = Services.locale.requestedLocales;
|
|
Services.locale.requestedLocales = ["und"];
|
|
|
|
let bundle = Services.strings.createBundle(
|
|
"chrome://global/locale/test.properties"
|
|
);
|
|
let entry = bundle.GetStringFromName("message");
|
|
equal(entry, "Value from .properties");
|
|
|
|
Services.locale.requestedLocales = reqLocs;
|
|
}
|
|
|
|
await addon.uninstall();
|
|
});
|
|
|
|
/**
|
|
* This test verifies that registries are able to load and asynchronously return
|
|
* correct strings available in the language pack.
|
|
*/
|
|
add_task(async function test_locale_registries_async() {
|
|
let [, { addon }] = await Promise.all([
|
|
promiseLangpackStartup(),
|
|
AddonTestUtils.promiseInstallXPI(ADDONS.langpack_1),
|
|
]);
|
|
|
|
{
|
|
// Toolkit string
|
|
let bundles = L10nRegistry.getInstance().generateBundles(
|
|
["und"],
|
|
["toolkit_test.ftl"]
|
|
);
|
|
let bundle0 = (await bundles.next()).value;
|
|
equal(bundle0.hasMessage("message-id1"), true);
|
|
}
|
|
|
|
{
|
|
// Browser string
|
|
let bundles = L10nRegistry.getInstance().generateBundles(
|
|
["und"],
|
|
["browser.ftl"]
|
|
);
|
|
let bundle0 = (await bundles.next()).value;
|
|
equal(bundle0.hasMessage("message-browser"), true);
|
|
}
|
|
|
|
await addon.uninstall();
|
|
await promiseShutdownManager();
|
|
});
|
|
|
|
add_task(async function test_langpack_app_shutdown() {
|
|
let langpackId = `langpack-und-${AppConstants.MOZ_BUILD_APP.replace(
|
|
"/",
|
|
"-"
|
|
)}`;
|
|
let check = (yes, msg) => {
|
|
equal(resourceProtocol.hasSubstitution(langpackId), yes, msg);
|
|
};
|
|
|
|
await promiseStartupManager();
|
|
|
|
check(false, "no initial resource substitution");
|
|
|
|
await Promise.all([
|
|
promiseLangpackStartup(),
|
|
AddonTestUtils.promiseInstallXPI(ADDONS.langpack_1),
|
|
]);
|
|
|
|
check(true, "langpack resource available after startup");
|
|
|
|
await promiseShutdownManager();
|
|
|
|
check(true, "langpack resource available after app shutdown");
|
|
|
|
await promiseStartupManager();
|
|
|
|
let addon = await AddonManager.getAddonByID(ID);
|
|
await addon.uninstall();
|
|
|
|
check(false, "langpack resource removed during shutdown for uninstall");
|
|
|
|
await promiseShutdownManager();
|
|
});
|
|
|
|
add_task(async function test_amazing_disappearing_langpacks() {
|
|
let check = yes => {
|
|
equal(
|
|
L10nRegistry.getInstance().getAvailableLocales().includes("und"),
|
|
yes,
|
|
"check L10nRegistry"
|
|
);
|
|
equal(
|
|
Services.locale.availableLocales.includes("und"),
|
|
yes,
|
|
"check availableLocales"
|
|
);
|
|
};
|
|
|
|
await promiseStartupManager();
|
|
|
|
check(false);
|
|
|
|
await Promise.all([
|
|
promiseLangpackStartup(),
|
|
AddonTestUtils.promiseInstallXPI(ADDONS.langpack_1),
|
|
]);
|
|
|
|
check(true);
|
|
|
|
await promiseShutdownManager();
|
|
|
|
check(false);
|
|
|
|
await AddonTestUtils.manuallyUninstall(AddonTestUtils.profileExtensions, ID);
|
|
|
|
await promiseStartupManager();
|
|
|
|
check(false);
|
|
});
|
|
|
|
/**
|
|
* This test verifies that language pack will get disabled after app
|
|
* gets upgraded.
|
|
*/
|
|
add_task(async function test_disable_after_app_update() {
|
|
let [, { addon }] = await Promise.all([
|
|
promiseLangpackStartup(),
|
|
AddonTestUtils.promiseInstallXPI(ADDONS.langpack_1),
|
|
]);
|
|
Assert.ok(addon.isActive);
|
|
|
|
await promiseRestartManager("59");
|
|
|
|
addon = await promiseAddonByID(ID);
|
|
Assert.ok(!addon.isActive);
|
|
Assert.ok(addon.appDisabled);
|
|
|
|
await addon.uninstall();
|
|
await promiseShutdownManager();
|
|
});
|
|
|
|
/**
|
|
* This test verifies that a postponed language pack update will be
|
|
* applied after a restart.
|
|
*/
|
|
add_task(async function test_after_app_update() {
|
|
await promiseStartupManager("58");
|
|
let [, { addon }] = await Promise.all([
|
|
promiseLangpackStartup(),
|
|
AddonTestUtils.promiseInstallXPI(ADDONS.langpack_1),
|
|
]);
|
|
Assert.ok(addon.isActive);
|
|
|
|
await promiseRestartManager("60");
|
|
|
|
addon = await promiseAddonByID(ID);
|
|
Assert.ok(!addon.isActive);
|
|
Assert.ok(addon.appDisabled);
|
|
Assert.equal(addon.version, "58.0.20230105.121014");
|
|
|
|
let update = await promiseFindAddonUpdates(addon);
|
|
Assert.ok(update.updateAvailable, "update is available");
|
|
let install = update.updateAvailable;
|
|
let postponed = promisePostponeInstall(install);
|
|
install.install();
|
|
await postponed;
|
|
Assert.equal(
|
|
install.state,
|
|
AddonManager.STATE_POSTPONED,
|
|
"install postponed"
|
|
);
|
|
|
|
await promiseRestartManager();
|
|
|
|
addon = await promiseAddonByID(ID);
|
|
Assert.ok(addon.isActive);
|
|
Assert.equal(addon.version, "60.1.20230309.91233");
|
|
|
|
await addon.uninstall();
|
|
await promiseShutdownManager();
|
|
});
|
|
|
|
// Support setting the request locale.
|
|
function promiseLocaleChanged(requestedLocales) {
|
|
let changed = ExtensionUtils.promiseObserved(
|
|
"intl:requested-locales-changed"
|
|
);
|
|
Services.locale.requestedLocales = requestedLocales;
|
|
return changed;
|
|
}
|
|
|
|
/**
|
|
* This test verifies that an addon update for the next version can be
|
|
* retrieved and staged for restart.
|
|
*/
|
|
add_task(async function test_staged_langpack_for_app_update() {
|
|
let originalLocales = Services.locale.requestedLocales;
|
|
|
|
await promiseStartupManager("58");
|
|
let [, { addon }] = await Promise.all([
|
|
promiseLangpackStartup(),
|
|
AddonTestUtils.promiseInstallXPI(ADDONS.langpack_1),
|
|
]);
|
|
Assert.ok(addon.isActive);
|
|
await promiseLocaleChanged(["und"]);
|
|
|
|
// Mimick a major release update happening while a
|
|
// langpack from a dotrelease what already available
|
|
// (and then assert that the dotrelease langpack
|
|
// is the one staged and then installed on browser
|
|
// restart)
|
|
await AddonManager.stageLangpacksForAppUpdate("60");
|
|
await promiseRestartManager("60");
|
|
|
|
addon = await promiseAddonByID(ID);
|
|
Assert.ok(addon.isActive);
|
|
Assert.equal(addon.version, "60.1.20230309.91233");
|
|
|
|
// Mimick a second dotrelease update, along with
|
|
// staging of the langpacks released along with it
|
|
// (then assert that the langpack for the second
|
|
// dotrelease is staged and then installed on
|
|
// browser restart).
|
|
await promiseRestartManager("60.1");
|
|
await AddonManager.stageLangpacksForAppUpdate("60.2");
|
|
await promiseRestartManager("60.2");
|
|
|
|
addon = await promiseAddonByID(ID);
|
|
Assert.ok(addon.isActive);
|
|
Assert.equal(addon.version, "60.2.20230319.94511");
|
|
|
|
await addon.uninstall();
|
|
await promiseShutdownManager();
|
|
|
|
Services.locale.requestedLocales = originalLocales;
|
|
});
|
|
|
|
/**
|
|
* This test verifies that an addon update for the next version can be
|
|
* retrieved and staged for restart, but a restart failure falls back.
|
|
*/
|
|
add_task(async function test_staged_langpack_for_app_update_fail() {
|
|
let originalLocales = Services.locale.requestedLocales;
|
|
|
|
await promiseStartupManager("58");
|
|
let [, { addon }] = await Promise.all([
|
|
promiseLangpackStartup(),
|
|
AddonTestUtils.promiseInstallXPI(ADDONS.langpack_1),
|
|
]);
|
|
Assert.ok(addon.isActive);
|
|
await promiseLocaleChanged(["und"]);
|
|
|
|
await AddonManager.stageLangpacksForAppUpdate("60");
|
|
await promiseRestartManager();
|
|
|
|
addon = await promiseAddonByID(ID);
|
|
Assert.ok(addon.isActive);
|
|
Assert.equal(addon.version, "58.0.20230105.121014");
|
|
|
|
await addon.uninstall();
|
|
await promiseShutdownManager();
|
|
Services.locale.requestedLocales = originalLocales;
|
|
});
|
|
|
|
/**
|
|
* This test verifies that an update restart works when the langpack
|
|
* cannot be updated.
|
|
*/
|
|
add_task(async function test_staged_langpack_for_app_update_not_found() {
|
|
let originalLocales = Services.locale.requestedLocales;
|
|
|
|
await promiseStartupManager("58");
|
|
let [, { addon }] = await Promise.all([
|
|
promiseLangpackStartup(),
|
|
AddonTestUtils.promiseInstallXPI(ADDONS.langpack_1),
|
|
]);
|
|
Assert.ok(addon.isActive);
|
|
await promiseLocaleChanged(["und"]);
|
|
|
|
await AddonManager.stageLangpacksForAppUpdate("59");
|
|
await promiseRestartManager("59");
|
|
|
|
addon = await promiseAddonByID(ID);
|
|
Assert.ok(!addon.isActive);
|
|
Assert.equal(addon.version, "58.0.20230105.121014");
|
|
|
|
await addon.uninstall();
|
|
await promiseShutdownManager();
|
|
Services.locale.requestedLocales = originalLocales;
|
|
});
|
|
|
|
/**
|
|
* This test verifies that a compat update with an invalid max_version
|
|
* will be disabled, at least allowing Firefox to startup without failures.
|
|
*/
|
|
add_task(async function test_staged_langpack_compat_startup() {
|
|
let originalLocales = Services.locale.requestedLocales;
|
|
|
|
await promiseStartupManager("58");
|
|
let [, { addon }] = await Promise.all([
|
|
promiseLangpackStartup(),
|
|
AddonTestUtils.promiseInstallXPI(ADDONS.langpack_1),
|
|
]);
|
|
Assert.ok(addon.isActive);
|
|
await promiseLocaleChanged(["und"]);
|
|
|
|
// Mimick a compatibility update
|
|
let compatUpdate = {
|
|
targetApplications: [
|
|
{
|
|
id: "toolkit@mozilla.org",
|
|
minVersion: "58",
|
|
maxVersion: "*",
|
|
},
|
|
],
|
|
};
|
|
addon.__AddonInternal__.applyCompatibilityUpdate(compatUpdate);
|
|
|
|
await promiseRestartManager("59");
|
|
|
|
addon = await promiseAddonByID(ID);
|
|
Assert.ok(!addon.isActive, "addon is not active after upgrade");
|
|
ok(!addon.isCompatible, "compatibility update fixed");
|
|
|
|
await promiseRestartManager("58");
|
|
|
|
addon = await promiseAddonByID(ID);
|
|
Assert.ok(addon.isActive, "addon is active after downgrade");
|
|
ok(addon.isCompatible, "compatibility update fixed");
|
|
|
|
await addon.uninstall();
|
|
await promiseShutdownManager();
|
|
Services.locale.requestedLocales = originalLocales;
|
|
});
|