Bug 1550091 - Add button to open a a site from a login item. r=MattN,Pike
authorJared Wein <jwein@mozilla.com>
Tue, 14 May 2019 20:36:33 +0000
changeset 532689 6e1256ea2dd73c29e9c4147c4afd2566062080a8
parent 532688 044fba1f3da7b9400382184918a9d325db3f3c90
child 532690 cedd9c296f112a48c64159888612a1da50d4ea8b
push id11270
push userrgurzau@mozilla.com
push dateWed, 15 May 2019 15:07:19 +0000
treeherdermozilla-beta@571bc76da583 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMattN, Pike
bugs1550091
milestone68.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 1550091 - Add button to open a a site from a login item. r=MattN,Pike Differential Revision: https://phabricator.services.mozilla.com/D30975
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/login-item.js
browser/components/aboutlogins/tests/browser/browser.ini
browser/components/aboutlogins/tests/browser/browser_openSite.js
--- a/browser/components/BrowserGlue.jsm
+++ b/browser/components/BrowserGlue.jsm
@@ -31,16 +31,17 @@ let ACTORS = {
 
 let LEGACY_ACTORS = {
   AboutLogins: {
     child: {
       matches: ["about:logins"],
       module: "resource:///actors/AboutLoginsChild.jsm",
       events: {
         "AboutLoginsDeleteLogin": {wantUntrusted: true},
+        "AboutLoginsOpenSite": {wantUntrusted: true},
         "AboutLoginsUpdateLogin": {wantUntrusted: true},
         "AboutLoginsInit": {wantUntrusted: true},
       },
       messages: [
         "AboutLogins:AllLogins",
         "AboutLogins:LoginAdded",
         "AboutLogins:LoginModified",
         "AboutLogins:LoginRemoved",
@@ -541,18 +542,19 @@ const listeners = {
     // PLEASE KEEP THIS LIST IN SYNC WITH THE LISTENERS ADDED IN AsyncPrefs.init
 
     "webrtc:UpdateGlobalIndicators": ["webrtcUI"],
     "webrtc:UpdatingIndicators": ["webrtcUI"],
   },
 
   mm: {
     "AboutLogins:DeleteLogin": ["AboutLoginsParent"],
+    "AboutLogins:OpenSite": ["AboutLoginsParent"],
+    "AboutLogins:Subscribe": ["AboutLoginsParent"],
     "AboutLogins:UpdateLogin": ["AboutLoginsParent"],
-    "AboutLogins:Subscribe": ["AboutLoginsParent"],
     "Content:Click": ["ContentClick"],
     "ContentSearch": ["ContentSearch"],
     "FormValidation:ShowPopup": ["FormValidationHandler"],
     "FormValidation:HidePopup": ["FormValidationHandler"],
     "PictureInPicture:Request": ["PictureInPicture"],
     "PictureInPicture:Close": ["PictureInPicture"],
     "PictureInPicture:Playing": ["PictureInPicture"],
     "PictureInPicture:Paused": ["PictureInPicture"],
--- a/browser/components/aboutlogins/AboutLoginsChild.jsm
+++ b/browser/components/aboutlogins/AboutLoginsChild.jsm
@@ -25,16 +25,20 @@ class AboutLoginsChild extends ActorChil
           cloneFunctions: true,
         });
         break;
       }
       case "AboutLoginsDeleteLogin": {
         this.mm.sendAsyncMessage("AboutLogins:DeleteLogin", {login: event.detail});
         break;
       }
+      case "AboutLoginsOpenSite": {
+        this.mm.sendAsyncMessage("AboutLogins:OpenSite", {login: event.detail});
+        break;
+      }
       case "AboutLoginsUpdateLogin": {
         this.mm.sendAsyncMessage("AboutLogins:UpdateLogin", {login: event.detail});
         break;
       }
     }
   }
 
   receiveMessage(message) {
--- a/browser/components/aboutlogins/AboutLoginsParent.jsm
+++ b/browser/components/aboutlogins/AboutLoginsParent.jsm
@@ -56,16 +56,27 @@ var AboutLoginsParent = {
     }
 
     switch (message.name) {
       case "AboutLogins:DeleteLogin": {
         let login = LoginHelper.vanillaObjectToLogin(message.data.login);
         Services.logins.removeLogin(login);
         break;
       }
+      case "AboutLogins:OpenSite": {
+        let guid = message.data.login.guid;
+        let logins = LoginHelper.searchLoginsWithObject({guid});
+        if (!logins || logins.length != 1) {
+          log.warn(`AboutLogins:OpenSite: expected to find a login for guid: ${guid} but found ${(logins || []).length}`);
+          return;
+        }
+
+        message.target.ownerGlobal.openWebLinkIn(logins[0].hostname, "tab", {relatedToCurrent: true});
+        break;
+      }
       case "AboutLogins:Subscribe": {
         if (!ChromeUtils.nondeterministicGetWeakSetKeys(this._subscribers).length) {
           Services.obs.addObserver(this, "passwordmgr-storage-changed");
         }
         this._subscribers.add(message.target);
 
         let messageManager = message.target.messageManager;
         messageManager.sendAsyncMessage("AboutLogins:AllLogins", this.getAllLogins());
--- a/browser/components/aboutlogins/content/aboutLogins.ftl
+++ b/browser/components/aboutlogins/content/aboutLogins.ftl
@@ -23,14 +23,15 @@ login-list =
     }
 
 login-item =
   .cancel-button = Cancel
   .delete-button = Delete
   .edit-button = Edit
   .hostname-label = Website Address
   .modal-input-reveal-button = Toggle password visibility
+  .open-site-button = Launch
   .password-label = Password
   .save-changes-button = Save Changes
   .time-created = Created: { DATETIME($timeCreated, day: "numeric", month: "long", year: "numeric") }
   .time-changed = Last changed: { DATETIME($timeChanged, day: "numeric", month: "long", year: "numeric") }
   .time-used = Last used: { DATETIME($timeUsed, day: "numeric", month: "long", year: "numeric") }
   .username-label = Username
--- a/browser/components/aboutlogins/content/aboutLogins.html
+++ b/browser/components/aboutlogins/content/aboutLogins.html
@@ -29,16 +29,17 @@
                 data-l10n-args='{"count": 0}'></login-list>
     <login-item data-l10n-id="login-item"
                 data-l10n-args='{"timeCreated": 0, "timeChanged": 0, "timeUsed": 0}'
                 data-l10n-attrs="cancel-button,
                                  delete-button,
                                  edit-button,
                                  hostname-label,
                                  modal-input-reveal-button,
+                                 open-site-button,
                                  password-label,
                                  save-changes-button,
                                  time-created,
                                  time-changed,
                                  time-used,
                                  username-label"></login-item>
 
     <template id="login-list-template">
@@ -65,16 +66,17 @@
         <h2 class="title"></h2>
         <button class="edit-button"></button>
         <button class="delete-button"></button>
       </div>
       <label>
         <span class="hostname-label field-label"></span>
         <span class="hostname"/>
       </label>
+      <button class="open-site-button"></button>
       <label>
         <span class="username-label field-label"></span>
         <modal-input name="username"/>
       </label>
       <label>
         <span class="password-label field-label"></span>
         <modal-input type="password" name="password"/>
       </label>
--- a/browser/components/aboutlogins/content/components/login-item.js
+++ b/browser/components/aboutlogins/content/components/login-item.js
@@ -20,16 +20,17 @@ class LoginItem extends ReflectedFluentE
     this.attachShadow({mode: "open"})
         .appendChild(loginItemTemplate.content.cloneNode(true));
 
     this.reflectFluentStrings();
 
     for (let selector of [
       ".delete-button",
       ".edit-button",
+      ".open-site-button",
       ".save-changes-button",
       ".cancel-button",
     ]) {
       let button = this.shadowRoot.querySelector(selector);
       button.addEventListener("click", this);
     }
 
     window.addEventListener("AboutLoginsLoginSelected", this);
@@ -39,16 +40,17 @@ class LoginItem extends ReflectedFluentE
 
   static get reflectedFluentIDs() {
     return [
       "cancel-button",
       "delete-button",
       "edit-button",
       "hostname-label",
       "modal-input-reveal-button",
+      "open-site-button",
       "password-label",
       "save-changes-button",
       "time-created",
       "time-changed",
       "time-used",
       "username-label",
     ];
   }
@@ -99,16 +101,23 @@ class LoginItem extends ReflectedFluentE
             detail: this._login,
           }));
           return;
         }
         if (event.target.classList.contains("edit-button")) {
           this.toggleEditing();
           return;
         }
