Bug 1559551 - Add Feedback menuitem to ellipsis menu.?jaws r=fluent-reviewers,jaws,flod
authormeandave <djustice@mozilla.com>
Tue, 25 Jun 2019 17:42:51 +0000
changeset 542897 82f44a6420c57bd206a166477bf3ab1fe005bcab
parent 542896 0d9f920680e323ad178ac6546f8834b1e4ef1e37
child 542898 700dcc6217ede19b57001457ffc927888003f1e9
push id2131
push userffxbld-merge
push dateMon, 26 Aug 2019 18:30:20 +0000
treeherdermozilla-release@b19ffb3ca153 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfluent-reviewers, jaws, flod
bugs1559551
milestone69.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 1559551 - Add Feedback menuitem to ellipsis menu.?jaws r=fluent-reviewers,jaws,flod Differential Revision: https://phabricator.services.mozilla.com/D35379
browser/app/profile/firefox.js
browser/components/BrowserGlue.jsm
browser/components/aboutlogins/AboutLoginsChild.jsm
browser/components/aboutlogins/AboutLoginsParent.jsm
browser/components/aboutlogins/content/aboutLogins.ftl
browser/components/aboutlogins/content/aboutLogins.html
browser/components/aboutlogins/content/components/menu-button.css
browser/components/aboutlogins/content/components/menu-button.js
browser/components/aboutlogins/content/icons/feedback.svg
browser/components/aboutlogins/jar.mn
browser/components/aboutlogins/tests/chrome/test_menu_button.html
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1725,16 +1725,18 @@ pref("extensions.pocket.oAuthConsumerKey
 pref("extensions.pocket.site", "getpocket.com");
 
 pref("signon.schemeUpgrades", true);
 pref("signon.privateBrowsingCapture.enabled", true);
 pref("signon.showAutoCompleteFooter", true);
 pref("signon.management.page.enabled", false);
 pref("signon.showAutoCompleteOrigins", true);
 pref("signon.includeOtherSubdomainsInLookup", true);
+pref("signon.feedbackURL",
+     "https://www.surveygizmo.com/s3/5036102/Lockwise-feedback?ver=%VERSION%");
 
 // Enable the "Simplify Page" feature in Print Preview. This feature
 // is disabled by default in toolkit.
 pref("print.use_simplify_page", true);
 
 // Space separated list of URLS that are allowed to send objects (instead of
 // only strings) through webchannels. This list is duplicated in mobile/android/app/mobile.js
 pref("webchannel.allowObject.urlWhitelist", "https://content.cdn.mozilla.net https://support.mozilla.org https://install.mozilla.org");
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -84,16 +84,17 @@ let LEGACY_ACTORS = {
     child: {
       matches: ["about:logins", "about:logins?*"],
       module: "resource:///actors/AboutLoginsChild.jsm",
       events: {
         "AboutLoginsCreateLogin": {wantUntrusted: true},
         "AboutLoginsDeleteLogin": {wantUntrusted: true},
         "AboutLoginsImport": {wantUntrusted: true},
         "AboutLoginsInit": {wantUntrusted: true},
+        "AboutLoginsOpenFeedback": {wantUntrusted: true},
         "AboutLoginsOpenPreferences": {wantUntrusted: true},
         "AboutLoginsOpenSite": {wantUntrusted: true},
         "AboutLoginsRecordTelemetryEvent": {wantUntrusted: true},
         "AboutLoginsUpdateLogin": {wantUntrusted: true},
       },
       messages: [
         "AboutLogins:AllLogins",
         "AboutLogins:LoginAdded",
@@ -580,16 +581,17 @@ const listeners = {
     "webrtc:UpdateGlobalIndicators": ["webrtcUI"],
     "webrtc:UpdatingIndicators": ["webrtcUI"],
   },
 
   mm: {
     "AboutLogins:CreateLogin": ["AboutLoginsParent"],
     "AboutLogins:DeleteLogin": ["AboutLoginsParent"],
     "AboutLogins:Import": ["AboutLoginsParent"],
+    "AboutLogins:OpenFeedback": ["AboutLoginsParent"],
     "AboutLogins:OpenPreferences": ["AboutLoginsParent"],
     "AboutLogins:OpenSite": ["AboutLoginsParent"],
     "AboutLogins:Subscribe": ["AboutLoginsParent"],
     "AboutLogins:UpdateLogin": ["AboutLoginsParent"],
     "Content:Click": ["ContentClick"],
     "ContentSearch": ["ContentSearch"],
     "FormValidation:ShowPopup": ["FormValidationHandler"],
     "FormValidation:HidePopup": ["FormValidationHandler"],
@@ -603,16 +605,17 @@ const listeners = {
     // PLEASE KEEP THIS LIST IN SYNC WITH THE MOBILE LISTENERS IN BrowserCLH.js
     "PasswordManager:findLogins": ["LoginManagerParent"],
     "PasswordManager:findRecipes": ["LoginManagerParent"],
     "PasswordManager:onFormSubmit": ["LoginManagerParent"],
     "PasswordManager:onGeneratedPasswordFilled": ["LoginManagerParent"],
     "PasswordManager:autoCompleteLogins": ["LoginManagerParent"],
     "PasswordManager:removeLogin": ["LoginManagerParent"],
     "PasswordManager:insecureLoginFormPresent": ["LoginManagerParent"],
+    "PasswordManager:OpenFeedback": ["AboutLoginsParent"],
     "PasswordManager:OpenPreferences": ["LoginManagerParent"],
     // PLEASE KEEP THIS LIST IN SYNC WITH THE MOBILE LISTENERS IN BrowserCLH.js
     "rtcpeer:CancelRequest": ["webrtcUI"],
     "rtcpeer:Request": ["webrtcUI"],
     "webrtc:CancelRequest": ["webrtcUI"],
     "webrtc:Request": ["webrtcUI"],
     "webrtc:StopRecording": ["webrtcUI"],
     "webrtc:UpdateBrowserIndicators": ["webrtcUI"],
--- a/browser/components/aboutlogins/AboutLoginsChild.jsm
+++ b/browser/components/aboutlogins/AboutLoginsChild.jsm
@@ -37,16 +37,20 @@ class AboutLoginsChild extends ActorChil
       case "AboutLoginsCreateLogin": {
         this.mm.sendAsyncMessage("AboutLogins:CreateLogin", {login: event.detail});
         break;
       }
       case "AboutLoginsDeleteLogin": {
         this.mm.sendAsyncMessage("AboutLogins:DeleteLogin", {login: event.detail});
         break;
       }
+      case "AboutLoginsOpenFeedback": {
+        this.mm.sendAsyncMessage("AboutLogins:OpenFeedback");
+        break;
+      }
       case "AboutLoginsImport": {
         this.mm.sendAsyncMessage("AboutLogins:Import");
         break;
       }
       case "AboutLoginsOpenPreferences": {
         this.mm.sendAsyncMessage("AboutLogins:OpenPreferences");
         break;
       }
--- a/browser/components/aboutlogins/AboutLoginsParent.jsm
+++ b/browser/components/aboutlogins/AboutLoginsParent.jsm
@@ -25,16 +25,20 @@ XPCOMUtils.defineLazyGetter(this, "log",
 const ABOUT_LOGINS_ORIGIN = "about:logins";
 const MASTER_PASSWORD_NOTIFICATION_ID = "master-password-login-required";
 
 const PRIVILEGEDABOUT_PROCESS_PREF =
   "browser.tabs.remote.separatePrivilegedContentProcess";
 const PRIVILEGEDABOUT_PROCESS_ENABLED =
   Services.prefs.getBoolPref(PRIVILEGEDABOUT_PROCESS_PREF, false);
 
+
+const FEEDBACK_URL_PREF = "signon.feedbackURL";
+const FEEDBACK_URL = Services.urlFormatter.formatURLPref(FEEDBACK_URL_PREF);
+
 // When the privileged content process is enabled, we expect about:logins
 // to load in it. Otherwise, it's in a normal web content process.
 const EXPECTED_ABOUTLOGINS_REMOTE_TYPE =
   PRIVILEGEDABOUT_PROCESS_ENABLED ? E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE
                                   : E10SUtils.DEFAULT_REMOTE_TYPE;
 
 const isValidLogin = login => {
   return !(login.origin || "").startsWith("chrome://");
@@ -102,16 +106,20 @@ var AboutLoginsParent = {
         try {
           MigrationUtils.showMigrationWizard(message.target.ownerGlobal,
                                              [MigrationUtils.MIGRATION_ENTRYPOINT_PASSWORDS]);
         } catch (ex) {
           Cu.reportError(ex);
         }
         break;
       }
+      case "AboutLogins:OpenFeedback": {
+        message.target.ownerGlobal.openWebLinkIn(FEEDBACK_URL, "tab", {relatedToCurrent: true});
+        break;
+      }
       case "AboutLogins:OpenPreferences": {
         message.target.ownerGlobal.openPreferences("privacy-logins");
         break;
       }
       case "AboutLogins:OpenSite": {
         let guid = message.data.login.guid;
         let logins = LoginHelper.searchLoginsWithObject({guid});
         if (logins.length != 1) {
--- a/browser/components/aboutlogins/content/aboutLogins.ftl
+++ b/browser/components/aboutlogins/content/aboutLogins.ftl
@@ -56,14 +56,15 @@ login-item =
 master-password-notification-message = Please enter your master password to view saved logins & passwords
 # TODO: Not sure how to use formatValue with these as attributes on a single ID
 master-password-reload-button-label = Log in
 # TODO: Not sure how to use formatValue with these as attributes on a single ID
 master-password-reload-button-accesskey = L
 
 menu-button =
   .button-title = Open menu
+  .menuitem-feedback = Leave Feedback
   .menuitem-import = Import Passwords…
   .menuitem-preferences =
     { PLATFORM() ->
         [windows] Options
        *[other] Preferences
     }
--- a/browser/components/aboutlogins/content/aboutLogins.html
+++ b/browser/components/aboutlogins/content/aboutLogins.html
@@ -22,16 +22,17 @@
   <body>
     <header>
       <img id="branding-logo" src="chrome://branding/content/aboutlogins.svg" alt=""/>
       <login-filter data-l10n-id="login-filter"
                     data-l10n-attrs="placeholder"></login-filter>
       <button id="create-login-button" data-l10n-id="create-login-button"></button>
       <menu-button data-l10n-id="menu-button"
                    data-l10n-attrs="button-title,
+                                    menuitem-feedback,
                                     menuitem-import,
                                     menuitem-preferences"></menu-button>
     </header>
     <login-list data-l10n-id="login-list"
                 data-l10n-args='{"count": 0}'
                 data-l10n-attrs="count,
                                  last-changed-option,
                                  last-used-option,
@@ -143,16 +144,19 @@
       <link rel="stylesheet" href="chrome://browser/content/aboutlogins/common.css">
       <link rel="stylesheet" href="chrome://browser/content/aboutlogins/components/menu-button.css">
       <button class="menu-button alternate-button"></button>
       <ul class="menu" role="menu" hidden>
         <li role="menuitem" class="menuitem windows-only">
           <button class="menuitem-button menuitem-import alternate-button" data-event-name="AboutLoginsImport"></button>
         </li>
         <li role="menuitem" class="menuitem">
+          <button class="menuitem-button menuitem-feedback alternate-button" data-event-name="AboutLoginsOpenFeedback"></button>
+        </li>
+        <li role="menuitem" class="menuitem">
           <button class="menuitem-button menuitem-preferences alternate-button" data-event-name="AboutLoginsOpenPreferences"></button>
         </li>
       </ul>
     </template>
 
     <template id="copy-to-clipboard-button-template">
       <link rel="stylesheet" href="chrome://global/skin/in-content/common.css">
       <link rel="stylesheet" href="chrome://browser/content/aboutlogins/components/copy-to-clipboard-button.css">
--- a/browser/components/aboutlogins/content/components/menu-button.css
+++ b/browser/components/aboutlogins/content/components/menu-button.css
@@ -63,11 +63,15 @@
 .menuitem-import {
   background-image: url("chrome://browser/skin/save.svg");
 }
 
 .menuitem-preferences {
   background-image: url("chrome://browser/skin/settings.svg");
 }
 
+.menuitem-feedback {
+  background-image: url("chrome://browser/content/aboutlogins/icons/feedback.svg");
+}
+
 :host(:not(.Win32)) .windows-only {
   display: none;
 }
--- a/browser/components/aboutlogins/content/components/menu-button.js
+++ b/browser/components/aboutlogins/content/components/menu-button.js
@@ -28,16 +28,17 @@ export default class MenuButton extends 
 
     super.connectedCallback();
   }
 
   static get reflectedFluentIDs() {
     return [
       "button-title",
       "menuitem-import",
+      "menuitem-feedback",
       "menuitem-preferences",
     ];
   }
 
   static get observedAttributes() {
     return MenuButton.reflectedFluentIDs;
   }
 
@@ -58,16 +59,17 @@ export default class MenuButton extends 
         // that was clicked on.
         if (event.currentTarget == document.documentElement &&
             event.target == this &&
             event.originalTarget == this._menuButton) {
           return;
         }
         let classList = event.originalTarget.classList;
         if (classList.contains("menuitem-import") ||
+            classList.contains("menuitem-feedback") ||
             classList.contains("menuitem-preferences")) {
           let eventName = event.originalTarget.dataset.eventName;
           document.dispatchEvent(new CustomEvent(eventName, {
             bubbles: true,
           }));
           this._hideMenu();
           break;
         }
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/content/icons/feedback.svg
@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="14" viewBox="0 0 16 14">
+    <g fill="none" fill-rule="evenodd">
+        <path d="M0-1h16v16H0z"/>
+        <path fill="context-fill" fill-rule="nonzero" d="M14.929 2.984c-.715-.878-1.685-1.572-2.911-2.08C10.792.393 9.452.14 8 .14S5.208.394 3.982.903c-1.226.51-2.196 1.203-2.91 2.08C.356 3.863 0 4.819 0 5.855c0 .893.27 1.73.808 2.51.539.779 1.275 1.434 2.21 1.964a5.7 5.7 0 0 1-.232.678c-.09.214-.17.39-.241.527a3.624 3.624 0 0 1-.29.455 4.657 4.657 0 0 1-.277.353 142.3 142.3 0 0 0-.657.728l-.062.076-.054.08c-.03.044-.043.073-.04.085a.237.237 0 0 1-.018.089c-.015.048-.013.083.005.107v.009a.455.455 0 0 0 .143.246.358.358 0 0 0 .24.093h.045a6.733 6.733 0 0 0 1.018-.196 9.757 9.757 0 0 0 4.107-2.161c.447.048.878.071 1.295.071 1.452 0 2.792-.254 4.018-.763 1.226-.51 2.196-1.202 2.91-2.08C15.643 7.847 16 6.89 16 5.855c0-1.036-.357-1.993-1.071-2.871zm-1.457 4.948c-.575.645-1.35 1.158-2.326 1.538-.976.38-2.025.57-3.146.57-.335 0-.693-.022-1.072-.065l-.47-.05-.355.312a8.138 8.138 0 0 1-2.268 1.403c.253-.443.446-.913.578-1.41l.222-.788-.717-.41c-.715-.405-1.269-.885-1.662-1.44-.393-.555-.59-1.14-.59-1.752 0-.749.288-1.446.862-2.092.575-.645 1.35-1.158 2.326-1.538A8.591 8.591 0 0 1 8 1.64c1.122 0 2.17.19 3.146.57.976.38 1.75.893 2.326 1.538.574.646.861 1.343.861 2.092 0 .75-.287 1.447-.861 2.092z"/>
+    </g>
+</svg>
--- a/browser/components/aboutlogins/jar.mn
+++ b/browser/components/aboutlogins/jar.mn
@@ -13,15 +13,16 @@ browser.jar:
   content/browser/aboutlogins/components/login-list.js         (content/components/login-list.js)
   content/browser/aboutlogins/components/login-list-item.css   (content/components/login-list-item.css)
   content/browser/aboutlogins/components/login-list-item.js    (content/components/login-list-item.js)
   content/browser/aboutlogins/components/menu-button.css       (content/components/menu-button.css)
   content/browser/aboutlogins/components/menu-button.js        (content/components/menu-button.js)
   content/browser/aboutlogins/components/reflected-fluent-element.js  (content/components/reflected-fluent-element.js)
   content/browser/aboutlogins/icons/delete.svg  (content/icons/delete.svg)
   content/browser/aboutlogins/icons/edit.svg    (content/icons/edit.svg)
+  content/browser/aboutlogins/icons/feedback.svg  (content/icons/feedback.svg)
   content/browser/aboutlogins/icons/hide-password.svg (content/icons/hide-password.svg)
   content/browser/aboutlogins/icons/show-password.svg (content/icons/show-password.svg)
   content/browser/aboutlogins/aboutLogins.css   (content/aboutLogins.css)
   content/browser/aboutlogins/aboutLogins.js    (content/aboutLogins.js)
   content/browser/aboutlogins/aboutLogins.html  (content/aboutLogins.html)
   content/browser/aboutlogins/aboutLoginsUtils.js (content/aboutLoginsUtils.js)
   content/browser/aboutlogins/common.css        (content/common.css)
--- a/browser/components/aboutlogins/tests/chrome/test_menu_button.html
+++ b/browser/components/aboutlogins/tests/chrome/test_menu_button.html
@@ -45,24 +45,34 @@ add_task(async function test_menu_open_c
   is(document.activeElement, gMenuButton, "menu-button should be focused to start the test");
 
   let menu = gMenuButton.shadowRoot.querySelector(".menu");
   is(true, menu.hidden, "menu should be hidden before pressing 'space'");
   sendKey("SPACE");
   await new Promise(resolve => requestAnimationFrame(resolve));
   is(false, menu.hidden, "menu should be visible after pressing 'space'");
 
-  let preferencesItem = gMenuButton.shadowRoot.querySelector(".menuitem-preferences");
-  ok(!preferencesItem.matches(":focus"), ".menuitem-preferences should not be focused before tabbing to it");
+  let feedbackItem = gMenuButton.shadowRoot.querySelector(".menuitem-feedback");
+  ok(!feedbackItem.matches(":focus"), ".menuitem-feedback should not be focused before tabbing to it");
   // The Import menuitem is only visible on Windows, where we will need a second Tab
-  // press to get to the Preferences item.
+  // press to get to the Feedback item.
   let tabs = navigator.platform == "Win32" ? 2 : 1;
   while (tabs--) {
     sendKey("TAB");
   }
+
+  await SimpleTest.promiseWaitForCondition(() => feedbackItem.matches(":focus"),
+                                           "waiting for feedbackItem to get focus");
+  ok(feedbackItem.matches(":focus"), ".menuitem-feedback should be focused after tabbing to it");
+
+  let preferencesItem = gMenuButton.shadowRoot.querySelector(".menuitem-preferences");
+  ok(!preferencesItem.matches(":focus"), ".menuitem-preferences should not be focused before tabbing to it");
+  // We will need a third Tab press to get to the Preferences item.
+  sendKey("TAB");
+
   await SimpleTest.promiseWaitForCondition(() => preferencesItem.matches(":focus"),
     "waiting for preferencesItem to get focus");
   ok(preferencesItem.matches(":focus"), ".menuitem-preferences should be focused after tabbing to it");
 
   let openPreferencesEvent = null;
   is(false, menu.hidden, "menu should be visible before pressing 'space' on .menuitem-preferences");
   window.addEventListener("AboutLoginsOpenPreferences", event => openPreferencesEvent = event);
   sendKey("SPACE");