Bug 1257078 - Implement a selection feature for showing some specific password.; r?MattN draft
authorSean Lee <selee@mozilla.com>
Sat, 25 Jun 2016 04:44:50 +0800
changeset 453272 d914362e4406679c8f5d96387e94fd5ba6c62c3a
parent 453271 5b0c26479636c5991fc86f017b5b1dd5c089445c
child 540408 e0ae6c6c647551e77c4e011186406467e4dc0f2a
push id39611
push userbmo:selee@mozilla.com
push dateFri, 23 Dec 2016 03:13:14 +0000
reviewersMattN
bugs1257078
milestone53.0a1
Bug 1257078 - Implement a selection feature for showing some specific password.; r?MattN MozReview-Commit-ID: CTGhZ9ZxbTm
dom/base/nsGkAtomList.h
layout/xul/tree/nsTreeBodyFrame.cpp
toolkit/components/passwordmgr/content/passwordManager.js
toolkit/components/passwordmgr/content/passwordManager.xul
toolkit/components/passwordmgr/test/browser/browser_passwordmgr_contextmenu.js
toolkit/components/passwordmgr/test/browser/browser_passwordmgr_sort.js
toolkit/components/passwordmgr/test/browser/browser_passwordmgrdlg.js
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -1075,16 +1075,17 @@ GK_ATOM(resizebefore, "resizebefore")
 GK_ATOM(resizer, "resizer")
 GK_ATOM(resolution, "resolution")
 GK_ATOM(resource, "resource")
 GK_ATOM(resources, "resources")
 GK_ATOM(result, "result")
 GK_ATOM(resultPrefix, "result-prefix")
 GK_ATOM(retargetdocumentfocus, "retargetdocumentfocus")
 GK_ATOM(rev, "rev")
+GK_ATOM(revealSelectedPasswords, "revealselectedpasswords")
 GK_ATOM(reverse, "reverse")
 GK_ATOM(reversed, "reversed")
 GK_ATOM(richlistbox, "richlistbox")
 GK_ATOM(richlistitem, "richlistitem")
 GK_ATOM(right, "right")
 GK_ATOM(rightmargin, "rightmargin")
 GK_ATOM(rightpadding, "rightpadding")
 GK_ATOM(role, "role")
