Skip to content

Commit

Permalink
Bug 1793557 - Convert Extension.jsm strings to Fluent. r=mkmelin,geck…
Browse files Browse the repository at this point in the history
…oview-reviewers,robwu,flod,owlish

This changes the arguments of `ExtensionData.formatPermissionStrings()`.
The second `bundle` arg is dropped, and a `localization` option is added.
Call sites in m-c are updated, but this will also need a matching update for Thunderbird.
A few Thunderbird test cases will also need to be updated,
as they currently point to a non-existing localization file paths
"messenger/addons.ftl" and "messenger/addonPermissions.ftl".

As discussed at the addon workweek,
the Fluent l10n keys for extension permissions match the pattern:

    webext-perms-description-{name}

where `{name}` is the permission's sanitized name.
A fluent-lint exception is added for the capitalization of these generated names.
To allow for message updates and subsequent l10n identifier updates,
a `PERMISSION_L10N_ID_OVERRIDES` map is provided.

Because Fluent localization keys are not enumerable
and attempting to format a missing key is an error,
the `PERMISSIONS_WITH_MESSAGE` set must be kept in sync with message updates.

Differential Revision: https://phabricator.services.mozilla.com/D158663
  • Loading branch information
eemeli committed May 23, 2023
1 parent e281133 commit 411a529
Show file tree
Hide file tree
Showing 18 changed files with 1,118 additions and 743 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,8 @@ add_task(async function test_sideloading() {
panel,
/\/foo-icon\.png$/,
[
["webextPerms.hostDescription.allUrls"],
["webextPerms.description.history"],
["webext-perms-host-description-all-urls"],
["webext-perms-description-history"],
],
kSideloaded
);
Expand Down Expand Up @@ -229,7 +229,7 @@ add_task(async function test_sideloading() {
checkNotification(
panel,
DEFAULT_ICON_URL,
[["webextPerms.hostDescription.allUrls"]],
[["webext-perms-host-description-all-urls"]],
kSideloaded
);

Expand Down Expand Up @@ -296,7 +296,7 @@ add_task(async function test_sideloading() {
checkNotification(
panel,
DEFAULT_ICON_URL,
[["webextPerms.hostDescription.allUrls"]],
[["webext-perms-host-description-all-urls"]],
kSideloaded
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,15 @@ add_task(async function test_unsigned() {
let description = panel.querySelector(
".popup-notification-description"
).textContent;
checkPermissionString(
description,
"webextPerms.headerUnsignedWithPerms",
undefined,
`Install notification includes unsigned warning`
);
const expected = formatExtValue("webext-perms-header-unsigned-with-perms", {
extension: "<>",
});
for (let part of expected.split("<>")) {
ok(
description.includes(part),
"Install notification includes unsigned warning"
);
}

// cancel the install
let promise = promiseInstallEvent({ id: ID }, "onInstallCancelled");
Expand Down
91 changes: 42 additions & 49 deletions browser/base/content/test/webextensions/head.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,26 @@ const { PermissionTestUtils } = ChromeUtils.importESModule(
"resource://testing-common/PermissionTestUtils.sys.mjs"
);

let extL10n = null;
/**
* @param {string} id
* @param {object} [args]
* @returns {string}
*/
function formatExtValue(id, args) {
if (!extL10n) {
extL10n = new Localization(
[
"toolkit/global/extensions.ftl",
"toolkit/global/extensionPermissions.ftl",
"branding/brand.ftl",
],
true
);
}
return extL10n.formatValueSync(id, args);
}

/**
* Wait for the given PopupNotification to display
*
Expand Down Expand Up @@ -185,37 +205,6 @@ function isDefaultIcon(icon) {
return icon == "chrome://mozapps/skin/extensions/extensionGeneric.svg";
}

/**
* Check the contents of an individual permission string.
* This function is fairly specific to the use here and probably not
* suitable for re-use elsewhere...
*
* @param {string} string
* The string value to check (i.e., pulled from the DOM)
* @param {string} key
* The key in browser.properties for the localized string to
* compare with.
* @param {string|null} param
* Optional string to substitute for %S in the localized string.
* @param {string} msg
* The message to be emitted as part of the actual test.
*/
function checkPermissionString(string, key, param, msg) {
let localizedString = param
? gBrowserBundle.formatStringFromName(key, [param])
: gBrowserBundle.GetStringFromName(key);

// If this is a parameterized string and the parameter isn't given,
// just do a simple comparison of the text before and after the %S
if (localizedString.includes("%S")) {
let i = localizedString.indexOf("%S");
ok(string.startsWith(localizedString.slice(0, i)), msg);
ok(string.endsWith(localizedString.slice(i + 2)), msg);
} else {
is(string, localizedString, msg);
}
}

/**
* Check the contents of a permission popup notification
*
Expand All @@ -230,7 +219,7 @@ function checkPermissionString(string, key, param, msg) {
* @param {array} permissions
* The expected entries in the permissions list. Each element
* in this array is itself a 2-element array with the string key
* for the item (e.g., "webextPerms.description.foo") and an
* for the item (e.g., "webext-perms-description-foo") and an
* optional formatting parameter.
* @param {boolean} sideloaded
* Whether the notification is for a sideloaded extenion.
Expand All @@ -255,19 +244,17 @@ function checkNotification(panel, checkIcon, permissions, sideloaded) {
let description = panel.querySelector(
".popup-notification-description"
).textContent;
let expectedDescription = "webextPerms.header";
let descL10nId = "webext-perms-header";
if (permissions.length) {
expectedDescription += "WithPerms";
descL10nId = "webext-perms-header-with-perms";
}
if (sideloaded) {
expectedDescription = "webextPerms.sideloadHeader";
descL10nId = "webext-perms-sideload-header";
}
checkPermissionString(
description,
expectedDescription,
undefined,
`Description is the expected one`
);
const exp = formatExtValue(descL10nId, { extension: "<>" }).split("<>");
ok(description.startsWith(exp.at(0)), "Description is the expected one");
ok(description.endsWith(exp.at(-1)), "Description is the expected one");

is(
learnMoreLink.hidden,
!permissions.length,
Expand All @@ -293,10 +280,10 @@ function checkNotification(panel, checkIcon, permissions, sideloaded) {
);
for (let i in permissions) {
let [key, param] = permissions[i];
checkPermissionString(
const expected = formatExtValue(key, param);
is(
ul.children[i].textContent,
key,
param,
expected,
`Permission number ${i + 1} is correct`
);
}
Expand Down Expand Up @@ -374,13 +361,19 @@ async function testInstallMethod(installFn, telemetryBase) {
// path, just make sure we've got a jar url pointing to the right path
// inside the jar.
checkNotification(panel, /^jar:file:\/\/.*\/icon\.png$/, [
["webextPerms.hostDescription.wildcard", "wildcard.domain"],
["webextPerms.hostDescription.oneSite", "singlehost.domain"],
["webextPerms.description.nativeMessaging"],
[
"webext-perms-host-description-wildcard",
{ domain: "wildcard.domain" },
],
[
"webext-perms-host-description-one-site",
{ domain: "singlehost.domain" },
],
["webext-perms-description-nativeMessaging"],
// The below permissions are deliberately in this order as permissions
// are sorted alphabetically by the permission string to match AMO.
["webextPerms.description.history"],
["webextPerms.description.tabs"],
["webext-perms-description-history"],
["webext-perms-description-tabs"],
]);
} else if (filename == NO_PERMS_XPI) {
checkNotification(panel, isDefaultIcon, []);
Expand Down
135 changes: 0 additions & 135 deletions browser/locales/en-US/chrome/browser/browser.properties
Original file line number Diff line number Diff line change
Expand Up @@ -73,153 +73,18 @@ addonInstallBlockedByPolicy=%1$S (%2$S) is blocked by your system administrator.
addonDomainBlockedByPolicy=Your system administrator prevented this site from asking you to install software on your computer.
addonInstallFullScreenBlocked=Add-on installation is not allowed while in or before entering fullscreen mode.

# LOCALIZATION NOTE (webextPerms.header,webextPerms.headerWithPerms,webextPerms.headerUnsigned,webextPerms.headerUnsignedWithPerms)
# This string is used as a header in the webextension permissions dialog,
# %S is replaced with the localized name of the extension being installed.
# See https://bug1308309.bmoattachments.org/attachment.cgi?id=8814612
# for an example of the full dialog.
# Note, this string will be used as raw markup. Avoid characters like <, >, &
webextPerms.header=Add %S?
webextPerms.headerWithPerms=Add %S? This extension will have permission to:
webextPerms.headerUnsigned=Add %S? This extension is unverified. Malicious extensions can steal your private information or compromise your computer. Only add it if you trust the source.
webextPerms.headerUnsignedWithPerms=Add %S? This extension is unverified. Malicious extensions can steal your private information or compromise your computer. Only add it if you trust the source. This extension will have permission to:

webextPerms.learnMore2=Learn more
webextPerms.add.label=Add
webextPerms.add.accessKey=A
webextPerms.cancel.label=Cancel
webextPerms.cancel.accessKey=C

# LOCALIZATION NOTE (webextPerms.sideloadMenuItem)
# %1$S will be replaced with the localized name of the sideloaded add-on.
# %2$S will be replace with the name of the application (e.g., Firefox, Nightly)
webextPerms.sideloadMenuItem=%1$S added to %2$S

# LOCALIZATION NOTE (webextPerms.sideloadHeader)
# This string is used as a header in the webextension permissions dialog
# when the extension is side-loaded.
# %S is replaced with the localized name of the extension being installed.
# Note, this string will be used as raw markup. Avoid characters like <, >, &
webextPerms.sideloadHeader=%S added
webextPerms.sideloadText2=Another program on your computer installed an add-on that may affect your browser. Please review this add-on’s permissions requests and choose to Enable or Cancel (to leave it disabled).
webextPerms.sideloadTextNoPerms=Another program on your computer installed an add-on that may affect your browser. Please choose to Enable or Cancel (to leave it disabled).

webextPerms.sideloadEnable.label=Enable
webextPerms.sideloadEnable.accessKey=E
webextPerms.sideloadCancel.label=Cancel
webextPerms.sideloadCancel.accessKey=C

# LOCALIZATION NOTE (webextPerms.updateMenuItem)
# %S will be replaced with the localized name of the extension which
# has been updated.
webextPerms.updateMenuItem=%S requires new permissions

# LOCALIZATION NOTE (webextPerms.updateText)
# %S is replaced with the localized name of the updated extension.
# Note, this string will be used as raw markup. Avoid characters like <, >, &
webextPerms.updateText2=%S has been updated. You must approve new permissions before the updated version will install. Choosing “Cancel” will maintain your current extension version. This extension will have permission to:

webextPerms.updateAccept.label=Update
webextPerms.updateAccept.accessKey=U

# LOCALIZATION NOTE (webextPerms.optionalPermsHeader)
# %S is replace with the localized name of the extension requested new
# permissions.
# Note, this string will be used as raw markup. Avoid characters like <, >, &
webextPerms.optionalPermsHeader=%S requests additional permissions.
webextPerms.optionalPermsListIntro=It wants to:
webextPerms.optionalPermsAllow.label=Allow
webextPerms.optionalPermsAllow.accessKey=A
webextPerms.optionalPermsDeny.label=Deny
webextPerms.optionalPermsDeny.accessKey=D

webextPerms.description.bookmarks=Read and modify bookmarks
webextPerms.description.browserSettings=Read and modify browser settings
webextPerms.description.browsingData=Clear recent browsing history, cookies, and related data
webextPerms.description.clipboardRead=Get data from the clipboard
webextPerms.description.clipboardWrite=Input data to the clipboard
webextPerms.description.declarativeNetRequest=Block content on any page
webextPerms.description.declarativeNetRequestFeedback=Read your browsing history
webextPerms.description.devtools=Extend developer tools to access your data in open tabs
webextPerms.description.downloads=Download files and read and modify the browser’s download history
webextPerms.description.downloads.open=Open files downloaded to your computer
webextPerms.description.find=Read the text of all open tabs
webextPerms.description.geolocation=Access your location
webextPerms.description.history=Access browsing history
webextPerms.description.management=Monitor extension usage and manage themes
# LOCALIZATION NOTE (webextPerms.description.nativeMessaging)
# %S will be replaced with the name of the application
webextPerms.description.nativeMessaging=Exchange messages with programs other than %S
webextPerms.description.notifications=Display notifications to you
webextPerms.description.pkcs11=Provide cryptographic authentication services
webextPerms.description.privacy=Read and modify privacy settings
webextPerms.description.proxy=Control browser proxy settings
webextPerms.description.sessions=Access recently closed tabs
webextPerms.description.tabs=Access browser tabs
webextPerms.description.tabHide=Hide and show browser tabs
webextPerms.description.topSites=Access browsing history
webextPerms.description.webNavigation=Access browser activity during navigation

webextPerms.hostDescription.allUrls=Access your data for all websites

# LOCALIZATION NOTE (webextPerms.hostDescription.wildcard)
# %S will be replaced by the DNS domain for which a webextension
# is requesting access (e.g., mozilla.org)
webextPerms.hostDescription.wildcard=Access your data for sites in the %S domain

# LOCALIZATION NOTE (webextPerms.hostDescription.tooManyWildcards):
# Semi-colon list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 will be replaced by an integer indicating the number of additional
# domains for which this webextension is requesting permission.
webextPerms.hostDescription.tooManyWildcards=Access your data in #1 other domain;Access your data in #1 other domains

# LOCALIZATION NOTE (webextPerms.hostDescription.oneSite)
# %S will be replaced by the DNS host name for which a webextension
# is requesting access (e.g., www.mozilla.org)
webextPerms.hostDescription.oneSite=Access your data for %S

# LOCALIZATION NOTE (webextPerms.hostDescription.tooManySites)
# Semi-colon list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 will be replaced by an integer indicating the number of additional
# hosts for which this webextension is requesting permission.
webextPerms.hostDescription.tooManySites=Access your data on #1 other site;Access your data on #1 other sites

# LOCALIZATION NOTE (webextSitePerms.headerWithPerms,webextSitePerms.headerUnsignedWithPerms)
# This string is used as a header in the webextension permissions dialog,
# %1$S is replaced with the localized name of the extension being installed.
# %2$S will be replaced by the DNS host name for which a webextension enables permissions
# Note, this string will be used as raw markup. Avoid characters like <, >, &
webextSitePerms.headerWithPerms=Add %1$S? This extension grants the following capabilities to %2$S:
webextSitePerms.headerUnsignedWithPerms=Add %1$S? This extension is unverified. Malicious extensions can steal your private information or compromise your computer. Only add it if you trust the source. This extension grants the following capabilities to %2$S:

# LOCALIZATION NOTE (webextSitePerms.headerWithGatedPerms.midi)
# This string is used as a header in the webextension permissions dialog for synthetic add-ons.
# The part of the string describing what privileges the extension gives should be consistent
# with the value of webextSitePerms.description.{sitePermission}.
# %S is the hostname of the site the add-on is being installed from.
# Note, this string will be used as raw markup. Avoid characters like <, >, &
webextSitePerms.headerWithGatedPerms.midi=This add-on gives %S access to your MIDI devices.

# LOCALIZATION NOTE (webextSitePerms.headerWithGatedPerms.midi-sysex)
# This string is used as a header in the webextension permissions dialog for synthetic add-ons.
# The part of the string describing what privileges the extension gives should be consistent
# with the value of webextSitePerms.description.{sitePermission}.
# %S is the hostname of the site the add-on is being installed from.
# Note, this string will be used as raw markup. Avoid characters like <, >, &
webextSitePerms.headerWithGatedPerms.midi-sysex=This add-on gives %S access to your MIDI devices (with SysEx support).

# LOCALIZATION NOTE (webextSitePerms.descriptionGatedPerms)
# This string is used as description in the webextension permissions dialog for synthetic add-ons.
# Note, the \n\n is used to create a line break between the two sections.
# Note, this string will be used as raw markup. Avoid characters like <, >, &
webextSitePerms.descriptionGatedPerms.midi=These are usually plug-in devices like audio synthesizers, but might also be built into your computer.\n\nWebsites are normally not allowed to access MIDI devices. Improper usage could cause damage or compromise security.

# These should remain in sync with permissions.NAME.label in sitePermissions.properties
webextSitePerms.description.midi=Access MIDI devices
webextSitePerms.description.midi-sysex=Access MIDI devices with SysEx support

# LOCALIZATION NOTE (webext.defaultSearch.description)
# %1$S is replaced with the localized named of the extension that is asking to change the default search engine.
# %2$S is replaced with the name of the current search engine
Expand Down
6 changes: 1 addition & 5 deletions browser/modules/ExtensionsUI.jsm
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ const DEFAULT_EXTENSION_ICON =
"chrome://mozapps/skin/extensions/extensionGeneric.svg";

const BROWSER_PROPERTIES = "chrome://browser/locale/browser.properties";
const BRAND_PROPERTIES = "chrome://branding/locale/brand.properties";

const HTML_NS = "http://www.w3.org/1999/xhtml";

Expand Down Expand Up @@ -317,11 +316,8 @@ var ExtensionsUI = {
// Create a set of formatted strings for a permission prompt
_buildStrings(info) {
let bundle = Services.strings.createBundle(BROWSER_PROPERTIES);
let brandBundle = Services.strings.createBundle(BRAND_PROPERTIES);
let appName = brandBundle.GetStringFromName("brandShortName");
let info2 = Object.assign({ appName }, info);

let strings = lazy.ExtensionData.formatPermissionStrings(info2, bundle, {
let strings = lazy.ExtensionData.formatPermissionStrings(info, {
collapseOrigins: true,
});
strings.addonName = info.addon.name;
Expand Down
Loading

0 comments on commit 411a529

Please sign in to comment.