Bug 1793557 - Convert Extension.jsm strings to Fluent. r=mkmelin,geckoview-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
This commit is contained in:
Eemeli Aro 2023-05-23 12:00:01 +00:00
Родитель e281133e36
Коммит 411a529019
18 изменённых файлов: 1122 добавлений и 747 удалений

Просмотреть файл

@ -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
);
@ -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
);
@ -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
);

Просмотреть файл

@ -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");

Просмотреть файл

@ -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
*
@ -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
*
@ -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.
@ -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,
@ -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`
);
}
@ -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, []);

Просмотреть файл

@ -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-ons 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 browsers 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

Просмотреть файл

@ -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";
@ -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;

Просмотреть файл

@ -12,6 +12,15 @@ const PAGE_WITH_IFRAMES_URL = `https://example.org/document-builder.sjs?html=
'https://example.net/document-builder.sjs?html=CrossOrigin"'
)}"></iframe>`;
const l10n = new Localization(
[
"toolkit/global/extensions.ftl",
"toolkit/global/extensionPermissions.ftl",
"branding/brand.ftl",
],
true
);
const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js");
ChromeUtils.defineModuleGetter(
this,
@ -141,19 +150,16 @@ add_task(async function testRequestMIDIAccess() {
let installDialog = await dialogPromise;
is(
installDialog.querySelector(".popup-notification-description").textContent,
gNavigatorBundle.getFormattedString(
"webextSitePerms.headerWithGatedPerms.midi-sysex",
[testPageHost]
l10n.formatValueSync(
"webext-site-perms-header-with-gated-perms-midi-sysex",
{ hostname: testPageHost }
),
"Install dialog has expected header text"
);
is(
installDialog.querySelector("popupnotificationcontent description")
.textContent,
gNavigatorBundle.getFormattedString(
"webextSitePerms.descriptionGatedPerms.midi",
[testPageHost]
),
l10n.formatValueSync("webext-site-perms-description-gated-perms-midi"),
"Install dialog has expected description"
);
@ -288,19 +294,15 @@ add_task(async function testRequestMIDIAccess() {
is(
installDialog.querySelector(".popup-notification-description").textContent,
gNavigatorBundle.getFormattedString(
"webextSitePerms.headerWithGatedPerms.midi",
[testPageHost]
),
l10n.formatValueSync("webext-site-perms-header-with-gated-perms-midi", {
hostname: testPageHost,
}),
"Install dialog has expected header text"
);
is(
installDialog.querySelector("popupnotificationcontent description")
.textContent,
gNavigatorBundle.getFormattedString(
"webextSitePerms.descriptionGatedPerms.midi",
[testPageHost]
),
l10n.formatValueSync("webext-site-perms-description-gated-perms-midi"),
"Install dialog has expected description"
);

Просмотреть файл

@ -2,99 +2,6 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# In Extension.jsm
# LOCALIZATION NOTE (webextPerms.header)
# 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.add.label=Add
webextPerms.cancel.label=Cancel
# 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:
# 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 (webextPerms.updateText)
# %S is replaced with the localized name of the updated extension.
webextPerms.updateText=%S has been updated. You must approve new permissions before the updated version will install. Choosing “Cancel” will maintain your current add-on version.
webextPerms.updateAccept.label=Update
# LOCALIZATION NOTE (webextPerms.optionalPermsHeader)
# %S is replaced with the localized name of the extension requesting new
# permissions.
webextPerms.optionalPermsHeader=%S requests additional permissions.
webextPerms.optionalPermsListIntro=It wants to:
webextPerms.optionalPermsAllow.label=Allow
webextPerms.optionalPermsDeny.label=Deny
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 browsers 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.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.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
# Web Console API (in GeckoViewConsole.jsm)
stacktrace.anonymousFunction=<anonymous>
stacktrace.outputMessage=Stack trace from %S, function %S, line %S.

Просмотреть файл

@ -0,0 +1,391 @@
# Any copyright is dedicated to the Public Domain.
# http://creativecommons.org/publicdomain/zero/1.0/
import fluent.syntax.ast as FTL
from fluent.migrate.helpers import TERM_REFERENCE, VARIABLE_REFERENCE
from fluent.migrate.transforms import COPY, PLURALS, REPLACE, REPLACE_IN_TEXT
def migrate(ctx):
"""Bug 1793557 - Convert Extension.jsm to Fluent, part {index}."""
source = "browser/chrome/browser/browser.properties"
extensions = "toolkit/toolkit/global/extensions.ftl"
permissions = "toolkit/toolkit/global/extensionPermissions.ftl"
ctx.add_transforms(
extensions,
extensions,
[
FTL.Message(
id=FTL.Identifier("webext-perms-header"),
value=REPLACE(
source,
"webextPerms.header",
{"%1$S": VARIABLE_REFERENCE("extension")},
),
),
FTL.Message(
id=FTL.Identifier("webext-perms-header-with-perms"),
value=REPLACE(
source,
"webextPerms.headerWithPerms",
{"%1$S": VARIABLE_REFERENCE("extension")},
),
),
FTL.Message(
id=FTL.Identifier("webext-perms-header-unsigned"),
value=REPLACE(
source,
"webextPerms.headerUnsigned",
{"%1$S": VARIABLE_REFERENCE("extension")},
),
),
FTL.Message(
id=FTL.Identifier("webext-perms-header-unsigned-with-perms"),
value=REPLACE(
source,
"webextPerms.headerUnsignedWithPerms",
{"%1$S": VARIABLE_REFERENCE("extension")},
),
),
FTL.Message(
id=FTL.Identifier("webext-perms-add"),
attributes=[
FTL.Attribute(
id=FTL.Identifier("label"),
value=COPY(source, "webextPerms.add.label"),
),
FTL.Attribute(
id=FTL.Identifier("accesskey"),
value=COPY(source, "webextPerms.add.accessKey"),
),
],
),
FTL.Message(
id=FTL.Identifier("webext-perms-cancel"),
attributes=[
FTL.Attribute(
id=FTL.Identifier("label"),
value=COPY(source, "webextPerms.cancel.label"),
),
FTL.Attribute(
id=FTL.Identifier("accesskey"),
value=COPY(source, "webextPerms.cancel.accessKey"),
),
],
),
FTL.Message(
id=FTL.Identifier("webext-perms-sideload-header"),
value=REPLACE(
source,
"webextPerms.sideloadHeader",
{"%1$S": VARIABLE_REFERENCE("extension")},
),
),
FTL.Message(
id=FTL.Identifier("webext-perms-sideload-text"),
value=COPY(source, "webextPerms.sideloadText2"),
),
FTL.Message(
id=FTL.Identifier("webext-perms-sideload-text-no-perms"),
value=COPY(source, "webextPerms.sideloadTextNoPerms"),
),
FTL.Message(
id=FTL.Identifier("webext-perms-sideload-enable"),
attributes=[
FTL.Attribute(
id=FTL.Identifier("label"),
value=COPY(source, "webextPerms.sideloadEnable.label"),
),
FTL.Attribute(
id=FTL.Identifier("accesskey"),
value=COPY(source, "webextPerms.sideloadEnable.accessKey"),
),
],
),
FTL.Message(
id=FTL.Identifier("webext-perms-sideload-cancel"),
attributes=[
FTL.Attribute(
id=FTL.Identifier("label"),
value=COPY(source, "webextPerms.sideloadCancel.label"),
),
FTL.Attribute(
id=FTL.Identifier("accesskey"),
value=COPY(source, "webextPerms.sideloadCancel.accessKey"),
),
],
),
FTL.Message(
id=FTL.Identifier("webext-perms-update-text"),
value=REPLACE(
source,
"webextPerms.updateText2",
{"%1$S": VARIABLE_REFERENCE("extension")},
),
),
FTL.Message(
id=FTL.Identifier("webext-perms-update-accept"),
attributes=[
FTL.Attribute(
id=FTL.Identifier("label"),
value=COPY(source, "webextPerms.updateAccept.label"),
),
FTL.Attribute(
id=FTL.Identifier("accesskey"),
value=COPY(source, "webextPerms.updateAccept.accessKey"),
),
],
),
FTL.Message(
id=FTL.Identifier("webext-perms-optional-perms-header"),
value=REPLACE(
source,
"webextPerms.optionalPermsHeader",
{"%1$S": VARIABLE_REFERENCE("extension")},
),
),
FTL.Message(
id=FTL.Identifier("webext-perms-optional-perms-list-intro"),
value=COPY(source, "webextPerms.optionalPermsListIntro"),
),
FTL.Message(
id=FTL.Identifier("webext-perms-optional-perms-allow"),
attributes=[
FTL.Attribute(
id=FTL.Identifier("label"),
value=COPY(source, "webextPerms.optionalPermsAllow.label"),
),
FTL.Attribute(
id=FTL.Identifier("accesskey"),
value=COPY(source, "webextPerms.optionalPermsAllow.accessKey"),
),
],
),
FTL.Message(
id=FTL.Identifier("webext-perms-optional-perms-deny"),
attributes=[
FTL.Attribute(
id=FTL.Identifier("label"),
value=COPY(source, "webextPerms.optionalPermsDeny.label"),
),
FTL.Attribute(
id=FTL.Identifier("accesskey"),
value=COPY(source, "webextPerms.optionalPermsDeny.accessKey"),
),
],
),
FTL.Message(
id=FTL.Identifier("webext-perms-host-description-all-urls"),
value=COPY(source, "webextPerms.hostDescription.allUrls"),
),
FTL.Message(
id=FTL.Identifier("webext-perms-host-description-wildcard"),
value=REPLACE(
source,
"webextPerms.hostDescription.wildcard",
{"%1$S": VARIABLE_REFERENCE("domain")},
),
),
FTL.Message(
id=FTL.Identifier("webext-perms-host-description-too-many-wildcards"),
value=PLURALS(
source,
"webextPerms.hostDescription.tooManyWildcards",
VARIABLE_REFERENCE("domainCount"),
foreach=lambda n: REPLACE_IN_TEXT(
n,
{"#1": VARIABLE_REFERENCE("domainCount")},
),
),
),
FTL.Message(
id=FTL.Identifier("webext-perms-host-description-one-site"),
value=REPLACE(
source,
"webextPerms.hostDescription.oneSite",
{"%1$S": VARIABLE_REFERENCE("domain")},
),
),
FTL.Message(
id=FTL.Identifier("webext-perms-host-description-too-many-sites"),
value=PLURALS(
source,
"webextPerms.hostDescription.tooManySites",
VARIABLE_REFERENCE("domainCount"),
foreach=lambda n: REPLACE_IN_TEXT(
n,
{"#1": VARIABLE_REFERENCE("domainCount")},
),
),
),
FTL.Message(
id=FTL.Identifier("webext-site-perms-header-with-gated-perms-midi"),
value=REPLACE(
source,
"webextSitePerms.headerWithGatedPerms.midi",
{
"%1$S": VARIABLE_REFERENCE("hostname"),
},
),
),
FTL.Message(
id=FTL.Identifier(
"webext-site-perms-header-with-gated-perms-midi-sysex"
),
value=REPLACE(
source,
"webextSitePerms.headerWithGatedPerms.midi-sysex",
{
"%1$S": VARIABLE_REFERENCE("hostname"),
},
),
),
FTL.Message(
id=FTL.Identifier("webext-site-perms-description-gated-perms-midi"),
value=COPY(source, "webextSitePerms.descriptionGatedPerms.midi"),
),
FTL.Message(
id=FTL.Identifier("webext-site-perms-header-with-perms"),
value=REPLACE(
source,
"webextSitePerms.headerWithPerms",
{
"%1$S": VARIABLE_REFERENCE("extension"),
"%2$S": VARIABLE_REFERENCE("hostname"),
},
),
),
FTL.Message(
id=FTL.Identifier("webext-site-perms-header-unsigned-with-perms"),
value=REPLACE(
source,
"webextSitePerms.headerUnsignedWithPerms",
{
"%1$S": VARIABLE_REFERENCE("extension"),
"%2$S": VARIABLE_REFERENCE("hostname"),
},
),
),
FTL.Message(
id=FTL.Identifier("webext-site-perms-midi"),
value=COPY(source, "webextSitePerms.description.midi"),
),
FTL.Message(
id=FTL.Identifier("webext-site-perms-midi-sysex"),
value=COPY(source, "webextSitePerms.description.midi-sysex"),
),
],
)
ctx.add_transforms(
permissions,
permissions,
[
FTL.Message(
id=FTL.Identifier("webext-perms-description-bookmarks"),
value=COPY(source, "webextPerms.description.bookmarks"),
),
FTL.Message(
id=FTL.Identifier("webext-perms-description-browserSettings"),
value=COPY(source, "webextPerms.description.browserSettings"),
),
FTL.Message(
id=FTL.Identifier("webext-perms-description-browsingData"),
value=COPY(source, "webextPerms.description.browsingData"),
),
FTL.Message(
id=FTL.Identifier("webext-perms-description-clipboardRead"),
value=COPY(source, "webextPerms.description.clipboardRead"),
),
FTL.Message(
id=FTL.Identifier("webext-perms-description-clipboardWrite"),
value=COPY(source, "webextPerms.description.clipboardWrite"),
),
FTL.Message(
id=FTL.Identifier("webext-perms-description-declarativeNetRequest"),
value=COPY(source, "webextPerms.description.declarativeNetRequest"),
),
FTL.Message(
id=FTL.Identifier(
"webext-perms-description-declarativeNetRequestFeedback"
),
value=COPY(
source, "webextPerms.description.declarativeNetRequestFeedback"
),
),
FTL.Message(
id=FTL.Identifier("webext-perms-description-devtools"),
value=COPY(source, "webextPerms.description.devtools"),
),
FTL.Message(
id=FTL.Identifier("webext-perms-description-downloads"),
value=COPY(source, "webextPerms.description.downloads"),
),
FTL.Message(
id=FTL.Identifier("webext-perms-description-downloads-open"),
value=COPY(source, "webextPerms.description.downloads.open"),
),
FTL.Message(
id=FTL.Identifier("webext-perms-description-find"),
value=COPY(source, "webextPerms.description.find"),
),
FTL.Message(
id=FTL.Identifier("webext-perms-description-geolocation"),
value=COPY(source, "webextPerms.description.geolocation"),
),
FTL.Message(
id=FTL.Identifier("webext-perms-description-history"),
value=COPY(source, "webextPerms.description.history"),
),
FTL.Message(
id=FTL.Identifier("webext-perms-description-management"),
value=COPY(source, "webextPerms.description.management"),
),
FTL.Message(
id=FTL.Identifier("webext-perms-description-nativeMessaging"),
value=REPLACE(
source,
"webextPerms.description.nativeMessaging",
{"%1$S": TERM_REFERENCE("brand-short-name")},
),
),
FTL.Message(
id=FTL.Identifier("webext-perms-description-notifications"),
value=COPY(source, "webextPerms.description.notifications"),
),
FTL.Message(
id=FTL.Identifier("webext-perms-description-pkcs11"),
value=COPY(source, "webextPerms.description.pkcs11"),
),
FTL.Message(
id=FTL.Identifier("webext-perms-description-privacy"),
value=COPY(source, "webextPerms.description.privacy"),
),
FTL.Message(
id=FTL.Identifier("webext-perms-description-proxy"),
value=COPY(source, "webextPerms.description.proxy"),
),
FTL.Message(
id=FTL.Identifier("webext-perms-description-sessions"),
value=COPY(source, "webextPerms.description.sessions"),
),
FTL.Message(
id=FTL.Identifier("webext-perms-description-tabs"),
value=COPY(source, "webextPerms.description.tabs"),
),
FTL.Message(
id=FTL.Identifier("webext-perms-description-tabHide"),
value=COPY(source, "webextPerms.description.tabHide"),
),
FTL.Message(
id=FTL.Identifier("webext-perms-description-topSites"),
value=COPY(source, "webextPerms.description.topSites"),
),
FTL.Message(
id=FTL.Identifier("webext-perms-description-webNavigation"),
value=COPY(source, "webextPerms.description.webNavigation"),
),
],
)

Просмотреть файл

@ -56,6 +56,8 @@ ChromeUtils.defineESModuleGetters(lazy, {
ExtensionDNR: "resource://gre/modules/ExtensionDNR.sys.mjs",
ExtensionDNRStore: "resource://gre/modules/ExtensionDNRStore.sys.mjs",
Log: "resource://gre/modules/Log.sys.mjs",
permissionToL10nId:
"resource://gre/modules/ExtensionPermissionMessages.sys.mjs",
SITEPERMS_ADDON_TYPE:
"resource://gre/modules/addons/siteperms-addon-utils.sys.mjs",
});
@ -75,7 +77,6 @@ XPCOMUtils.defineLazyModuleGetters(lazy, {
ExtensionTelemetry: "resource://gre/modules/ExtensionTelemetry.jsm",
LightweightThemeManager: "resource://gre/modules/LightweightThemeManager.jsm",
NetUtil: "resource://gre/modules/NetUtil.jsm",
PluralForm: "resource://gre/modules/PluralForm.jsm",
Schemas: "resource://gre/modules/Schemas.jsm",
ServiceWorkerCleanUp: "resource://gre/modules/ServiceWorkerCleanUp.jsm",
});
@ -86,6 +87,20 @@ XPCOMUtils.defineLazyGetter(lazy, "resourceProtocol", () =>
.QueryInterface(Ci.nsIResProtocolHandler)
);
XPCOMUtils.defineLazyGetter(
lazy,
"l10n",
() =>
new Localization(
[
"toolkit/global/extensions.ftl",
"toolkit/global/extensionPermissions.ftl",
"branding/brand.ftl",
],
true
)
);
const { ExtensionCommon } = ChromeUtils.import(
"resource://gre/modules/ExtensionCommon.jsm"
);
@ -2179,30 +2194,29 @@ class ExtensionData {
return { allUrls, wildcards, sites, wildcardsMap, sitesMap };
}
/**
* @typedef {object} Permissions
* @property {Array<string>} origins Origin permissions.
* @property {Array<string>} permissions Regular (non-origin) permissions.
*/
/**
* Formats all the strings for a permissions dialog/notification.
*
* @param {object} info Information about the permissions being requested.
*
* @param {Array<string>} info.permissions.origins
* Origin permissions requested.
* @param {Array<string>} info.permissions.permissions
* Regular (non-origin) permissions requested.
* @param {Array<string>} info.optionalPermissions.origins
* Optional origin permissions listed in the manifest.
* @param {Array<string>} info.optionalPermissions.permissions
* Optional (non-origin) permissions listed in the manifest.
* @param {object} [info.addon] Optional information about the addon.
* @param {Permissions} [info.optionalPermissions]
* Optional permissions listed in the manifest.
* @param {Permissions} info.permissions Requested permissions.
* @param {string} info.siteOrigin
* @param {Array<string>} [info.sitePermissions]
* @param {boolean} info.unsigned
* True if the prompt is for installing an unsigned addon.
* @param {string} info.type
* The type of prompt being shown. May be one of "update",
* "sideload", "optional", or omitted for a regular
* install prompt.
* @param {string} info.appName
* The localized name of the application, to be substituted
* in computed strings as needed.
* @param {nsIStringBundle} bundle
* The string bundle to use for l10n.
* @param {object} options
* @param {boolean} options.collapseOrigins
* Wether to limit the number of displayed host permissions.
@ -2210,13 +2224,8 @@ class ExtensionData {
* @param {boolean} options.buildOptionalOrigins
* Wether to build optional origins Maps for permission
* controls. Defaults to false.
* @param {Function} options.getKeyForPermission
* An optional callback function that returns the locale key for a given
* permission name (set by default to a callback returning the locale
* key following the default convention `webextPerms.description.PERMNAME`).
* Overriding the default mapping can become necessary, when a permission
* description needs to be modified and a non-default locale key has to be
* used. There is at least one non-default locale key used in Thunderbird.
* @param {Localization} options.localization
* Optional custom localization instance.
*
* @returns {object} An object with properties containing localized strings
* for various elements of a permission dialog. The "header"
@ -2234,32 +2243,58 @@ class ExtensionData {
* all url style permissions are included.
*/
static formatPermissionStrings(
info,
bundle,
{
collapseOrigins = false,
buildOptionalOrigins = false,
getKeyForPermission = perm => `webextPerms.description.${perm}`,
} = {}
addon,
optionalPermissions,
permissions,
siteOrigin,
sitePermissions,
type,
unsigned,
},
{ collapseOrigins = false, buildOptionalOrigins = false, localization } = {}
) {
let result = {
const l10n = localization ?? lazy.l10n;
const msgIds = [];
const headerArgs = { extension: "<>" };
let acceptId = "webext-perms-add";
let cancelId = "webext-perms-cancel";
const result = {
msgs: [],
optionalPermissions: {},
optionalOrigins: {},
text: "",
listIntro: "",
};
const haveAccessKeys = AppConstants.platform !== "android";
// To keep the label & accesskey in sync for localizations,
// they need to be stored as attributes of the same Fluent message.
// This unpacks them into the shape expected of them in `result`.
function setAcceptCancel(acceptId, cancelId) {
const haveAccessKeys = AppConstants.platform !== "android";
let headerKey;
result.text = "";
result.listIntro = "";
result.acceptText = bundle.GetStringFromName("webextPerms.add.label");
result.cancelText = bundle.GetStringFromName("webextPerms.cancel.label");
if (haveAccessKeys) {
result.acceptKey = bundle.GetStringFromName("webextPerms.add.accessKey");
result.cancelKey = bundle.GetStringFromName(
"webextPerms.cancel.accessKey"
);
const [accept, cancel] = l10n.formatMessagesSync([
{ id: acceptId },
{ id: cancelId },
]);
for (let { name, value } of accept.attributes) {
if (name === "label") {
result.acceptText = value;
} else if (name === "accesskey" && haveAccessKeys) {
result.acceptKey = value;
}
}
for (let { name, value } of cancel.attributes) {
if (name === "label") {
result.cancelText = value;
} else if (name === "accesskey" && haveAccessKeys) {
result.cancelKey = value;
}
}
}
// Synthetic addon install can only grant access to a single permission so we can have
@ -2267,252 +2302,230 @@ class ExtensionData {
// NOTE: this is used as part of the synthetic addon install flow implemented for the
// SitePermissionAddonProvider.
// (and so it should not be removed as part of Bug 1789718 changes, while this additional note should be).
if (info.addon?.type === lazy.SITEPERMS_ADDON_TYPE) {
// FIXME
if (addon?.type === lazy.SITEPERMS_ADDON_TYPE) {
// We simplify the origin to make it more user friendly. The origin is assured to be
// available because the SitePermsAddon install is always expected to be triggered
// from a website, making the siteOrigin always available through the installing principal.
const host = new URL(info.siteOrigin).hostname;
headerArgs.hostname = new URL(siteOrigin).hostname;
// messages are specific to the type of gated permission being installed
result.header = bundle.formatStringFromName(
`webextSitePerms.headerWithGatedPerms.${info.sitePermissions[0]}`,
[host]
);
const headerId =
sitePermissions[0] === "midi-sysex"
? "webext-site-perms-header-with-gated-perms-midi-sysex"
: "webext-site-perms-header-with-gated-perms-midi";
result.header = l10n.formatValueSync(headerId, headerArgs);
// We use the same string for midi and midi-sysex, and don't support any
// other types of site permission add-ons. So we just hard-code the
// descriptor for now. See bug 1826747.
result.text = bundle.GetStringFromName(
`webextSitePerms.descriptionGatedPerms.midi`
result.text = l10n.formatValueSync(
"webext-site-perms-description-gated-perms-midi"
);
setAcceptCancel(acceptId, cancelId);
return result;
}
// TODO(Bug 1789718): Remove after the deprecated XPIProvider-based implementation is also removed.
if (info.sitePermissions) {
// Generate a map of site_permission names to permission strings for site permissions.
for (let permission of info.sitePermissions) {
try {
result.msgs.push(
bundle.GetStringFromName(
`webextSitePerms.description.${permission}`
)
);
} catch (err) {
Cu.reportError(
`site_permission ${permission} missing readable text property`
);
// We must never have a DOM api permission that is hidden so in
// the case of any error, we'll use the plain permission string.
// test_ext_sitepermissions.js tests for no missing messages, this
// is just an extra fallback.
result.msgs.push(permission);
if (sitePermissions) {
for (let permission of sitePermissions) {
let permMsg;
switch (permission) {
case "midi":
permMsg = l10n.formatValueSync("webext-site-perms-midi");
break;
case "midi-sysex":
permMsg = l10n.formatValueSync("webext-site-perms-midi-sysex");
break;
default:
Cu.reportError(
`site_permission ${permission} missing readable text property`
);
// We must never have a DOM api permission that is hidden so in
// the case of any error, we'll use the plain permission string.
// test_ext_sitepermissions.js tests for no missing messages, this
// is just an extra fallback.
permMsg = permission;
}
result.msgs.push(permMsg);
}
// We simplify the origin to make it more user friendly. The origin is
// assured to be available via schema requirement.
const host = new URL(info.siteOrigin).hostname;
headerKey = info.unsigned
? "webextSitePerms.headerUnsignedWithPerms"
: "webextSitePerms.headerWithPerms";
result.header = bundle.formatStringFromName(headerKey, ["<>", host]);
headerArgs.hostname = new URL(siteOrigin).hostname;
const headerId = unsigned
? "webext-site-perms-header-unsigned-with-perms"
: "webext-site-perms-header-with-perms";
result.header = l10n.formatValueSync(headerId, headerArgs);
setAcceptCancel(acceptId, cancelId);
return result;
}
let perms = info.permissions || { origins: [], permissions: [] };
let optional_permissions = info.optionalPermissions || {
origins: [],
permissions: [],
};
if (permissions) {
// First classify our host permissions
let { allUrls, wildcards, sites } =
ExtensionData.classifyOriginPermissions(permissions.origins);
// First classify our host permissions
let { allUrls, wildcards, sites } = ExtensionData.classifyOriginPermissions(
perms.origins
);
// Format the host permissions. If we have a wildcard for all urls,
// a single string will suffice. Otherwise, show domain wildcards
// first, then individual host permissions.
if (allUrls) {
msgIds.push("webext-perms-host-description-all-urls");
} else {
// Formats a list of host permissions. If we have 4 or fewer, display
// them all, otherwise display the first 3 followed by an item that
// says "...plus N others"
const addMessages = (set, l10nId, moreL10nId) => {
if (collapseOrigins && set.size > 4) {
for (let domain of Array.from(set).slice(0, 3)) {
msgIds.push({ id: l10nId, args: { domain } });
}
msgIds.push({
id: moreL10nId,
args: { domainCount: set.size - 3 },
});
} else {
for (let domain of set) {
msgIds.push({ id: l10nId, args: { domain } });
}
}
};
// Format the host permissions. If we have a wildcard for all urls,
// a single string will suffice. Otherwise, show domain wildcards
// first, then individual host permissions.
if (allUrls) {
result.msgs.push(
bundle.GetStringFromName("webextPerms.hostDescription.allUrls")
);
} else {
// Formats a list of host permissions. If we have 4 or fewer, display
// them all, otherwise display the first 3 followed by an item that
// says "...plus N others"
let format = (list, itemKey, moreKey) => {
function formatItems(items) {
result.msgs.push(
...items.map(item => bundle.formatStringFromName(itemKey, [item]))
);
}
if (list.length < 5 || !collapseOrigins) {
formatItems(list);
} else {
formatItems(list.slice(0, 3));
let remaining = list.length - 3;
result.msgs.push(
lazy.PluralForm.get(
remaining,
bundle.GetStringFromName(moreKey)
).replace("#1", remaining)
);
}
};
format(
Array.from(wildcards),
"webextPerms.hostDescription.wildcard",
"webextPerms.hostDescription.tooManyWildcards"
);
format(
Array.from(sites),
"webextPerms.hostDescription.oneSite",
"webextPerms.hostDescription.tooManySites"
);
}
// Next, show the native messaging permission if it is present.
const NATIVE_MSG_PERM = "nativeMessaging";
if (perms.permissions.includes(NATIVE_MSG_PERM)) {
result.msgs.push(
bundle.formatStringFromName(getKeyForPermission(NATIVE_MSG_PERM), [
info.appName,
])
);
}
// Finally, show remaining permissions, in the same order as AMO.
// The permissions are sorted alphabetically by the permission
// string to match AMO.
let permissionsCopy = perms.permissions.slice(0);
for (let permission of permissionsCopy.sort()) {
// Handled above
if (permission == NATIVE_MSG_PERM) {
continue;
}
try {
result.msgs.push(
bundle.GetStringFromName(getKeyForPermission(permission))
addMessages(
wildcards,
"webext-perms-host-description-wildcard",
"webext-perms-host-description-too-many-wildcards"
);
} catch (err) {
addMessages(
sites,
"webext-perms-host-description-one-site",
"webext-perms-host-description-too-many-sites"
);
}
// Finally, show remaining permissions, in the same order as AMO.
// The permissions are sorted alphabetically by the permission
// string to match AMO.
// Show the native messaging permission first if it is present.
const NATIVE_MSG_PERM = "nativeMessaging";
const permissionsSorted = permissions.permissions.sort((a, b) => {
if (a === NATIVE_MSG_PERM) {
return -1;
} else if (b === NATIVE_MSG_PERM) {
return 1;
}
return a < b ? -1 : 1;
});
for (let permission of permissionsSorted) {
const l10nId = lazy.permissionToL10nId(permission);
// We deliberately do not include all permissions in the prompt.
// So if we don't find one then just skip it.
if (l10nId) {
msgIds.push(l10nId);
}
}
}
// Generate a map of permission names to permission strings for optional
// permissions. The key is necessary to handle toggling those permissions.
for (let permission of optional_permissions.permissions) {
if (permission == NATIVE_MSG_PERM) {
result.optionalPermissions[permission] = bundle.formatStringFromName(
getKeyForPermission(permission),
[info.appName]
);
continue;
}
try {
result.optionalPermissions[permission] = bundle.GetStringFromName(
getKeyForPermission(permission)
);
} catch (err) {
// We deliberately do not have strings for all permissions.
if (optionalPermissions) {
// Generate a map of permission names to permission strings for optional
// permissions. The key is necessary to handle toggling those permissions.
const opKeys = [];
const opL10nIds = [];
for (let permission of optionalPermissions.permissions) {
const l10nId = lazy.permissionToL10nId(permission);
// We deliberately do not include all permissions in the prompt.
// So if we don't find one then just skip it.
if (l10nId) {
opKeys.push(permission);
opL10nIds.push(l10nId);
}
}
if (opKeys.length) {
const opRes = l10n.formatValuesSync(opL10nIds);
for (let i = 0; i < opKeys.length; ++i) {
result.optionalPermissions[opKeys[i]] = opRes[i];
}
}
const { allUrls, sitesMap, wildcardsMap } =
ExtensionData.classifyOriginPermissions(
optionalPermissions.origins,
true
);
const ooKeys = [];
const ooL10nIds = [];
if (allUrls) {
ooKeys.push(allUrls);
ooL10nIds.push("webext-perms-host-description-all-urls");
}
// Current UX controls are meant for developer testing with mv3.
if (buildOptionalOrigins) {
for (let [pattern, domain] of wildcardsMap.entries()) {
ooKeys.push(pattern);
ooL10nIds.push({
id: "webext-perms-host-description-wildcard",
args: { domain },
});
}
for (let [pattern, domain] of sitesMap.entries()) {
ooKeys.push(pattern);
ooL10nIds.push({
id: "webext-perms-host-description-one-site",
args: { domain },
});
}
}
if (ooKeys.length) {
const res = l10n.formatValuesSync(ooL10nIds);
for (let i = 0; i < res.length; ++i) {
result.optionalOrigins[ooKeys[i]] = res[i];
}
}
}
let optionalInfo = ExtensionData.classifyOriginPermissions(
optional_permissions.origins,
true
);
if (optionalInfo.allUrls) {
result.optionalOrigins[optionalInfo.allUrls] = bundle.GetStringFromName(
"webextPerms.hostDescription.allUrls"
);
let headerId;
switch (type) {
case "sideload":
headerId = "webext-perms-sideload-header";
acceptId = "webext-perms-sideload-enable";
cancelId = "webext-perms-sideload-cancel";
result.text = l10n.formatValueSync(
msgIds.length
? "webext-perms-sideload-text"
: "webext-perms-sideload-text-no-perms"
);
break;
case "update":
headerId = "webext-perms-update-text";
acceptId = "webext-perms-update-accept";
break;
case "optional":
headerId = "webext-perms-optional-perms-header";
acceptId = "webext-perms-optional-perms-allow";
cancelId = "webext-perms-optional-perms-deny";
result.listIntro = l10n.formatValueSync(
"webext-perms-optional-perms-list-intro"
);
break;
default:
if (msgIds.length) {
headerId = unsigned
? "webext-perms-header-unsigned-with-perms"
: "webext-perms-header-with-perms";
} else {
headerId = unsigned
? "webext-perms-header-unsigned"
: "webext-perms-header";
}
}
// Current UX controls are meant for developer testing with mv3.
if (buildOptionalOrigins) {
for (let [pattern, originLabel] of optionalInfo.wildcardsMap.entries()) {
let key = "webextPerms.hostDescription.wildcard";
let str = bundle.formatStringFromName(key, [originLabel]);
result.optionalOrigins[pattern] = str;
}
for (let [pattern, originLabel] of optionalInfo.sitesMap.entries()) {
let key = "webextPerms.hostDescription.oneSite";
let str = bundle.formatStringFromName(key, [originLabel]);
result.optionalOrigins[pattern] = str;
}
}
if (info.type == "sideload") {
headerKey = "webextPerms.sideloadHeader";
let key = !result.msgs.length
? "webextPerms.sideloadTextNoPerms"
: "webextPerms.sideloadText2";
result.text = bundle.GetStringFromName(key);
result.acceptText = bundle.GetStringFromName(
"webextPerms.sideloadEnable.label"
);
result.cancelText = bundle.GetStringFromName(
"webextPerms.sideloadCancel.label"
);
if (haveAccessKeys) {
result.acceptKey = bundle.GetStringFromName(
"webextPerms.sideloadEnable.accessKey"
);
result.cancelKey = bundle.GetStringFromName(
"webextPerms.sideloadCancel.accessKey"
);
}
} else if (info.type == "update") {
headerKey = "webextPerms.updateText2";
result.text = "";
result.acceptText = bundle.GetStringFromName(
"webextPerms.updateAccept.label"
);
if (haveAccessKeys) {
result.acceptKey = bundle.GetStringFromName(
"webextPerms.updateAccept.accessKey"
);
}
} else if (info.type == "optional") {
headerKey = "webextPerms.optionalPermsHeader";
result.text = "";
result.listIntro = bundle.GetStringFromName(
"webextPerms.optionalPermsListIntro"
);
result.acceptText = bundle.GetStringFromName(
"webextPerms.optionalPermsAllow.label"
);
result.cancelText = bundle.GetStringFromName(
"webextPerms.optionalPermsDeny.label"
);
if (haveAccessKeys) {
result.acceptKey = bundle.GetStringFromName(
"webextPerms.optionalPermsAllow.accessKey"
);
result.cancelKey = bundle.GetStringFromName(
"webextPerms.optionalPermsDeny.accessKey"
);
}
} else {
headerKey = "webextPerms.header";
if (result.msgs.length) {
headerKey = info.unsigned
? "webextPerms.headerUnsignedWithPerms"
: "webextPerms.headerWithPerms";
} else if (info.unsigned) {
headerKey = "webextPerms.headerUnsigned";
}
}
result.header = bundle.formatStringFromName(headerKey, ["<>"]);
result.header = l10n.formatValueSync(headerId, headerArgs);
result.msgs = l10n.formatValuesSync(msgIds);
setAcceptCancel(acceptId, cancelId);
return result;
}
}

Просмотреть файл

@ -0,0 +1,79 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* List of permissions that are associated with a permission message.
*
* Keep this list in sync with:
* - The messages in `toolkit/locales/en-US/toolkit/global/extensionPermissions.ftl`
* - `permissionToTranslation` at https://github.com/mozilla-mobile/firefox-android/blob/d9c08c387917e3e53963386ad53229e69d52da6e/android-components/components/feature/addons/src/main/java/mozilla/components/feature/addons/Addon.kt#L174-L206
* - https://extensionworkshop.com/documentation/develop/request-the-right-permissions/#advised-permissions
* - https://support.mozilla.org/en-US/kb/permission-request-messages-firefox-extensions
*
* This is exported to allow builds (e.g. Thunderbird) to extend or modify the set.
*/
export const PERMISSIONS_WITH_MESSAGE = new Set([
"bookmarks",
"browserSettings",
"browsingData",
"clipboardRead",
"clipboardWrite",
"declarativeNetRequest",
"declarativeNetRequestFeedback",
"devtools",
"downloads",
"downloads.open",
"find",
"geolocation",
"history",
"management",
"nativeMessaging",
"notifications",
"pkcs11",
"privacy",
"proxy",
"sessions",
"tabs",
"tabHide",
"topSites",
"webNavigation",
]);
/**
* Overrides for permission description l10n identifiers,
* which by default use the pattern `webext-perms-description-${permission}`
* where `permission` is sanitized to be a valid Fluent identifier.
*
* This is exported to allow builds (e.g. Thunderbird) to extend or modify the map.
*/
export const PERMISSION_L10N_ID_OVERRIDES = new Map();
/**
* Maps a permission name to its l10n identifier.
*
* Returns `null` for permissions not in `PERMISSIONS_WITH_MESSAGE`.
*
* The default `webext-perms-description-${permission}` mapping
* may be overridden by entries in `PERMISSION_L10N_ID_OVERRIDES`.
*
* @param {string} permission
* @returns {string | null}
*/
export function permissionToL10nId(permission) {
if (!PERMISSIONS_WITH_MESSAGE.has(permission)) {
return null;
}
if (PERMISSION_L10N_ID_OVERRIDES.has(permission)) {
return PERMISSION_L10N_ID_OVERRIDES.get(permission);
}
// Sanitize input to end up with a valid l10n id.
// E.g. "<all_urls>" to "all-urls", "downloads.open" to "downloads-open".
const sanitized = permission
.replace(/[^a-zA-Z0-9]+/g, "-")
.replace(/^-|-$/g, "");
return `webext-perms-description-${sanitized}`;
}

Просмотреть файл

@ -23,6 +23,7 @@ EXTRA_JS_MODULES += [
"ExtensionDNRStore.sys.mjs",
"ExtensionPageChild.jsm",
"ExtensionParent.jsm",
"ExtensionPermissionMessages.sys.mjs",
"ExtensionPermissions.jsm",
"ExtensionPreferencesManager.jsm",
"ExtensionProcessScript.jsm",

Просмотреть файл

@ -4,19 +4,26 @@ let { ExtensionTestCommon } = ChromeUtils.import(
"resource://testing-common/ExtensionTestCommon.jsm"
);
let bundle;
if (AppConstants.MOZ_APP_NAME == "thunderbird") {
bundle = Services.strings.createBundle(
"chrome://messenger/locale/addons.properties"
);
} else {
// For Android, these strings are only used in tests. In the actual UI, the
// warnings are in Android-Components, as explained in bug 1671453.
bundle = Services.strings.createBundle(
"chrome://browser/locale/browser.properties"
);
}
const DUMMY_APP_NAME = "Dummy brandName";
const { PERMISSION_L10N_ID_OVERRIDES } = ChromeUtils.importESModule(
"resource://gre/modules/ExtensionPermissionMessages.sys.mjs"
);
const EXTENSION_L10N_PATHS =
AppConstants.MOZ_APP_NAME == "thunderbird"
? [
"messenger/addons.ftl", // FIXME: mock path, file does not exist
"messenger/addonPermissions.ftl", // FIXME: mock path, file does not exist
"branding/brand.ftl",
]
: [
"toolkit/global/extensions.ftl",
"toolkit/global/extensionPermissions.ftl",
"branding/brand.ftl",
];
// For Android, these strings are only used in tests. In the actual UI, the
// warnings are in Android-Components, as explained in bug 1671453.
const l10n = new Localization(EXTENSION_L10N_PATHS, true);
// nativeMessaging is in PRIVILEGED_PERMS on Android.
const IS_NATIVE_MESSAGING_PRIVILEGED = AppConstants.platform == "android";
@ -50,18 +57,9 @@ async function getManifestPermissions(extensionData) {
return result;
}
function getPermissionWarnings(
manifestPermissions,
options,
stringBundle = bundle
) {
let info = {
permissions: manifestPermissions,
appName: DUMMY_APP_NAME,
};
function getPermissionWarnings(permissions, options) {
let { msgs } = ExtensionData.formatPermissionStrings(
info,
stringBundle,
{ permissions },
options
);
return msgs;
@ -80,44 +78,53 @@ async function getPermissionWarningsForUpdate(
// Tests that the callers of ExtensionData.formatPermissionStrings can customize the
// mapping between the permission names and related localized strings.
add_task(async function customized_permission_keys_mapping() {
const mockBundle = {
// Mocked nsIStringBundle getStringFromName to returns a fake localized string.
GetStringFromName: key => `Fake localized ${key}`,
formatStringFromName: (name, params) => "Fake formatted string",
const mockLocalization = {
formatMessagesSync: args =>
args.map(arg => ({
value: `Fake localized ${arg.id ?? arg}`,
attributes: [],
})),
formatValueSync: key => `Fake localized ${key}`,
formatValuesSync: args =>
args.map(arg => `Fake localized ${arg.id ?? arg}`),
};
// Define a non-default mapping for permission names -> locale keys.
const getKeyForPermission = perm => `customWebExtPerms.description.${perm}`;
const getKeyForPermission = perm => `custom-webext-perms-description-${perm}`;
const manifest = {
permissions: ["downloads", "proxy"],
};
const expectedWarnings = manifest.permissions.map(k =>
mockBundle.GetStringFromName(getKeyForPermission(k))
const expectedWarnings = mockLocalization.formatValuesSync(
manifest.permissions.map(getKeyForPermission)
);
const manifestPermissions = await getManifestPermissions({ manifest });
// Pass the callback function for the non-default key mapping to
// ExtensionData.formatPermissionStrings() and verify it being used.
const warnings = getPermissionWarnings(
manifestPermissions,
{ getKeyForPermission },
mockBundle
);
deepEqual(
warnings,
expectedWarnings,
"Got the expected string from customized permission mapping"
);
try {
for (let perm of manifest.permissions) {
PERMISSION_L10N_ID_OVERRIDES.set(perm, getKeyForPermission(perm));
}
const manifestPermissions = await getManifestPermissions({ manifest });
// Pass the callback function for the non-default key mapping to
// ExtensionData.formatPermissionStrings() and verify it being used.
const warnings = getPermissionWarnings(manifestPermissions, {
localization: mockLocalization,
});
deepEqual(
warnings,
expectedWarnings,
"Got the expected string from customized permission mapping"
);
} finally {
for (let perm of manifest.permissions) {
PERMISSION_L10N_ID_OVERRIDES.delete(perm);
}
}
});
// Tests that the expected permission warnings are generated for various
// combinations of host permissions.
add_task(async function host_permissions() {
let { PluralForm } = ChromeUtils.import(
"resource://gre/modules/PluralForm.jsm"
);
let permissionTestCases = [
{
description: "Empty manifest without permissions",
@ -167,7 +174,7 @@ add_task(async function host_permissions() {
},
expectedOrigins: ["<all_urls>"],
expectedWarnings: [
bundle.GetStringFromName("webextPerms.hostDescription.allUrls"),
l10n.formatValueSync("webext-perms-host-description-all-urls"),
],
},
{
@ -177,7 +184,7 @@ add_task(async function host_permissions() {
},
expectedOrigins: ["file://*/"],
expectedWarnings: [
bundle.GetStringFromName("webextPerms.hostDescription.allUrls"),
l10n.formatValueSync("webext-perms-host-description-all-urls"),
],
},
{
@ -187,7 +194,7 @@ add_task(async function host_permissions() {
},
expectedOrigins: ["http://*/"],
expectedWarnings: [
bundle.GetStringFromName("webextPerms.hostDescription.allUrls"),
l10n.formatValueSync("webext-perms-host-description-all-urls"),
],
},
{
@ -197,7 +204,7 @@ add_task(async function host_permissions() {
},
expectedOrigins: ["*://*/"],
expectedWarnings: [
bundle.GetStringFromName("webextPerms.hostDescription.allUrls"),
l10n.formatValueSync("webext-perms-host-description-all-urls"),
],
},
{
@ -214,7 +221,7 @@ add_task(async function host_permissions() {
},
expectedOrigins: ["https://*/"],
expectedWarnings: [
bundle.GetStringFromName("webextPerms.hostDescription.allUrls"),
l10n.formatValueSync("webext-perms-host-description-all-urls"),
],
},
{
@ -223,18 +230,21 @@ add_task(async function host_permissions() {
permissions: ["http://a/", "http://*.b/", "http://c/*"],
},
expectedOrigins: ["http://a/", "http://*.b/", "http://c/*"],
expectedWarnings: [
expectedWarnings: l10n.formatValuesSync([
// Wildcard hosts take precedence in the permission list.
bundle.formatStringFromName("webextPerms.hostDescription.wildcard", [
"b",
]),
bundle.formatStringFromName("webextPerms.hostDescription.oneSite", [
"a",
]),
bundle.formatStringFromName("webextPerms.hostDescription.oneSite", [
"c",
]),
],
{
id: "webext-perms-host-description-wildcard",
args: { domain: "b" },
},
{
id: "webext-perms-host-description-one-site",
args: { domain: "a" },
},
{
id: "webext-perms-host-description-one-site",
args: { domain: "c" },
},
]),
},
{
description: "many host permission",
@ -262,34 +272,41 @@ add_task(async function host_permissions() {
"http://*.3/",
"http://*.4/",
],
expectedWarnings: [
expectedWarnings: l10n.formatValuesSync([
// Wildcard hosts take precedence in the permission list.
bundle.formatStringFromName("webextPerms.hostDescription.wildcard", [
"1",
]),
bundle.formatStringFromName("webextPerms.hostDescription.wildcard", [
"2",
]),
bundle.formatStringFromName("webextPerms.hostDescription.wildcard", [
"3",
]),
bundle.formatStringFromName("webextPerms.hostDescription.wildcard", [
"4",
]),
bundle.formatStringFromName("webextPerms.hostDescription.oneSite", [
"a",
]),
bundle.formatStringFromName("webextPerms.hostDescription.oneSite", [
"b",
]),
bundle.formatStringFromName("webextPerms.hostDescription.oneSite", [
"c",
]),
PluralForm.get(
2,
bundle.GetStringFromName("webextPerms.hostDescription.tooManySites")
).replace("#1", "2"),
],
{
id: "webext-perms-host-description-wildcard",
args: { domain: "1" },
},
{
id: "webext-perms-host-description-wildcard",
args: { domain: "2" },
},
{
id: "webext-perms-host-description-wildcard",
args: { domain: "3" },
},
{
id: "webext-perms-host-description-wildcard",
args: { domain: "4" },
},
{
id: "webext-perms-host-description-one-site",
args: { domain: "a" },
},
{
id: "webext-perms-host-description-one-site",
args: { domain: "b" },
},
{
id: "webext-perms-host-description-one-site",
args: { domain: "c" },
},
{
id: "webext-perms-host-description-too-many-sites",
args: { domainCount: 2 },
},
]),
options: {
collapseOrigins: true,
},
@ -323,38 +340,18 @@ add_task(async function host_permissions() {
"http://*.4/",
"http://*.5/",
],
expectedWarnings: [
bundle.formatStringFromName("webextPerms.hostDescription.wildcard", [
"1",
]),
bundle.formatStringFromName("webextPerms.hostDescription.wildcard", [
"2",
]),
bundle.formatStringFromName("webextPerms.hostDescription.wildcard", [
"3",
]),
bundle.formatStringFromName("webextPerms.hostDescription.wildcard", [
"4",
]),
bundle.formatStringFromName("webextPerms.hostDescription.wildcard", [
"5",
]),
bundle.formatStringFromName("webextPerms.hostDescription.oneSite", [
"a",
]),
bundle.formatStringFromName("webextPerms.hostDescription.oneSite", [
"b",
]),
bundle.formatStringFromName("webextPerms.hostDescription.oneSite", [
"c",
]),
bundle.formatStringFromName("webextPerms.hostDescription.oneSite", [
"d",
]),
bundle.formatStringFromName("webextPerms.hostDescription.oneSite", [
"e",
]),
],
expectedWarnings: l10n.formatValuesSync([
{ id: "webext-perms-host-description-wildcard", args: { domain: "1" } },
{ id: "webext-perms-host-description-wildcard", args: { domain: "2" } },
{ id: "webext-perms-host-description-wildcard", args: { domain: "3" } },
{ id: "webext-perms-host-description-wildcard", args: { domain: "4" } },
{ id: "webext-perms-host-description-wildcard", args: { domain: "5" } },
{ id: "webext-perms-host-description-one-site", args: { domain: "a" } },
{ id: "webext-perms-host-description-one-site", args: { domain: "b" } },
{ id: "webext-perms-host-description-one-site", args: { domain: "c" } },
{ id: "webext-perms-host-description-one-site", args: { domain: "d" } },
{ id: "webext-perms-host-description-one-site", args: { domain: "e" } },
]),
},
];
for (let manifest_version of [2, 3]) {
@ -423,24 +420,18 @@ add_task(async function api_permissions() {
deepEqual(
getPermissionWarnings(manifestPermissions),
[
l10n.formatValuesSync([
// Host permissions first, with wildcards on top.
bundle.formatStringFromName("webextPerms.hostDescription.wildcard", [
"x",
]),
bundle.formatStringFromName("webextPerms.hostDescription.wildcard", [
"tld",
]),
bundle.formatStringFromName("webextPerms.hostDescription.oneSite", ["x"]),
{ id: "webext-perms-host-description-wildcard", args: { domain: "x" } },
{ id: "webext-perms-host-description-wildcard", args: { domain: "tld" } },
{ id: "webext-perms-host-description-one-site", args: { domain: "x" } },
// nativeMessaging permission warning first of all permissions.
bundle.formatStringFromName("webextPerms.description.nativeMessaging", [
DUMMY_APP_NAME,
]),
"webext-perms-description-nativeMessaging",
// Other permissions in alphabetical order.
// Note: activeTab has no permission warning string.
bundle.GetStringFromName("webextPerms.description.tabs"),
bundle.GetStringFromName("webextPerms.description.webNavigation"),
],
"webext-perms-description-tabs",
"webext-perms-description-webNavigation",
]),
"Expected warnings"
);
});
@ -492,14 +483,10 @@ add_task(
deepEqual(
getPermissionWarnings(manifestPermissions),
[
bundle.GetStringFromName(
"webextPerms.description.declarativeNetRequest"
),
bundle.GetStringFromName(
"webextPerms.description.declarativeNetRequestFeedback"
),
],
l10n.formatValuesSync([
"webext-perms-description-declarativeNetRequest",
"webext-perms-description-declarativeNetRequestFeedback",
]),
"Expected warnings"
);
}
@ -553,7 +540,7 @@ add_task(async function privileged_with_mozillaAddons() {
deepEqual(
getPermissionWarnings(manifestPermissions),
[bundle.GetStringFromName("webextPerms.hostDescription.allUrls")],
[l10n.formatValueSync("webext-perms-host-description-all-urls")],
"Expected warnings for privileged add-on with mozillaAddons permission."
);
});
@ -584,7 +571,11 @@ add_task(async function unprivileged_with_mozillaAddons() {
deepEqual(
getPermissionWarnings(manifestPermissions),
[bundle.formatStringFromName("webextPerms.hostDescription.oneSite", ["a"])],
[
l10n.formatValueSync("webext-perms-host-description-one-site", {
domain: "a",
}),
],
"Expected warnings for unprivileged add-on with mozillaAddons permission."
);
});
@ -671,14 +662,10 @@ add_task(async function update_change_permissions() {
);
deepEqual(
warnings,
[
bundle.formatStringFromName("webextPerms.hostDescription.wildcard", [
"c",
]),
bundle.formatStringFromName("webextPerms.description.proxy", [
DUMMY_APP_NAME,
]),
],
l10n.formatValuesSync([
{ id: "webext-perms-host-description-wildcard", args: { domain: "c" } },
"webext-perms-description-proxy",
]),
"Expected permission warnings for new permissions only"
);
});
@ -702,7 +689,11 @@ add_task(async function update_privileged_with_mozillaAddons() {
);
deepEqual(
warnings,
[bundle.formatStringFromName("webextPerms.hostDescription.oneSite", ["b"])],
[
l10n.formatValueSync("webext-perms-host-description-one-site", {
domain: "b",
}),
],
"Expected permission warnings for new host only"
);
});

Просмотреть файл

@ -20,7 +20,13 @@ ChromeUtils.defineModuleGetter(
"resource://gre/modules/ExtensionParent.jsm"
);
const BROWSER_PROPERTIES = "chrome://browser/locale/browser.properties";
const l10n = new Localization([
"toolkit/global/extensions.ftl",
"toolkit/global/extensionPermissions.ftl",
"branding/brand.ftl",
]);
// Localization resources need to be first iterated outside a test
l10n.formatValue("webext-perms-add");
AddonTestUtils.init(this);
AddonTestUtils.overrideCertDB();
@ -677,7 +683,7 @@ const GRANTED_WITHOUT_USER_PROMPT = [
"webRequestFilterResponse.serviceWorkerScript",
];
add_task(function test_permissions_have_localization_strings() {
add_task(async function test_permissions_have_localization_strings() {
let noPromptNames = Schemas.getPermissionNames([
"PermissionNoPrompt",
"OptionalPermissionNoPrompt",
@ -689,11 +695,10 @@ add_task(function test_permissions_have_localization_strings() {
"List of no-prompt permissions is correct."
);
const bundle = Services.strings.createBundle(BROWSER_PROPERTIES);
for (const perm of Schemas.getPermissionNames()) {
try {
const str = bundle.GetStringFromName(`webextPerms.description.${perm}`);
const permId = perm.replace(/\./g, "-");
const str = await l10n.formatValue(`webext-perms-description-${permId}`);
ok(str.length, `Found localization string for '${perm}' permission`);
} catch (e) {

Просмотреть файл

@ -28,10 +28,13 @@ AddonTestUtils.createAppInfo(
"42"
);
const BROWSER_PROPERTIES =
AppConstants.MOZ_APP_NAME == "thunderbird"
? "chrome://messenger/locale/addons.properties"
: "chrome://browser/locale/browser.properties";
const l10n = new Localization([
"toolkit/global/extensions.ftl",
"toolkit/global/extensionPermissions.ftl",
"branding/brand.ftl",
]);
// Localization resources need to be first iterated outside a test
l10n.formatValue("webext-perms-add");
// Lazily import ExtensionParent to allow AddonTestUtils.createAppInfo to
// override Services.appinfo.
@ -371,13 +374,10 @@ add_task(async function test_site_permissions_have_localization_strings() {
]);
ok(SCHEMA_SITE_PERMISSIONS.length, "we have site permissions");
const bundle = Services.strings.createBundle(BROWSER_PROPERTIES);
for (const perm of SCHEMA_SITE_PERMISSIONS) {
const l10nId = `webext-site-perms-${perm}`;
try {
const str = bundle.GetStringFromName(
`webextSitePerms.description.${perm}`
);
const str = await l10n.formatValue(l10nId);
ok(str.length, `Found localization string for '${perm}' site permission`);
} catch (e) {

Просмотреть файл

@ -0,0 +1,32 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
## Extension permission description keys are derived from permission names.
## Permissions for which the message has been changed and the key updated
## must have a corresponding entry in the `PERMISSION_L10N_ID_OVERRIDES` map.
webext-perms-description-bookmarks = Read and modify bookmarks
webext-perms-description-browserSettings = Read and modify browser settings
webext-perms-description-browsingData = Clear recent browsing history, cookies, and related data
webext-perms-description-clipboardRead = Get data from the clipboard
webext-perms-description-clipboardWrite = Input data to the clipboard
webext-perms-description-declarativeNetRequest = Block content on any page
webext-perms-description-declarativeNetRequestFeedback = Read your browsing history
webext-perms-description-devtools = Extend developer tools to access your data in open tabs
webext-perms-description-downloads = Download files and read and modify the browsers download history
webext-perms-description-downloads-open = Open files downloaded to your computer
webext-perms-description-find = Read the text of all open tabs
webext-perms-description-geolocation = Access your location
webext-perms-description-history = Access browsing history
webext-perms-description-management = Monitor extension usage and manage themes
webext-perms-description-nativeMessaging = Exchange messages with programs other than { -brand-short-name }
webext-perms-description-notifications = Display notifications to you
webext-perms-description-pkcs11 = Provide cryptographic authentication services
webext-perms-description-privacy = Read and modify privacy settings
webext-perms-description-proxy = Control browser proxy settings
webext-perms-description-sessions = Access recently closed tabs
webext-perms-description-tabs = Access browser tabs
webext-perms-description-tabHide = Hide and show browser tabs
webext-perms-description-topSites = Access browsing history
webext-perms-description-webNavigation = Access browser activity during navigation

Просмотреть файл

@ -0,0 +1,111 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
## Headers used in the webextension permissions dialog,
## 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 <, >, &
## Variables:
## $extension (String): replaced with the localized name of the extension.
webext-perms-header = Add { $extension }?
webext-perms-header-with-perms = Add { $extension }? This extension will have permission to:
webext-perms-header-unsigned = Add { $extension }? This extension is unverified. Malicious extensions can steal your private information or compromise your computer. Only add it if you trust the source.
webext-perms-header-unsigned-with-perms = Add { $extension }? 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:
webext-perms-sideload-header = { $extension } added
webext-perms-optional-perms-header = { $extension } requests additional permissions.
##
webext-perms-add =
.label = Add
.accesskey = A
webext-perms-cancel =
.label = Cancel
.accesskey = C
webext-perms-sideload-text = Another program on your computer installed an add-on that may affect your browser. Please review this add-ons permissions requests and choose to Enable or Cancel (to leave it disabled).
webext-perms-sideload-text-no-perms = Another program on your computer installed an add-on that may affect your browser. Please choose to Enable or Cancel (to leave it disabled).
webext-perms-sideload-enable =
.label = Enable
.accesskey = E
webext-perms-sideload-cancel =
.label = Cancel
.accesskey = C
# Variables:
# $extension (String): replaced with the localized name of the extension.
webext-perms-update-text = { $extension } 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:
webext-perms-update-accept =
.label = Update
.accesskey = U
webext-perms-optional-perms-list-intro = It wants to:
webext-perms-optional-perms-allow =
.label = Allow
.accesskey = A
webext-perms-optional-perms-deny =
.label = Deny
.accesskey = D
webext-perms-host-description-all-urls = Access your data for all websites
# Variables:
# $domain (String): will be replaced by the DNS domain for which a webextension is requesting access (e.g., mozilla.org)
webext-perms-host-description-wildcard = Access your data for sites in the { $domain } domain
# Variables:
# $domainCount (Number): Integer indicating the number of additional
# hosts for which this webextension is requesting permission.
webext-perms-host-description-too-many-wildcards =
{ $domainCount ->
[one] Access your data in { $domainCount } other domain
*[other] Access your data in { $domainCount } other domains
}
# Variables:
# $domain (String): will be replaced by the DNS host name for which a webextension is requesting access (e.g., www.mozilla.org)
webext-perms-host-description-one-site = Access your data for { $domain }
# Variables:
# $domainCount (Number): Integer indicating the number of additional
# hosts for which this webextension is requesting permission.
webext-perms-host-description-too-many-sites =
{ $domainCount ->
[one] Access your data on { $domainCount } other site
*[other] Access your data on { $domainCount } other sites
}
## Headers used 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 webext-site-perms-description-gated-perms-{sitePermission}.
## Note, this string will be used as raw markup. Avoid characters like <, >, &
## Variables:
## $hostname (String): the hostname of the site the add-on is being installed from.
webext-site-perms-header-with-gated-perms-midi = This add-on gives { $hostname } access to your MIDI devices.
webext-site-perms-header-with-gated-perms-midi-sysex = This add-on gives { $hostname } access to your MIDI devices (with SysEx support).
##
# This string is used as description in the webextension permissions dialog for synthetic add-ons.
# Note, the empty line is used to create a line break between the two sections.
# Note, this string will be used as raw markup. Avoid characters like <, >, &
webext-site-perms-description-gated-perms-midi =
These are usually plug-in devices like audio synthesizers, but might also be built into your computer.
Websites are normally not allowed to access MIDI devices. Improper usage could cause damage or compromise security.
## Headers used in the webextension permissions dialog.
## Note: This string will be used as raw markup. Avoid characters like <, >, &
## Variables:
## $extension (String): replaced with the localized name of the extension being installed.
## $hostname (String): will be replaced by the DNS host name for which a webextension enables permissions.
webext-site-perms-header-with-perms = Add { $extension }? This extension grants the following capabilities to { $hostname }:
webext-site-perms-header-unsigned-with-perms = Add { $extension }? 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 { $hostname }:
## These should remain in sync with permissions.NAME.label in sitePermissions.properties
webext-site-perms-midi = Access MIDI devices
webext-site-perms-midi-sysex = Access MIDI devices with SysEx support

Просмотреть файл

@ -25,16 +25,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
ExtensionPermissions: "resource://gre/modules/ExtensionPermissions.jsm",
});
XPCOMUtils.defineLazyGetter(this, "browserBundle", () => {
return Services.strings.createBundle(
"chrome://browser/locale/browser.properties"
);
});
XPCOMUtils.defineLazyGetter(this, "brandBundle", () => {
return Services.strings.createBundle(
"chrome://branding/locale/brand.properties"
);
});
XPCOMUtils.defineLazyGetter(this, "extensionStylesheets", () => {
const { ExtensionParent } = ChromeUtils.import(
"resource://gre/modules/ExtensionParent.jsm"
@ -1945,8 +1935,6 @@ class AddonPermissionsList extends HTMLElement {
}
async render() {
let appName = brandBundle.GetStringFromName("brandShortName");
let empty = { origins: [], permissions: [] };
let requiredPerms = { ...(this.addon.userPermissions ?? empty) };
let optionalPerms = { ...(this.addon.optionalPermissions ?? empty) };
@ -1966,9 +1954,7 @@ class AddonPermissionsList extends HTMLElement {
{
permissions: requiredPerms,
optionalPermissions: optionalPerms,
appName,
},
browserBundle,
{ buildOptionalOrigins: manifestV3enabled }
);
let optionalEntries = [
@ -2049,15 +2035,10 @@ class AddonSitePermissionsList extends HTMLElement {
}
async render() {
let appName = brandBundle.GetStringFromName("brandShortName");
let permissions = Extension.formatPermissionStrings(
{
sitePermissions: this.addon.sitePermissions,
siteOrigin: this.addon.siteOrigin,
appName,
},
browserBundle
);
let permissions = Extension.formatPermissionStrings({
sitePermissions: this.addon.sitePermissions,
siteOrigin: this.addon.siteOrigin,
});
this.textContent = "";
let frag = importTemplate("addon-sitepermissions-list");

Просмотреть файл

@ -82,6 +82,9 @@ ID01:
# policies-descriptions.ftl: These IDs are generated programmatically
# from policy names.
- browser/locales/en-US/browser/policies/policies-descriptions.ftl
# The webext-perms-description-* IDs are generated programmatically
# from permission names
- toolkit/locales/en-US/toolkit/global/extensionPermissions.ftl
ID02:
messages:
# browser/components/ion/content/ion.ftl
@ -185,6 +188,8 @@ CO01:
- about-telemetry-firefox-data-doc
- about-telemetry-telemetry-client-doc
- about-telemetry-telemetry-dashboard
# toolkit/locales/en-US/toolkit/global/extensionPermissions.ftl
- webext-perms-description-management
# toolkit/locales/en-US/toolkit/global/processTypes.ftl
- process-type-privilegedmozilla
files: