Bug 1516889 - Part 2: Make sure the storage access API prompt is denied when pressing Esc; r=johannh
☠☠ backed out by 03e18916050f ☠ ☠
authorEhsan Akhgari <ehsan@mozilla.com>
Wed, 16 Jan 2019 15:30:32 -0500
changeset 514716 fe40c77b54b48f19c629aea383a26af668239402
parent 514715 95fee3425c550b542894e545a365b9e15cd59d7e
child 514717 3b1381f652033d767f08494f68c8f74fe793b19d
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjohannh
bugs1516889
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1516889 - Part 2: Make sure the storage access API prompt is denied when pressing Esc; r=johannh Differential Revision: https://phabricator.services.mozilla.com/D16737
browser/modules/PermissionUI.jsm
toolkit/components/antitracking/test/browser/browser_storageAccessDoorHanger.js
toolkit/modules/PopupNotifications.jsm
--- a/browser/modules/PermissionUI.jsm
+++ b/browser/modules/PermissionUI.jsm
@@ -858,16 +858,17 @@ StorageAccessPermissionPrompt.prototype 
     return host;
   },
 
   get popupOptions() {
     return {
       displayURI: false,
       name: this.prettifyHostPort(this.principal.URI),
       secondName: this.prettifyHostPort(this.topLevelPrincipal.URI),
+      escAction: "buttoncommand",
     };
   },
 
   onShown() {
     let document = this.browser.ownerDocument;
     let label =
       gBrowserBundle.formatStringFromName("storageAccess.description.label",
                                           [this.prettifyHostPort(this.request.principal.URI), "<>"], 2);
--- a/toolkit/components/antitracking/test/browser/browser_storageAccessDoorHanger.js
+++ b/toolkit/components/antitracking/test/browser/browser_storageAccessDoorHanger.js
@@ -4,22 +4,23 @@ ChromeUtils.import("resource://gre/modul
 const CHROME_BASE = "chrome://mochitests/content/browser/browser/modules/test/browser/";
 Services.scriptloader.loadSubScript(CHROME_BASE + "head.js", this);
 /* import-globals-from ../../../../../browser/modules/test/browser/head.js */
 
 const BLOCK = 0;
 const ALLOW = 1;
 const ALLOW_ON_ANY_SITE = 2;
 
-async function testDoorHanger(choice, showPrompt, topPage, maxConcurrent) {
+async function testDoorHanger(choice, showPrompt, useEscape, topPage, maxConcurrent) {
   info(`Running doorhanger test with choice #${choice}, showPrompt: ${showPrompt} and ` +
-       `topPage: ${topPage}, maxConcurrent: ${maxConcurrent}`);
+       `useEscape: ${useEscape}, topPage: ${topPage}, maxConcurrent: ${maxConcurrent}`);
 
   if (!showPrompt) {
     is(choice, ALLOW, "When not showing a prompt, we can only auto-grant");
+    ok(!useEscape, "When not showing a prompt, we should not be trying to use the Esc key");
   }
 
   await SpecialPowers.flushPrefEnv();
   await SpecialPowers.pushPrefEnv({"set": [
     ["browser.contentblocking.allowlist.annotations.enabled", true],
     ["browser.contentblocking.allowlist.storage.enabled", true],
     [ContentBlocking.prefIntroCount, ContentBlocking.MAX_INTROS],
     ["dom.storage_access.auto_grants", true],
@@ -45,16 +46,17 @@ async function testDoorHanger(choice, sh
     // We need to repeat this constant here since runChecks is stringified
     // and sent to the content process.
     const BLOCK = 0;
 
     await new Promise(resolve => {
       addEventListener("message", function onMessage(e) {
         if (e.data.startsWith("choice:")) {
           window.choice = e.data.split(":")[1];
+          window.useEscape = e.data.split(":")[3];
           removeEventListener("message", onMessage);
           resolve();
         }
       }, false);
       parent.postMessage("getchoice", "*");
     });
 
     /* import-globals-from storageAccessAPIHelpers.js */
@@ -126,32 +128,37 @@ async function testDoorHanger(choice, sh
         resolve(notification);
         return;
       }
       setTimeout(poll, 10);
     });
     Assert.ok(notification, "Should have gotten the notification");
 
     if (choice == BLOCK) {
-      await clickMainAction();
+      if (useEscape) {
+        EventUtils.synthesizeKey("KEY_Escape", {}, window);
+      } else {
+        await clickMainAction();
+      }
     } else if (choice == ALLOW) {
       await clickSecondaryAction(choice - 1);
     } else if (choice == ALLOW_ON_ANY_SITE) {
       await clickSecondaryAction(choice - 1);
     }
     if (choice != BLOCK) {
       await permChanged;
     }
   });
 
   let url = TEST_3RD_PARTY_PAGE + "?disableWaitUntilPermission";
   let ct = ContentTask.spawn(browser,
                              { page: url,
                                callback: runChecks.toString(),
                                choice,
+                               useEscape,
                              },
                              async function(obj) {
     await new content.Promise(resolve => {
       let ifr = content.document.createElement("iframe");
       ifr.onload = function() {
         info("Sending code to the 3rd party content");
         ifr.contentWindow.postMessage(obj.callback, "*");
       };
@@ -169,17 +176,18 @@ async function testDoorHanger(choice, sh
         }
 
         if (event.data.type == "info") {
           info(event.data.msg);
           return;
         }
 
         if (event.data == "getchoice") {
-          ifr.contentWindow.postMessage("choice:" + obj.choice, "*");
+          ifr.contentWindow.postMessage("choice:" + obj.choice +
+                                        ":useEscape:" + obj.useEscape, "*");
           return;
         }
 
         ok(false, "Unknown message");
       });
 
       content.document.body.appendChild(ifr);
       ifr.src = obj.page;
@@ -298,26 +306,29 @@ async function cleanUp() {
   await new Promise(resolve => {
     Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value => resolve());
   });
 }
 
 async function runRound(topPage, showPrompt, maxConcurrent) {
   if (showPrompt) {
     await preparePermissionsFromOtherSites(topPage);
-    await testDoorHanger(BLOCK, showPrompt, topPage, maxConcurrent);
+    await testDoorHanger(BLOCK, showPrompt, true, topPage, maxConcurrent);
+    await cleanUp();
+    await preparePermissionsFromOtherSites(topPage);
+    await testDoorHanger(BLOCK, showPrompt, false, topPage, maxConcurrent);
     await cleanUp();
     await preparePermissionsFromOtherSites(topPage);
-    await testDoorHanger(ALLOW, showPrompt, topPage, maxConcurrent);
+    await testDoorHanger(ALLOW, showPrompt, false, topPage, maxConcurrent);
     await cleanUp();
     await preparePermissionsFromOtherSites(topPage);
-    await testDoorHanger(ALLOW_ON_ANY_SITE, showPrompt, topPage, maxConcurrent);
+    await testDoorHanger(ALLOW_ON_ANY_SITE, showPrompt, false, topPage, maxConcurrent);
   } else {
     await preparePermissionsFromOtherSites(topPage);
-    await testDoorHanger(ALLOW, showPrompt, topPage, maxConcurrent);
+    await testDoorHanger(ALLOW, showPrompt, false, topPage, maxConcurrent);
   }
   await cleanUp();
 }
 
 add_task(async function() {
   await runRound(TEST_TOP_PAGE, false, 1);
   await runRound(TEST_TOP_PAGE_2, true, 1);
   await runRound(TEST_TOP_PAGE, false, 5);
--- a/toolkit/modules/PopupNotifications.jsm
+++ b/toolkit/modules/PopupNotifications.jsm
@@ -248,17 +248,18 @@ function PopupNotifications(tabbrowser, 
 
     // If the chrome window has a focused element, let it handle the ESC key instead.
     if (!focusedElement ||
         focusedElement == doc.body ||
         focusedElement == this.tabbrowser.selectedBrowser ||
         // Ignore focused elements inside the notification.
         getNotificationFromElement(focusedElement) == notification ||
         notification.contains(focusedElement)) {
-      this._onButtonEvent(aEvent, "secondarybuttoncommand", "esc-press", notification);
+      let escAction = notification.notification.options.escAction;
+      this._onButtonEvent(aEvent, escAction, "esc-press", notification);
     }
   };
 
   let documentElement = this.window.document.documentElement;
   let locationBarHidden = documentElement.getAttribute("chromehidden").includes("location");
   let isFullscreen = !!this.window.document.fullscreenElement;
 
   this.panel.setAttribute("followanchor", !locationBarHidden && !isFullscreen);
@@ -462,16 +463,21 @@ PopupNotifications.prototype = {
    *                     An optional string formatted to look bold and used in the
    *                     notifiation description header text. Usually a host name or
    *                     addon name.
    *        secondName:
    *                     An optional string formatted to look bold and used in the
    *                     notification description header text. Usually a host name or
    *                     addon name. This is similar to name, and only used in case
    *                     where message contains two "<>" placeholders.
+   *        escAction:
+   *                     An optional string indicating the action to take when the
+   *                     Esc key is pressed. This should be set to the name of the
+   *                     command to run. If not provided, "secondarybuttoncommand"
+   *                     will be used.
    * @returns the Notification object corresponding to the added notification.
    */
   show: function PopupNotifications_show(browser, id, message, anchorID,
                                          mainAction, secondaryActions, options) {
     function isInvalidAction(a) {
       return !a || !(typeof(a.callback) == "function") || !a.label || !a.accessKey;
     }
 
@@ -482,16 +488,25 @@ PopupNotifications.prototype = {
     if (mainAction && isInvalidAction(mainAction))
       throw "PopupNotifications_show: invalid mainAction";
     if (secondaryActions && secondaryActions.some(isInvalidAction))
       throw "PopupNotifications_show: invalid secondaryActions";
 
     let notification = new Notification(id, message, anchorID, mainAction,
                                         secondaryActions, browser, this, options);
 
+    if (options) {
+      let escAction = options.escAction;
+      if (escAction != "buttoncommand" ||
+          escAction != "secondarybuttoncommand") {
+        escAction = "secondarybuttoncommand";
+      }
+      notification.options.escAction = escAction;
+    }
+
     if (options && options.dismissed)
       notification.dismissed = true;
 
     let existingNotification = this.getNotification(id, browser);
     if (existingNotification)
       this._remove(existingNotification);
 
     let notifications = this._getNotificationsForBrowser(browser);
@@ -826,17 +841,17 @@ PopupNotifications.prototype = {
         popupnotification.setAttribute("secondname", desc.secondName);
         popupnotification.setAttribute("secondendlabel", desc.secondEnd);
       }
 
       popupnotification.setAttribute("id", popupnotificationID);
       popupnotification.setAttribute("popupid", n.id);
       popupnotification.setAttribute("oncommand", "PopupNotifications._onCommand(event);");
       if (Services.prefs.getBoolPref("privacy.permissionPrompts.showCloseButton")) {
-        popupnotification.setAttribute("closebuttoncommand", "PopupNotifications._onButtonEvent(event, 'secondarybuttoncommand', 'esc-press');");
+        popupnotification.setAttribute("closebuttoncommand", "PopupNotifications._onButtonEvent(event, '" + n.options.escAction + "', 'esc-press');");
       } else {
         popupnotification.setAttribute("closebuttoncommand", `PopupNotifications._dismiss(event, ${TELEMETRY_STAT_DISMISSAL_CLOSE_BUTTON});`);
       }
       if (n.mainAction) {
         popupnotification.setAttribute("buttonlabel", n.mainAction.label);
         popupnotification.setAttribute("buttonaccesskey", n.mainAction.accessKey);
         popupnotification.toggleAttribute("buttonhighlight", !n.mainAction.disableHighlight);
         popupnotification.setAttribute("buttoncommand", "PopupNotifications._onButtonEvent(event, 'buttoncommand');");