toolkit/components/passwordmgr/content/passwordManager.js

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:d5081f93814b
1 // -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 /*** =================== SAVED SIGNONS CODE =================== ***/
8
9 var kSignonBundle;
10 var showingPasswords = false;
11
12 function SignonsStartup() {
13 kSignonBundle = document.getElementById("signonBundle");
14 document.getElementById("togglePasswords").label = kSignonBundle.getString("showPasswords");
15 document.getElementById("togglePasswords").accessKey = kSignonBundle.getString("showPasswordsAccessKey");
16 document.getElementById("signonsIntro").textContent = kSignonBundle.getString("loginsSpielAll");
17 LoadSignons();
18
19 // filter the table if requested by caller
20 if (window.arguments &&
21 window.arguments[0] &&
22 window.arguments[0].filterString)
23 setFilter(window.arguments[0].filterString);
24
25 FocusFilterBox();
26 }
27
28 function setFilter(aFilterString) {
29 document.getElementById("filter").value = aFilterString;
30 _filterPasswords();
31 }
32
33 var signonsTreeView = {
34 _filterSet : [],
35 _lastSelectedRanges : [],
36 selection: null,
37
38 rowCount : 0,
39 setTree : function(tree) {},
40 getImageSrc : function(row,column) {},
41 getProgressMode : function(row,column) {},
42 getCellValue : function(row,column) {},
43 getCellText : function(row,column) {
44 var signon = this._filterSet.length ? this._filterSet[row] : signons[row];
45 switch (column.id) {
46 case "siteCol":
47 return signon.httpRealm ?
48 (signon.hostname + " (" + signon.httpRealm + ")"):
49 signon.hostname;
50 case "userCol":
51 return signon.username || "";
52 case "passwordCol":
53 return signon.password || "";
54 default:
55 return "";
56 }
57 },
58 isSeparator : function(index) { return false; },
59 isSorted : function() { return false; },
60 isContainer : function(index) { return false; },
61 cycleHeader : function(column) {},
62 getRowProperties : function(row) { return ""; },
63 getColumnProperties : function(column) { return ""; },
64 getCellProperties : function(row,column) {
65 if (column.element.getAttribute("id") == "siteCol")
66 return "ltr";
67
68 return "";
69 }
70 };
71
72
73 function LoadSignons() {
74 // loads signons into table
75 try {
76 signons = passwordmanager.getAllLogins();
77 } catch (e) {
78 signons = [];
79 }
80 signonsTreeView.rowCount = signons.length;
81
82 // sort and display the table
83 signonsTree.treeBoxObject.view = signonsTreeView;
84 // The sort column didn't change. SortTree (called by
85 // SignonColumnSort) assumes we want to toggle the sort
86 // direction but here we don't so we have to trick it
87 lastSignonSortAscending = !lastSignonSortAscending;
88 SignonColumnSort(lastSignonSortColumn);
89
90 // disable "remove all signons" button if there are no signons
91 var element = document.getElementById("removeAllSignons");
92 var toggle = document.getElementById("togglePasswords");
93 if (signons.length == 0) {
94 element.setAttribute("disabled","true");
95 toggle.setAttribute("disabled","true");
96 } else {
97 element.removeAttribute("disabled");
98 toggle.removeAttribute("disabled");
99 }
100
101 return true;
102 }
103
104 function SignonSelected() {
105 var selections = GetTreeSelections(signonsTree);
106 if (selections.length) {
107 document.getElementById("removeSignon").removeAttribute("disabled");
108 }
109 }
110
111 function DeleteSignon() {
112 var syncNeeded = (signonsTreeView._filterSet.length != 0);
113 DeleteSelectedItemFromTree(signonsTree, signonsTreeView,
114 signonsTreeView._filterSet.length ? signonsTreeView._filterSet : signons,
115 deletedSignons, "removeSignon", "removeAllSignons");
116 FinalizeSignonDeletions(syncNeeded);
117 }
118
119 function DeleteAllSignons() {
120 var prompter = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
121 .getService(Components.interfaces.nsIPromptService);
122
123 // Confirm the user wants to remove all passwords
124 var dummy = { value: false };
125 if (prompter.confirmEx(window,
126 kSignonBundle.getString("removeAllPasswordsTitle"),
127 kSignonBundle.getString("removeAllPasswordsPrompt"),
128 prompter.STD_YES_NO_BUTTONS + prompter.BUTTON_POS_1_DEFAULT,
129 null, null, null, null, dummy) == 1) // 1 == "No" button
130 return;
131
132 var syncNeeded = (signonsTreeView._filterSet.length != 0);
133 DeleteAllFromTree(signonsTree, signonsTreeView,
134 signonsTreeView._filterSet.length ? signonsTreeView._filterSet : signons,
135 deletedSignons, "removeSignon", "removeAllSignons");
136 FinalizeSignonDeletions(syncNeeded);
137 }
138
139 function TogglePasswordVisible() {
140 if (showingPasswords || masterPasswordLogin(AskUserShowPasswords)) {
141 showingPasswords = !showingPasswords;
142 document.getElementById("togglePasswords").label = kSignonBundle.getString(showingPasswords ? "hidePasswords" : "showPasswords");
143 document.getElementById("togglePasswords").accessKey = kSignonBundle.getString(showingPasswords ? "hidePasswordsAccessKey" : "showPasswordsAccessKey");
144 document.getElementById("passwordCol").hidden = !showingPasswords;
145 _filterPasswords();
146 }
147
148 // Notify observers that the password visibility toggling is
149 // completed. (Mostly useful for tests)
150 Components.classes["@mozilla.org/observer-service;1"]
151 .getService(Components.interfaces.nsIObserverService)
152 .notifyObservers(null, "passwordmgr-password-toggle-complete", null);
153 }
154
155 function AskUserShowPasswords() {
156 var prompter = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService);
157 var dummy = { value: false };
158
159 // Confirm the user wants to display passwords
160 return prompter.confirmEx(window,
161 null,
162 kSignonBundle.getString("noMasterPasswordPrompt"), prompter.STD_YES_NO_BUTTONS,
163 null, null, null, null, dummy) == 0; // 0=="Yes" button
164 }
165
166 function FinalizeSignonDeletions(syncNeeded) {
167 for (var s=0; s<deletedSignons.length; s++) {
168 passwordmanager.removeLogin(deletedSignons[s]);
169 }
170 // If the deletion has been performed in a filtered view, reflect the deletion in the unfiltered table.
171 // See bug 405389.
172 if (syncNeeded) {
173 try {
174 signons = passwordmanager.getAllLogins();
175 } catch (e) {
176 signons = [];
177 }
178 }
179 deletedSignons.length = 0;
180 }
181
182 function HandleSignonKeyPress(e) {
183 if (e.keyCode == 46) {
184 DeleteSignon();
185 }
186 }
187
188 function getColumnByName(column) {
189 switch (column) {
190 case "hostname":
191 return document.getElementById("siteCol");
192 case "username":
193 return document.getElementById("userCol");
194 case "password":
195 return document.getElementById("passwordCol");
196 }
197 }
198
199 var lastSignonSortColumn = "hostname";
200 var lastSignonSortAscending = true;
201
202 function SignonColumnSort(column) {
203 // clear out the sortDirection attribute on the old column
204 var lastSortedCol = getColumnByName(lastSignonSortColumn);
205 lastSortedCol.removeAttribute("sortDirection");
206
207 // sort
208 lastSignonSortAscending =
209 SortTree(signonsTree, signonsTreeView,
210 signonsTreeView._filterSet.length ? signonsTreeView._filterSet : signons,
211 column, lastSignonSortColumn, lastSignonSortAscending);
212 lastSignonSortColumn = column;
213
214 // set the sortDirection attribute to get the styling going
215 // first we need to get the right element
216 var sortedCol = getColumnByName(column);
217 sortedCol.setAttribute("sortDirection", lastSignonSortAscending ?
218 "ascending" : "descending");
219 }
220
221 function SignonClearFilter() {
222 var singleSelection = (signonsTreeView.selection.count == 1);
223
224 // Clear the Tree Display
225 signonsTreeView.rowCount = 0;
226 signonsTree.treeBoxObject.rowCountChanged(0, -signonsTreeView._filterSet.length);
227 signonsTreeView._filterSet = [];
228
229 // Just reload the list to make sure deletions are respected
230 LoadSignons();
231
232 // Restore selection
233 if (singleSelection) {
234 signonsTreeView.selection.clearSelection();
235 for (let i = 0; i < signonsTreeView._lastSelectedRanges.length; ++i) {
236 var range = signonsTreeView._lastSelectedRanges[i];
237 signonsTreeView.selection.rangedSelect(range.min, range.max, true);
238 }
239 } else {
240 signonsTreeView.selection.select(0);
241 }
242 signonsTreeView._lastSelectedRanges = [];
243
244 document.getElementById("signonsIntro").textContent = kSignonBundle.getString("loginsSpielAll");
245 }
246
247 function FocusFilterBox() {
248 var filterBox = document.getElementById("filter");
249 if (filterBox.getAttribute("focused") != "true")
250 filterBox.focus();
251 }
252
253 function SignonMatchesFilter(aSignon, aFilterValue) {
254 if (aSignon.hostname.toLowerCase().indexOf(aFilterValue) != -1)
255 return true;
256 if (aSignon.username &&
257 aSignon.username.toLowerCase().indexOf(aFilterValue) != -1)
258 return true;
259 if (aSignon.httpRealm &&
260 aSignon.httpRealm.toLowerCase().indexOf(aFilterValue) != -1)
261 return true;
262 if (showingPasswords && aSignon.password &&
263 aSignon.password.toLowerCase().indexOf(aFilterValue) != -1)
264 return true;
265
266 return false;
267 }
268
269 function FilterPasswords(aFilterValue, view) {
270 aFilterValue = aFilterValue.toLowerCase();
271 return signons.filter(function (s) SignonMatchesFilter(s, aFilterValue));
272 }
273
274 function SignonSaveState() {
275 // Save selection
276 var seln = signonsTreeView.selection;
277 signonsTreeView._lastSelectedRanges = [];
278 var rangeCount = seln.getRangeCount();
279 for (var i = 0; i < rangeCount; ++i) {
280 var min = {}; var max = {};
281 seln.getRangeAt(i, min, max);
282 signonsTreeView._lastSelectedRanges.push({ min: min.value, max: max.value });
283 }
284 }
285
286 function _filterPasswords()
287 {
288 var filter = document.getElementById("filter").value;
289 if (filter == "") {
290 SignonClearFilter();
291 return;
292 }
293
294 var newFilterSet = FilterPasswords(filter, signonsTreeView);
295 if (!signonsTreeView._filterSet.length) {
296 // Save Display Info for the Non-Filtered mode when we first
297 // enter Filtered mode.
298 SignonSaveState();
299 }
300 signonsTreeView._filterSet = newFilterSet;
301
302 // Clear the display
303 let oldRowCount = signonsTreeView.rowCount;
304 signonsTreeView.rowCount = 0;
305 signonsTree.treeBoxObject.rowCountChanged(0, -oldRowCount);
306 // Set up the filtered display
307 signonsTreeView.rowCount = signonsTreeView._filterSet.length;
308 signonsTree.treeBoxObject.rowCountChanged(0, signonsTreeView.rowCount);
309
310 // if the view is not empty then select the first item
311 if (signonsTreeView.rowCount > 0)
312 signonsTreeView.selection.select(0);
313
314 document.getElementById("signonsIntro").textContent = kSignonBundle.getString("loginsSpielFiltered");
315 }
316
317 function CopyPassword() {
318 // Don't copy passwords if we aren't already showing the passwords & a master
319 // password hasn't been entered.
320 if (!showingPasswords && !masterPasswordLogin())
321 return;
322 // Copy selected signon's password to clipboard
323 var clipboard = Components.classes["@mozilla.org/widget/clipboardhelper;1"].
324 getService(Components.interfaces.nsIClipboardHelper);
325 var row = document.getElementById("signonsTree").currentIndex;
326 var password = signonsTreeView.getCellText(row, {id : "passwordCol" });
327 clipboard.copyString(password, document);
328 }
329
330 function CopyUsername() {
331 // Copy selected signon's username to clipboard
332 var clipboard = Components.classes["@mozilla.org/widget/clipboardhelper;1"].
333 getService(Components.interfaces.nsIClipboardHelper);
334 var row = document.getElementById("signonsTree").currentIndex;
335 var username = signonsTreeView.getCellText(row, {id : "userCol" });
336 clipboard.copyString(username);
337 }
338
339 function UpdateCopyPassword() {
340 var singleSelection = (signonsTreeView.selection.count == 1);
341 var passwordMenuitem = document.getElementById("context-copypassword");
342 var usernameMenuitem = document.getElementById("context-copyusername");
343 if (singleSelection) {
344 usernameMenuitem.removeAttribute("disabled");
345 passwordMenuitem.removeAttribute("disabled");
346 } else {
347 usernameMenuitem.setAttribute("disabled", "true");
348 passwordMenuitem.setAttribute("disabled", "true");
349 }
350 }
351
352 function masterPasswordLogin(noPasswordCallback) {
353 // This doesn't harm if passwords are not encrypted
354 var tokendb = Components.classes["@mozilla.org/security/pk11tokendb;1"]
355 .createInstance(Components.interfaces.nsIPK11TokenDB);
356 var token = tokendb.getInternalKeyToken();
357
358 // If there is no master password, still give the user a chance to opt-out of displaying passwords
359 if (token.checkPassword(""))
360 return noPasswordCallback ? noPasswordCallback() : true;
361
362 // So there's a master password. But since checkPassword didn't succeed, we're logged out (per nsIPK11Token.idl).
363 try {
364 // Relogin and ask for the master password.
365 token.login(true); // 'true' means always prompt for token password. User will be prompted until
366 // clicking 'Cancel' or entering the correct password.
367 } catch (e) {
368 // An exception will be thrown if the user cancels the login prompt dialog.
369 // User is also logged out of Software Security Device.
370 }
371
372 return token.isLoggedIn();
373 }

mercurial