Bug 1429610 Add internal api to look up available language packs
MozReview-Commit-ID: ISIu9mDIvbP
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -44,16 +44,17 @@ pref("extensions.webextOptionalPermissio
// Preferences for AMO integration
pref("extensions.getAddons.cache.enabled", true);
pref("extensions.getAddons.get.url", "https://services.addons.mozilla.org/api/v3/addons/search/?guid=%IDS%&lang=%LOCALE%");
pref("extensions.getAddons.compatOverides.url", "https://services.addons.mozilla.org/api/v3/addons/compat-override/?guid=%IDS%&lang=%LOCALE%");
pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/firefox/search?q=%TERMS%&platform=%OS%&appver=%VERSION%");
pref("extensions.webservice.discoverURL", "https://discovery.addons.mozilla.org/%LOCALE%/firefox/discovery/pane/%VERSION%/%OS%/%COMPATIBILITY_MODE%");
pref("extensions.getAddons.link.url", "https://addons.mozilla.org/%LOCALE%/firefox/");
pref("extensions.getAddons.themes.browseURL", "https://addons.mozilla.org/%LOCALE%/firefox/themes/?src=firefox");
+pref("extensions.getAddons.langpacks.url", "https://services.addons.mozilla.org/api/v3/addons/language-tools/?app=firefox&type=language&appversion=%VERSION%");
pref("extensions.update.autoUpdateDefault", true);
// Check AUS for system add-on updates.
pref("extensions.systemAddon.update.url", "https://aus5.mozilla.org/update/3/SystemAddons/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
// Disable add-ons that are not installed by the user in all scopes by default.
// See the SCOPE constants in AddonManager.jsm for values to use here.
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -207,16 +207,17 @@ pref("extensions.update.url", "https://v
pref("extensions.update.background.url", "https://versioncheck-bg.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%¤tAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%");
/* preferences for the Get Add-ons pane */
pref("extensions.getAddons.cache.enabled", true);
pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/android/search?q=%TERMS%&platform=%OS%&appver=%VERSION%");
pref("extensions.getAddons.browseAddons", "https://addons.mozilla.org/%LOCALE%/android/");
pref("extensions.getAddons.get.url", "https://services.addons.mozilla.org/api/v3/addons/search/?guid=%IDS%&lang=%LOCALE%");
pref("extensions.getAddons.compatOverides.url", "https://services.addons.mozilla.org/api/v3/addons/compat-override/?guid=%IDS%&lang=%LOCALE%");
+pref("extensions.getAddons.langpacks.url", "https://services.addons.mozilla.org/api/v3/addons/language-tools/?app=android&type=language&appversion=%VERSION%");
/* preference for the locale picker */
pref("extensions.getLocales.get.url", "");
pref("extensions.compatability.locales.buildid", "0");
/* Don't let XPIProvider install distribution add-ons; we do our own thing on mobile. */
pref("extensions.installDistroAddons", false);
--- a/toolkit/mozapps/extensions/internal/AddonRepository.jsm
+++ b/toolkit/mozapps/extensions/internal/AddonRepository.jsm
@@ -15,24 +15,27 @@ XPCOMUtils.defineLazyModuleGetters(this,
ServiceRequest: "resource://gre/modules/ServiceRequest.jsm",
NetUtil: "resource://gre/modules/NetUtil.jsm",
OS: "resource://gre/modules/osfile.jsm",
Preferences: "resource://gre/modules/Preferences.jsm",
});
var EXPORTED_SYMBOLS = [ "AddonRepository" ];
+Cu.importGlobalProperties(["fetch"]);
+
const PREF_GETADDONS_CACHE_ENABLED = "extensions.getAddons.cache.enabled";
const PREF_GETADDONS_CACHE_TYPES = "extensions.getAddons.cache.types";
const PREF_GETADDONS_CACHE_ID_ENABLED = "extensions.%ID%.getAddons.cache.enabled";
const PREF_GETADDONS_BROWSEADDONS = "extensions.getAddons.browseAddons";
const PREF_GETADDONS_BYIDS = "extensions.getAddons.get.url";
const PREF_COMPAT_OVERRIDES = "extensions.getAddons.compatOverides.url";
const PREF_GETADDONS_BROWSESEARCHRESULTS = "extensions.getAddons.search.browseURL";
const PREF_GETADDONS_DB_SCHEMA = "extensions.getAddons.databaseSchema";
+const PREF_GET_LANGPACKS = "extensions.getAddons.langpacks.url";
const PREF_METADATA_LASTUPDATE = "extensions.getAddons.cache.lastUpdate";
const PREF_METADATA_UPDATETHRESHOLD_SEC = "extensions.getAddons.cache.updateThreshold";
const DEFAULT_METADATA_UPDATETHRESHOLD_SEC = 172800; // two days
const DEFAULT_CACHE_TYPES = "extension,theme,locale,dictionary";
const FILE_DATABASE = "addons.json";
@@ -713,17 +716,17 @@ var AddonRepository = {
}
compat.compatRanges.push(override);
}
return compat;
},
// Create url from preference, returning null if preference does not exist
- _formatURLPref(aPreference, aSubstitutions) {
+ _formatURLPref(aPreference, aSubstitutions = {}) {
let url = Services.prefs.getCharPref(aPreference, "");
if (!url) {
logger.warn("_formatURLPref: Couldn't get pref: " + aPreference);
return null;
}
url = url.replace(/%([A-Z_]+)%/g, function(aMatch, aKey) {
return (aKey in aSubstitutions) ? encodeURIComponent(aSubstitutions[aKey])
@@ -754,16 +757,49 @@ var AddonRepository = {
}
}
return null;
},
flush() {
return AddonDatabase.flush();
},
+
+ async getAvailableLangpacks() {
+ // This should be the API endpoint documented at:
+ // http://addons-server.readthedocs.io/en/latest/topics/api/addons.html#language-tools
+ let url = this._formatURLPref(PREF_GET_LANGPACKS);
+
+ let response = await fetch(url);
+ if (!response.ok) {
+ throw new Error("fetching available language packs failed");
+ }
+
+ let data = await response.json();
+
+ let result = [];
+ for (let entry of data.results) {
+ if (!entry.current_compatible_version ||
+ !entry.current_compatible_version.files) {
+ continue;
+ }
+
+ for (let file of entry.current_compatible_version.files) {
+ if (file.platform == "all" || file.platform == Services.appinfo.OS.toLowerCase()) {
+ result.push({
+ target_locale: entry.target_locale,
+ url: file.url,
+ hash: file.hash,
+ });
+ }
+ }
+ }
+
+ return result;
+ },
};
var AddonDatabase = {
connectionPromise: null,
_loaded: false,
_saveTask: null,
_blockerAdded: false,
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_AddonRepository_langpacks.js
@@ -0,0 +1,95 @@
+ChromeUtils.import("resource://gre/modules/addons/AddonRepository.jsm");
+
+const PREF_GET_LANGPACKS = "extensions.getAddons.langpacks.url";
+
+let server = AddonTestUtils.createHttpServer({hosts: ["example.com"]});
+
+add_task(async function setup() {
+ Services.prefs.setStringPref(PREF_GET_LANGPACKS, "http://example.com/langpacks.json");
+
+ function setData(data) {
+ if (typeof data != "string") {
+ data = JSON.stringify(data);
+ }
+
+ server.registerPathHandler("/langpacks.json", (request, response) => {
+ response.setHeader("content-type", "application/json");
+ response.write(data);
+ });
+ }
+
+ const EXPECTED = [
+ {
+ target_locale: "kl",
+ url: "http://example.com/langpack1.xpi",
+ hash: "sha256:0123456789abcdef",
+ },
+ {
+ target_locale: "fo",
+ url: "http://example.com/langpack2.xpi",
+ hash: "sha256:fedcba9876543210",
+ },
+ ];
+
+ setData({
+ results: [
+ // A simple entry
+ {
+ target_locale: EXPECTED[0].target_locale,
+ current_compatible_version: {
+ files: [
+ {
+ platform: "all",
+ url: EXPECTED[0].url,
+ hash: EXPECTED[0].hash,
+ },
+ ],
+ },
+ },
+
+ // An entry with multiple supported platforms
+ {
+ target_locale: EXPECTED[1].target_locale,
+ current_compatible_version: {
+ files: [
+ {
+ platform: "somethingelse",
+ url: "http://example.com/bogus.xpi",
+ hash: "sha256:abcd",
+ },
+ {
+ platform: Services.appinfo.OS.toLowerCase(),
+ url: EXPECTED[1].url,
+ hash: EXPECTED[1].hash,
+ },
+ ],
+ },
+ },
+
+ // An entry with no matching platform
+ {
+ target_locale: "bla",
+ current_compatible_version: {
+ files: [
+ {
+ platform: "unsupportedplatform",
+ url: "http://example.com/bogus2.xpi",
+ hash: "sha256:1234",
+ },
+ ],
+ },
+ },
+ ]
+ });
+
+ let result = await AddonRepository.getAvailableLangpacks();
+ equal(result.length, 2, "Got 2 results");
+
+ deepEqual(result[0], EXPECTED[0], "Got expected result for simple entry");
+ deepEqual(result[1], EXPECTED[1], "Got expected result for multi-platform entry");
+
+ setData("not valid json");
+ Assert.rejects(AddonRepository.getAvailableLangpacks(),
+ /SyntaxError/, "Got parse error on invalid JSON");
+
+});
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
@@ -6,16 +6,17 @@ firefox-appdir = browser
dupe-manifest =
support-files =
data/**
[test_AddonRepository.js]
[test_AddonRepository_cache.js]
# Bug 676992: test consistently hangs on Android
skip-if = os == "android"
+[test_AddonRepository_langpacks.js]
[test_AddonRepository_paging.js]
[test_LightweightThemeManager.js]
[test_ProductAddonChecker.js]
[test_XPIStates.js]
[test_XPIcancel.js]
[test_addonStartup.js]
[test_asyncBlocklistLoad.js]
tags = blocklist