Bug 1717201 - Add optional callback parameter to map permission names to permission description keys. r=rpl
authorJohn Bieling <john@thunderbird.net>
Tue, 22 Jun 2021 16:56:13 +0000
changeset 584029 536a892dd51f394af6c0638166e8cb7e467ae88c
parent 584028 9fd017a7234e622227e83c7ab27ecc934924cc03
child 584030 97ce4ca4f50b990efec64fd0e76645f9ca46df9c
push id38557
push usermalexandru@mozilla.com
push dateTue, 22 Jun 2021 21:29:07 +0000
treeherdermozilla-central@536a892dd51f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrpl
bugs1717201
milestone91.0a1
first release with
nightly linux32
536a892dd51f / 91.0a1 / 20210622212907 / files
nightly linux64
536a892dd51f / 91.0a1 / 20210622212907 / files
nightly mac
536a892dd51f / 91.0a1 / 20210622212907 / files
nightly win32
536a892dd51f / 91.0a1 / 20210622212907 / files
nightly win64
536a892dd51f / 91.0a1 / 20210622212907 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1717201 - Add optional callback parameter to map permission names to permission description keys. r=rpl Differential Revision: https://phabricator.services.mozilla.com/D118267
toolkit/components/extensions/Extension.jsm
toolkit/components/extensions/test/xpcshell/test_ext_permission_warnings.js
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -1600,16 +1600,23 @@ class ExtensionData {
    *                 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.
    *                  Default is 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.
    *
    * @returns {object} An object with properties containing localized strings
    *                   for various elements of a permission dialog. The "header"
    *                   property on this object is the notification header text
    *                   and it has the string "<>" as a placeholder for the
    *                   addon name.
    *
    *                   "object.msgs" is an array of localized strings describing required permissions
@@ -1619,17 +1626,20 @@ class ExtensionData {
    *
    *                   "object.optionalOrigins" is a map of a host permission to localized strings
    *                   describing the host permission, where appropriate.  Currently only
    *                   all url style permissions are included.
    */
   static formatPermissionStrings(
     info,
     bundle,
-    { collapseOrigins = false } = {}
+    {
+      collapseOrigins = false,
+      getKeyForPermission = perm => `webextPerms.description.${perm}`,
+    } = {}
   ) {
     let result = {
       msgs: [],
       optionalPermissions: {},
       optionalOrigins: {},
     };
 
     let perms = info.permissions || { origins: [], permissions: [] };
@@ -1682,59 +1692,59 @@ class ExtensionData {
       );
       format(
         Array.from(sites),
         "webextPerms.hostDescription.oneSite",
         "webextPerms.hostDescription.tooManySites"
       );
     }
 
-    let permissionKey = perm => `webextPerms.description.${perm}`;
-
     // 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(permissionKey(NATIVE_MSG_PERM), [
+        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(permissionKey(permission)));
+        result.msgs.push(
+          bundle.GetStringFromName(getKeyForPermission(permission))
+        );
       } catch (err) {
         // We deliberately do not include all permissions in the prompt.
         // So if we don't find one then just skip it.
       }
     }
 
     // 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(permissionKey(permission), [
+        ] = bundle.formatStringFromName(getKeyForPermission(permission), [
           info.appName,
         ]);
         continue;
       }
       try {
         result.optionalPermissions[permission] = bundle.GetStringFromName(
-          permissionKey(permission)
+          getKeyForPermission(permission)
         );
       } catch (err) {
         // We deliberately do not have strings for all permissions.
         // So if we don't find one then just skip it.
       }
     }
     allUrls = ExtensionData.classifyOriginPermissions(
       optional_permissions.origins
--- a/toolkit/components/extensions/test/xpcshell/test_ext_permission_warnings.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_permission_warnings.js
@@ -29,35 +29,77 @@ async function getManifestPermissions(ex
   ExtensionTestUtils.failOnSchemaWarnings(false);
   await extension.loadManifest();
   ExtensionTestUtils.failOnSchemaWarnings(true);
   const { manifestPermissions } = extension;
   await extension.cleanupGeneratedFile();
   return manifestPermissions;
 }
 
-function getPermissionWarnings(manifestPermissions, options) {
+function getPermissionWarnings(
+  manifestPermissions,
+  options,
+  stringBundle = bundle
+) {
   let info = {
     permissions: manifestPermissions,
     appName: DUMMY_APP_NAME,
   };
-  let { msgs } = ExtensionData.formatPermissionStrings(info, bundle, options);
+  let { msgs } = ExtensionData.formatPermissionStrings(
+    info,
+    stringBundle,
+    options
+  );
   return msgs;
 }
 
 async function getPermissionWarningsForUpdate(
   oldExtensionData,
   newExtensionData
 ) {
   let oldPerms = await getManifestPermissions(oldExtensionData);
   let newPerms = await getManifestPermissions(newExtensionData);
   let difference = Extension.comparePermissions(oldPerms, newPerms);
   return getPermissionWarnings(difference);
 }
 
+// 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",
+  };
+
+  // Define a non-default mapping for permission names -> locale keys.
+  const getKeyForPermission = perm => `customWebExtPerms.description.${perm}`;
+
+  const manifest = {
+    permissions: ["downloads", "proxy"],
+  };
+  const expectedWarnings = manifest.permissions.map(k =>
+    mockBundle.GetStringFromName(getKeyForPermission(k))
+  );
+  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"
+  );
+});
+
 // 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 = [