--- a/layout/xul/tree/nsTreeBodyFrame.cpp
+++ b/layout/xul/tree/nsTreeBodyFrame.cpp
@@ -3735,18 +3735,35 @@ nsTreeBodyFrame::PaintText(int32_t      
   NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
 
   bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
 
   // Now obtain the text for our cell.
   nsAutoString text;
   mView->GetCellText(aRowIndex, aColumn, text);
 
-  if (aColumn->Type() == nsITreeColumn::TYPE_PASSWORD) {
-    TextEditRules::FillBufWithPWChars(&text, text.Length());
+  nsCOMPtr<nsITreeSelection> selection;
+  bool isSelected = false;
+  mView->GetSelection(getter_AddRefs(selection));
+  if (selection) {
+    selection->IsSelected(aRowIndex, &isSelected);
+  }
+
+  nsIContent* baseContent = GetBaseElement();
+  bool revealSelectedPasswords = isSelected && baseContent &&
+       baseContent->HasAttr(kNameSpaceID_None, nsGkAtoms::revealSelectedPasswords);
+
+  if (aColumn->Type() == nsITreeColumn::TYPE_PASSWORD && !revealSelectedPasswords) {
+    // For the security reason, the amount of dot character should be fixed.
+    // Please see Bug 1209267 as well.
+    TextEditRules::FillBufWithPWChars(&text, 11);
+  }
+
+  if (selection) {
+    selection->InvalidateSelection();
   }
 
   // We're going to paint this text so we need to ensure bidi is enabled if
   // necessary
   CheckTextForBidi(text);
 
   DrawResult result = DrawResult::SUCCESS;
 
--- a/toolkit/components/passwordmgr/content/passwordManager.js
+++ b/toolkit/components/passwordmgr/content/passwordManager.js
@@ -284,20 +284,18 @@ function LoadSignons() {
   // SignonColumnSort) assumes we want to toggle the sort
   // direction but here we don't so we have to trick it
   lastSignonSortAscending = !lastSignonSortAscending;
   SignonColumnSort(lastSignonSortColumn);
 
   // disable "remove all signons" button if there are no signons
   if (signons.length == 0) {
     removeAllButton.setAttribute("disabled", "true");
-    togglePasswordsButton.setAttribute("disabled", "true");
   } else {
     removeAllButton.removeAttribute("disabled");
-    togglePasswordsButton.removeAttribute("disabled");
   }
 
   return true;
 }
 
 function GetVisibleLogins() {
   return signonsTreeView._filterSet.length ? signonsTreeView._filterSet : signons;
 }
@@ -341,18 +339,20 @@ function RestoreSelections(selectedSigno
     }
   }
 }
 
 function SignonSelected() {
   let selections = GetTreeSelections();
   if (selections.length) {
     removeButton.removeAttribute("disabled");
+    togglePasswordsButton.removeAttribute("disabled");
   } else {
     removeButton.setAttribute("disabled", true);
+    togglePasswordsButton.setAttribute("disabled", true);
   }
 }
 
 function DeleteSignon() {
   let syncNeeded = (signonsTreeView._filterSet.length != 0);
   let tree = signonsTree;
   let view = signonsTreeView;
   let table = GetVisibleLogins();
@@ -436,17 +436,21 @@ function DeleteAllSignons() {
   Services.telemetry.getHistogramById("PWMGR_MANAGE_DELETED_ALL").add(1);
 }
 
 function TogglePasswordVisible() {
   if (showingPasswords || masterPasswordLogin(AskUserShowPasswords)) {
     showingPasswords = !showingPasswords;
     togglePasswordsButton.label = kSignonBundle.getString(showingPasswords ? "hidePasswords" : "showPasswords");
     togglePasswordsButton.accessKey = kSignonBundle.getString(showingPasswords ? "hidePasswordsAccessKey" : "showPasswordsAccessKey");
-    document.getElementById("passwordCol").hidden = !showingPasswords;
+    if (showingPasswords) {
+      signonsTree.setAttribute("revealselectedpasswords", "true");
+    } else {
+      signonsTree.removeAttribute("revealselectedpasswords");
+    }
     FilterPasswords();
   }
 
   // Notify observers that the password visibility toggling is
   // completed.  (Mostly useful for tests)
   Services.obs.notifyObservers(null, "passwordmgr-password-toggle-complete", null);
   Services.telemetry.getHistogramById("PWMGR_MANAGE_VISIBILITY_TOGGLED").add(showingPasswords);
 }
--- a/toolkit/components/passwordmgr/content/passwordManager.xul
+++ b/toolkit/components/passwordmgr/content/passwordManager.xul
@@ -77,19 +77,18 @@
                  ignoreincolumnpicker="true"
                  sortDirection="ascending"/>
         <splitter class="tree-splitter"/>
         <treecol id="userCol" label="&treehead.username.label;" flex="25"
                  ignoreincolumnpicker="true"
                  data-field-name="username" persist="width"/>
         <splitter class="tree-splitter"/>
         <treecol id="passwordCol" label="&treehead.password.label;" flex="15"
-                 ignoreincolumnpicker="true"
-                 data-field-name="password" persist="width"
-                 hidden="true"/>
+                 ignoreincolumnpicker="true" type="password"
+                 data-field-name="password" persist="width"/>
         <splitter class="tree-splitter"/>
         <treecol id="timeCreatedCol" label="&treehead.timeCreated.label;" flex="10"
                  data-field-name="timeCreated" persist="width hidden"
                  hidden="true"/>
         <splitter class="tree-splitter"/>
         <treecol id="timeLastUsedCol" label="&treehead.timeLastUsed.label;" flex="20"
                  data-field-name="timeLastUsed" persist="width hidden"
                  hidden="true"/>
@@ -113,17 +112,17 @@
               label="&removeall.label;" accesskey="&removeall.accesskey;"
               oncommand="DeleteAllSignons();"/>
       <spacer flex="1"/>
 #if defined(MOZ_BUILD_APP_IS_BROWSER) && defined(XP_WIN)
       <button accesskey="&import.accesskey;"
               label="&import.label;"
               oncommand="OpenMigrator();"/>
 #endif
-      <button id="togglePasswords"
+      <button id="togglePasswords" disabled="true"
               oncommand="TogglePasswordVisible();"/>
     </hbox>
   </vbox>
   <hbox align="end">
     <hbox class="actionButtons" flex="1">
       <spacer flex="1"/>
 #ifndef XP_MACOSX
       <button oncommand="close();" icon="close"
--- a/toolkit/components/passwordmgr/test/browser/browser_passwordmgr_contextmenu.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_passwordmgr_contextmenu.js
@@ -44,32 +44,32 @@ add_task(function* test() {
             assertMenuitemEnabled("copypassword", false);
             assertMenuitemEnabled("editpassword", false);
 
             info("Select the first row (with an empty username)");
             selection.select(0);
             assertMenuitemEnabled("copyusername", false, "empty username");
             assertMenuitemEnabled("editusername", true);
             assertMenuitemEnabled("copypassword", true);
-            assertMenuitemEnabled("editpassword", false, "password column hidden");
+            assertMenuitemEnabled("editpassword", true);
 
             info("Clear the selection");
             selection.clearSelection();
             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("copyusername", true);
             assertMenuitemEnabled("editusername", true);
             assertMenuitemEnabled("copypassword", true);
-            assertMenuitemEnabled("editpassword", true, "password column visible");
+            assertMenuitemEnabled("editpassword", true);
             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 : ""));
--- a/toolkit/components/passwordmgr/test/browser/browser_passwordmgr_sort.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_passwordmgr_sort.js
@@ -90,18 +90,22 @@ add_task(function* test() {
             }
 
             Services.obs.addObserver(function(aSubject, aTopic, aData) {
                 if (aTopic == "passwordmgr-password-toggle-complete") {
                     Services.obs.removeObserver(arguments.callee, aTopic);
                     func();
                 }
             }, "passwordmgr-password-toggle-complete", false);
-
-            EventUtils.synthesizeMouse(toggleButton, 1, 1, {}, win);
+            function handleSelect() {
+              sTree.removeEventListener('select', handleSelect);
+              EventUtils.synthesizeMouse(toggleButton, 1, 1, {}, win);
+            }
+            sTree.addEventListener('select', handleSelect);
+            sTree.view.selection.rangedSelect(0, 9, true);
         }
 
         function clickCol(col) {
             EventUtils.synthesizeMouse(col, 20, 1, {}, win);
             setTimeout(runNextTest, 0);
         }
 
         function setFilter(string) {
--- a/toolkit/components/passwordmgr/test/browser/browser_passwordmgrdlg.js
+++ b/toolkit/components/passwordmgr/test/browser/browser_passwordmgrdlg.js
@@ -148,17 +148,22 @@ add_task(function* test() {
                     runNextTest();
                 else
                     endFunction();
             }
 
             function runNextTest() {
                 let testCase = tests[testCounter++];
                 setFilter(testCase.filter);
-                setTimeout(runOneTest, 0, testCase);
+                function handleSelect() {
+                  tree.removeEventListener('select', handleSelect);
+                  setTimeout(runOneTest, 0, testCase);
+                }
+                tree.addEventListener('select', handleSelect);
+                view.selection.rangedSelect(0, 9, true);
             }
 
             runNextTest();
         }
 
         function step1() {
             runTests(1, step2);
         }