Bug 1190323: Part 1 - [webext] Fix @@extension_id locale string substitutions. r?billm
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -421,16 +421,18 @@ this.ExtensionData = function(rootURI) {
this.id = null;
this.localeData = null;
this._promiseLocales = null;
this.errors = [];
};
ExtensionData.prototype = {
+ builtinMessages: null,
+
get logger() {
let id = this.id || "<unknown>";
return Log.repository.getLogger(LOGGER_ID_BASE + id);
},
// Report an error about the extension's manifest file.
manifestError(message) {
this.packagingError(`Reading manifest: ${message}`);
@@ -604,30 +606,35 @@ ExtensionData.prototype = {
let entries = yield this.readDirectory("_locales");
for (let file of entries) {
if (file.isDir) {
let locale = this.normalizeLocaleCode(file.name);
locales.set(locale, file.name);
}
}
+ this.localeData = new LocaleData({
+ defaultLocale: this.defaultLocale,
+ locales,
+ builtinMessages: this.builtinMessages,
+ });
+
return locales;
}.bind(this));
}
return this._promiseLocales;
},
// Reads the locale messages for all locales, and returns a promise which
// resolves to a Map of locale messages upon completion. Each key in the map
// is a Gecko-compatible locale code, and each value is a locale data object
// as returned by |readLocaleFile|.
initAllLocales: Task.async(function* () {
let locales = yield this.promiseLocales();
- this.localeData = new LocaleData({ defaultLocale: this.defaultLocale, locales });
yield Promise.all(Array.from(locales.keys(),
locale => this.readLocaleFile(locale)));
let defaultLocale = this.defaultLocale;
if (defaultLocale) {
if (!locales.has(defaultLocale)) {
this.manifestError('Value for "default_locale" property must correspond to ' +
@@ -646,18 +653,16 @@ ExtensionData.prototype = {
// default locale if no locale code is given, and sets it as the currently
// selected locale on success.
//
// Pre-loads the default locale for fallback message processing, regardless
// of the locale specified.
//
// If no locales are unavailable, resolves to |null|.
initLocale: Task.async(function* (locale = this.defaultLocale) {
- this.localeData = new LocaleData({ defaultLocale: this.defaultLocale });
-
if (locale == null) {
return null;
}
let promises = [this.readLocaleFile(locale)];
let { defaultLocale } = this;
if (locale != defaultLocale && !this.localeData.has(defaultLocale)) {
@@ -965,16 +970,22 @@ Extension.prototype = extend(Object.crea
callOnClose(obj) {
this.onShutdown.add(obj);
},
forgetOnClose(obj) {
this.onShutdown.delete(obj);
},
+ get builtinMessages() {
+ return new Map([
+ ["@@extension_id", this.uuid],
+ ]);
+ },
+
// Reads the locale file for the given Gecko-compatible locale code, or if
// no locale is given, the available locale closest to the UI locale.
// Sets the currently selected locale on success.
initLocale: Task.async(function* (locale = undefined) {
if (locale === undefined) {
let locales = yield this.promiseLocales();
let localeList = Array.from(locales.keys(), locale => {
--- a/toolkit/components/extensions/ExtensionUtils.jsm
+++ b/toolkit/components/extensions/ExtensionUtils.jsm
@@ -108,42 +108,48 @@ DefaultWeakMap.prototype = {
},
};
function LocaleData(data) {
this.defaultLocale = data.defaultLocale;
this.selectedLocale = data.selectedLocale;
this.locales = data.locales || new Map();
- // Map(locale-name -> Map(message-key -> localized-strings))
+ // Map(locale-name -> Map(message-key -> localized-string))
//
// Contains a key for each loaded locale, each of which is a
// Map of message keys to their localized strings.
this.messages = data.messages || new Map();
+
+ if (data.builtinMessages) {
+ this.messages.set(this.BUILTIN, data.builtinMessages);
+ }
}
LocaleData.prototype = {
// Representation of the object to send to content processes. This
// should include anything the content process might need.
serialize() {
return {
defaultLocale: this.defaultLocale,
selectedLocale: this.selectedLocale,
messages: this.messages,
locales: this.locales,
};
},
+ BUILTIN: "@@BUILTIN_MESSAGES",
+
has(locale) {
return this.messages.has(locale);
},
// https://developer.chrome.com/extensions/i18n
localizeMessage(message, substitutions = [], locale = this.selectedLocale, defaultValue = "??") {
- let locales = new Set([locale, this.defaultLocale]
+ let locales = new Set([this.BUILTIN, locale, this.defaultLocale]
.filter(locale => this.messages.has(locale)));
// Message names are case-insensitive, so normalize them to lower-case.
message = message.toLowerCase();
for (let locale of locales) {
let messages = this.messages.get(locale);
if (messages.has(message)) {
let str = messages.get(message);
@@ -165,25 +171,17 @@ LocaleData.prototype = {
return dollarSigns;
}
};
return str.replace(/\$(?:([1-9]\d*)|(\$+))/g, replacer);
}
}
// Check for certain pre-defined messages.
- if (message == "@@extension_id") {
- if ("uuid" in this) {
- // Per Chrome, this isn't available before an ID is guaranteed
- // to have been assigned, namely, in manifest files.
- // This should only be present in instances of the |Extension|
- // subclass.
- return this.uuid;
- }
- } else if (message == "@@ui_locale") {
+ if (message == "@@ui_locale") {
// Return the browser locale, but convert it to a Chrome-style
// locale code.
return Locale.getLocale().replace(/-/g, "_");
} else if (message.startsWith("@@bidi_")) {
let registry = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry);
let rtl = registry.isLocaleRTL("global");
if (message == "@@bidi_dir") {