Bug 1334663 - Add ability to copy and launch URL from password manager
MozReview-Commit-ID: JApe0OhTIg8
--- a/toolkit/components/passwordmgr/content/passwordManager.js
+++ b/toolkit/components/passwordmgr/content/passwordManager.js
@@ -627,16 +627,25 @@ function FilterPasswords() {
if (signonsTreeView.rowCount > 0)
signonsTreeView.selection.select(0);
signonsIntro.textContent = kSignonBundle.getString("loginsDescriptionFiltered");
removeAllButton.setAttribute("label", kSignonBundle.getString("removeAllShown.label"));
removeAllButton.setAttribute("accesskey", kSignonBundle.getString("removeAllShown.accesskey"));
}
+function CopySiteUrl() {
+ // Copy selected site url to clipboard
+ let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].
+ getService(Ci.nsIClipboardHelper);
+ let row = signonsTree.currentIndex;
+ let url = signonsTreeView.getCellText(row, {id: "siteCol"});
+ clipboard.copyString(url);
+}
+
function CopyPassword() {
// Don't copy passwords if we aren't already showing the passwords & a master
// password hasn't been entered.
if (!showingPasswords && !masterPasswordLogin())
return;
// Copy selected signon's password to clipboard
let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].
getService(Ci.nsIClipboardHelper);
@@ -657,16 +666,22 @@ function CopyUsername() {
}
function EditCellInSelectedRow(columnName) {
let row = signonsTree.currentIndex;
let columnElement = getColumnByName(columnName);
signonsTree.startEditing(row, signonsTree.columns.getColumnFor(columnElement));
}
+function LaunchSiteUrl() {
+ let row = signonsTree.currentIndex;
+ let url = signonsTreeView.getCellText(row, {id: "siteCol"});
+ window.openUILinkIn(url, "tab");
+}
+
function UpdateContextMenu() {
let singleSelection = (signonsTreeView.selection.count == 1);
let menuItems = new Map();
let menupopup = document.getElementById("signonsTreeContextMenu");
for (let menuItem of menupopup.querySelectorAll("menuitem")) {
menuItems.set(menuItem.id, menuItem);
}
@@ -674,23 +689,32 @@ function UpdateContextMenu() {
for (let menuItem of menuItems.values()) {
menuItem.setAttribute("disabled", "true");
}
return;
}
let selectedRow = signonsTree.currentIndex;
+ // Don't display "Launch Site URL" if we're not a browser.
+ if (window.openUILinkIn) {
+ menuItems.get("context-launchsiteurl").removeAttribute("disabled");
+ } else {
+ menuItems.get("context-launchsiteurl").setAttribute("disabled", "true");
+ menuItems.get("context-launchsiteurl").setAttribute("hidden", "true");
+ }
+
// Disable "Copy Username" if the username is empty.
if (signonsTreeView.getCellText(selectedRow, { id: "userCol" }) != "") {
menuItems.get("context-copyusername").removeAttribute("disabled");
} else {
menuItems.get("context-copyusername").setAttribute("disabled", "true");
}
+ menuItems.get("context-copysiteurl").removeAttribute("disabled");
menuItems.get("context-editusername").removeAttribute("disabled");
menuItems.get("context-copypassword").removeAttribute("disabled");
// Disable "Edit Password" if the password column isn't showing.
if (!document.getElementById("passwordCol").hidden) {
menuItems.get("context-editpassword").removeAttribute("disabled");
} else {
menuItems.get("context-editpassword").setAttribute("disabled", "true");
--- a/toolkit/components/passwordmgr/content/passwordManager.xul
+++ b/toolkit/components/passwordmgr/content/passwordManager.xul
@@ -12,31 +12,43 @@
windowtype="Toolkit:PasswordManager"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="Startup();"
onunload="Shutdown();"
title="&savedLogins.title;"
style="width: 45em;"
persist="width height screenX screenY">
+#if defined(MOZ_BUILD_APP_IS_BROWSER)
+ <script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
+#endif
<script type="application/javascript" src="chrome://passwordmgr/content/passwordManager.js"/>
<stringbundle id="signonBundle"
src="chrome://passwordmgr/locale/passwordmgr.properties"/>
<keyset>
<key keycode="VK_ESCAPE" oncommand="escapeKeyHandler();"/>
<key key="&windowClose.key;" modifiers="accel" oncommand="escapeKeyHandler();"/>
<key key="&focusSearch1.key;" modifiers="accel" oncommand="FocusFilterBox();"/>
<key key="&focusSearch2.key;" modifiers="accel" oncommand="FocusFilterBox();"/>
</keyset>
<popupset id="signonsTreeContextSet">
<menupopup id="signonsTreeContextMenu"
onpopupshowing="UpdateContextMenu()">
+ <menuitem id="context-copysiteurl"
+ label="©SiteUrlCmd.label;"
+ accesskey="©SiteUrlCmd.accesskey;"
+ oncommand="CopySiteUrl()"/>
+ <menuitem id="context-launchsiteurl"
+ label="&launchSiteUrlCmd.label;"
+ accesskey="&launchSiteUrlCmd.accesskey;"
+ oncommand="LaunchSiteUrl()"/>
+ <menuseparator/>
<menuitem id="context-copyusername"
label="©UsernameCmd.label;"
accesskey="©UsernameCmd.accesskey;"
oncommand="CopyUsername()"/>
<menuitem id="context-editusername"
label="&editUsernameCmd.label;"
accesskey="&editUsernameCmd.accesskey;"
oncommand="EditCellInSelectedRow('username')"/>
--- a/toolkit/components/passwordmgr/test/browser/browser_passwordmgr_contextmenu.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_passwordmgr_contextmenu.js
@@ -34,42 +34,51 @@ add_task(function* test() {
function doTest() {
let doc = pwmgrdlg.document;
let selection = doc.getElementById("signonsTree").view.selection;
let menuitem = doc.getElementById("context-copyusername");
function copyField() {
info("Select all");
selection.selectAll();
+ assertMenuitemEnabled("copysiteurl", false);
+ assertMenuitemEnabled("launchsiteurl", false);
assertMenuitemEnabled("copyusername", false);
assertMenuitemEnabled("editusername", false);
assertMenuitemEnabled("copypassword", false);
assertMenuitemEnabled("editpassword", false);
info("Select the first row (with an empty username)");
selection.select(0);
+ assertMenuitemEnabled("copysiteurl", true);
+ assertMenuitemEnabled("launchsiteurl", true);
assertMenuitemEnabled("copyusername", false, "empty username");
assertMenuitemEnabled("editusername", true);
assertMenuitemEnabled("copypassword", true);
assertMenuitemEnabled("editpassword", false, "password column hidden");
info("Clear the selection");
selection.clearSelection();
+ assertMenuitemEnabled("copysiteurl", false);
+ assertMenuitemEnabled("launchsiteurl", false);
assertMenuitemEnabled("copyusername", false);
assertMenuitemEnabled("editusername", false);
assertMenuitemEnabled("copypassword", false);
assertMenuitemEnabled("editpassword", false);
info("Select the third row and making the password column visible");
selection.select(2);
doc.getElementById("passwordCol").hidden = false;
+ assertMenuitemEnabled("copysiteurl", true);
+ assertMenuitemEnabled("launchsiteurl", true);
assertMenuitemEnabled("copyusername", true);
assertMenuitemEnabled("editusername", true);
assertMenuitemEnabled("copypassword", true);
assertMenuitemEnabled("editpassword", true, "password column visible");
+
menuitem.doCommand();
}
function assertMenuitemEnabled(idSuffix, expected, reason = "") {
doc.defaultView.UpdateContextMenu();
let actual = !doc.getElementById("context-" + idSuffix).getAttribute("disabled");
is(actual, expected, idSuffix + " should be " + (expected ? "enabled" : "disabled") +
(reason ? ": " + reason : ""));
@@ -80,21 +89,29 @@ add_task(function* test() {
Services.ww.unregisterNotification(arguments.callee);
Services.logins.removeAllLogins();
doc.getElementById("passwordCol").hidden = true;
resolve();
});
pwmgrdlg.close();
}
+ function testSiteUrl() {
+ info("Testing Copy Site URL");
+ waitForClipboard("http://mozilla.org/", function copySiteUrl() {
+ menuitem = doc.getElementById("context-copysiteurl");
+ menuitem.doCommand();
+ }, cleanUp, cleanUp);
+ }
+
function testPassword() {
info("Testing Copy Password");
waitForClipboard("coded", function copyPassword() {
menuitem = doc.getElementById("context-copypassword");
menuitem.doCommand();
- }, cleanUp, cleanUp);
+ }, testSiteUrl, testSiteUrl);
}
info("Testing Copy Username");
waitForClipboard("ehsan", copyField, testPassword, testPassword);
}
});
});
--- a/toolkit/locales/en-US/chrome/passwordmgr/passwordManager.dtd
+++ b/toolkit/locales/en-US/chrome/passwordmgr/passwordManager.dtd
@@ -26,19 +26,25 @@
<!ENTITY searchFilter.label "Search">
<!ENTITY searchFilter.accesskey "S">
<!ENTITY windowClose.key "w">
<!ENTITY focusSearch1.key "f">
<!ENTITY focusSearch2.key "k">
+<!ENTITY copySiteUrlCmd.label "Copy URL">
+<!ENTITY copySiteUrlCmd.accesskey "y">
+
<!ENTITY copyPasswordCmd.label "Copy Password">
<!ENTITY copyPasswordCmd.accesskey "C">
<!ENTITY copyUsernameCmd.label "Copy Username">
<!ENTITY copyUsernameCmd.accesskey "U">
<!ENTITY editPasswordCmd.label "Edit Password">
<!ENTITY editPasswordCmd.accesskey "E">
<!ENTITY editUsernameCmd.label "Edit Username">
<!ENTITY editUsernameCmd.accesskey "d">
+
+<!ENTITY launchSiteUrlCmd.label "Visit URL">
+<!ENTITY launchSiteUrlCmd.accesskey "V">