+        if (event.target.classList.contains("open-site-button")) {
+          document.dispatchEvent(new CustomEvent("AboutLoginsOpenSite", {
+            bubbles: true,
+            detail: this._login,
+          }));
+          return;
+        }
         if (event.target.classList.contains("save-changes-button")) {
           let loginUpdates = {
             guid: this._login.guid,
           };
           let formUsername = this.shadowRoot.querySelector("modal-input[name='username']").value.trim();
           if (formUsername != this._login.username) {
             loginUpdates.username = formUsername;
           }
--- a/browser/components/aboutlogins/tests/browser/browser.ini
+++ b/browser/components/aboutlogins/tests/browser/browser.ini
@@ -1,7 +1,8 @@
 [DEFAULT]
 prefs =
   signon.management.page.enabled=true
 
 [browser_deleteLogin.js]
 [browser_loginListChanges.js]
+[browser_openSite.js]
 [browser_updateLogin.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/aboutlogins/tests/browser/browser_openSite.js
@@ -0,0 +1,37 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+let nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
+                                             Ci.nsILoginInfo, "init");
+const LOGIN_URL = "https://example.com/";
+let TEST_LOGIN1 = new nsLoginInfo(LOGIN_URL, LOGIN_URL, null, "user1", "pass1", "username", "password");
+
+add_task(async function setup() {
+  let storageChangedPromised = TestUtils.topicObserved("passwordmgr-storage-changed",
+                                                       (_, data) => data == "addLogin");
+  TEST_LOGIN1 = Services.logins.addLogin(TEST_LOGIN1);
+  await storageChangedPromised;
+  await BrowserTestUtils.openNewForegroundTab({gBrowser, url: "about:logins"});
+  registerCleanupFunction(() => {
+    BrowserTestUtils.removeTab(gBrowser.selectedTab);
+    Services.logins.removeAllLogins();
+  });
+});
+
+add_task(async function test_launch_login_item() {
+  let promiseNewTab = BrowserTestUtils.waitForNewTab(gBrowser, LOGIN_URL);
+
+  let browser = gBrowser.selectedBrowser;
+  await ContentTask.spawn(browser, LoginHelper.loginToVanillaObject(TEST_LOGIN1), async (login) => {
+    let loginItem = Cu.waiveXrays(content.document.querySelector("login-item"));
+    loginItem.setLogin(login);
+    let openSiteButton = loginItem.shadowRoot.querySelector(".open-site-button");
+    openSiteButton.click();
+  });
+
+  info("waiting for new tab to get opened");
+  let newTab = await promiseNewTab;
+  ok(true, "New tab opened to " + LOGIN_URL);
+
+  BrowserTestUtils.removeTab(newTab);
+});