1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/components/passwordmgr/content/passwordManager.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,373 @@ 1.4 +// -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/*** =================== SAVED SIGNONS CODE =================== ***/ 1.11 + 1.12 +var kSignonBundle; 1.13 +var showingPasswords = false; 1.14 + 1.15 +function SignonsStartup() { 1.16 + kSignonBundle = document.getElementById("signonBundle"); 1.17 + document.getElementById("togglePasswords").label = kSignonBundle.getString("showPasswords"); 1.18 + document.getElementById("togglePasswords").accessKey = kSignonBundle.getString("showPasswordsAccessKey"); 1.19 + document.getElementById("signonsIntro").textContent = kSignonBundle.getString("loginsSpielAll"); 1.20 + LoadSignons(); 1.21 + 1.22 + // filter the table if requested by caller 1.23 + if (window.arguments && 1.24 + window.arguments[0] && 1.25 + window.arguments[0].filterString) 1.26 + setFilter(window.arguments[0].filterString); 1.27 + 1.28 + FocusFilterBox(); 1.29 +} 1.30 + 1.31 +function setFilter(aFilterString) { 1.32 + document.getElementById("filter").value = aFilterString; 1.33 + _filterPasswords(); 1.34 +} 1.35 + 1.36 +var signonsTreeView = { 1.37 + _filterSet : [], 1.38 + _lastSelectedRanges : [], 1.39 + selection: null, 1.40 + 1.41 + rowCount : 0, 1.42 + setTree : function(tree) {}, 1.43 + getImageSrc : function(row,column) {}, 1.44 + getProgressMode : function(row,column) {}, 1.45 + getCellValue : function(row,column) {}, 1.46 + getCellText : function(row,column) { 1.47 + var signon = this._filterSet.length ? this._filterSet[row] : signons[row]; 1.48 + switch (column.id) { 1.49 + case "siteCol": 1.50 + return signon.httpRealm ? 1.51 + (signon.hostname + " (" + signon.httpRealm + ")"): 1.52 + signon.hostname; 1.53 + case "userCol": 1.54 + return signon.username || ""; 1.55 + case "passwordCol": 1.56 + return signon.password || ""; 1.57 + default: 1.58 + return ""; 1.59 + } 1.60 + }, 1.61 + isSeparator : function(index) { return false; }, 1.62 + isSorted : function() { return false; }, 1.63 + isContainer : function(index) { return false; }, 1.64 + cycleHeader : function(column) {}, 1.65 + getRowProperties : function(row) { return ""; }, 1.66 + getColumnProperties : function(column) { return ""; }, 1.67 + getCellProperties : function(row,column) { 1.68 + if (column.element.getAttribute("id") == "siteCol") 1.69 + return "ltr"; 1.70 + 1.71 + return ""; 1.72 + } 1.73 +}; 1.74 + 1.75 + 1.76 +function LoadSignons() { 1.77 + // loads signons into table 1.78 + try { 1.79 + signons = passwordmanager.getAllLogins(); 1.80 + } catch (e) { 1.81 + signons = []; 1.82 + } 1.83 + signonsTreeView.rowCount = signons.length; 1.84 + 1.85 + // sort and display the table 1.86 + signonsTree.treeBoxObject.view = signonsTreeView; 1.87 + // The sort column didn't change. SortTree (called by 1.88 + // SignonColumnSort) assumes we want to toggle the sort 1.89 + // direction but here we don't so we have to trick it 1.90 + lastSignonSortAscending = !lastSignonSortAscending; 1.91 + SignonColumnSort(lastSignonSortColumn); 1.92 + 1.93 + // disable "remove all signons" button if there are no signons 1.94 + var element = document.getElementById("removeAllSignons"); 1.95 + var toggle = document.getElementById("togglePasswords"); 1.96 + if (signons.length == 0) { 1.97 + element.setAttribute("disabled","true"); 1.98 + toggle.setAttribute("disabled","true"); 1.99 + } else { 1.100 + element.removeAttribute("disabled"); 1.101 + toggle.removeAttribute("disabled"); 1.102 + } 1.103 + 1.104 + return true; 1.105 +} 1.106 + 1.107 +function SignonSelected() { 1.108 + var selections = GetTreeSelections(signonsTree); 1.109 + if (selections.length) { 1.110 + document.getElementById("removeSignon").removeAttribute("disabled"); 1.111 + } 1.112 +} 1.113 + 1.114 +function DeleteSignon() { 1.115 + var syncNeeded = (signonsTreeView._filterSet.length != 0); 1.116 + DeleteSelectedItemFromTree(signonsTree, signonsTreeView, 1.117 + signonsTreeView._filterSet.length ? signonsTreeView._filterSet : signons, 1.118 + deletedSignons, "removeSignon", "removeAllSignons"); 1.119 + FinalizeSignonDeletions(syncNeeded); 1.120 +} 1.121 + 1.122 +function DeleteAllSignons() { 1.123 + var prompter = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] 1.124 + .getService(Components.interfaces.nsIPromptService); 1.125 + 1.126 + // Confirm the user wants to remove all passwords 1.127 + var dummy = { value: false }; 1.128 + if (prompter.confirmEx(window, 1.129 + kSignonBundle.getString("removeAllPasswordsTitle"), 1.130 + kSignonBundle.getString("removeAllPasswordsPrompt"), 1.131 + prompter.STD_YES_NO_BUTTONS + prompter.BUTTON_POS_1_DEFAULT, 1.132 + null, null, null, null, dummy) == 1) // 1 == "No" button 1.133 + return; 1.134 + 1.135 + var syncNeeded = (signonsTreeView._filterSet.length != 0); 1.136 + DeleteAllFromTree(signonsTree, signonsTreeView, 1.137 + signonsTreeView._filterSet.length ? signonsTreeView._filterSet : signons, 1.138 + deletedSignons, "removeSignon", "removeAllSignons"); 1.139 + FinalizeSignonDeletions(syncNeeded); 1.140 +} 1.141 + 1.142 +function TogglePasswordVisible() { 1.143 + if (showingPasswords || masterPasswordLogin(AskUserShowPasswords)) { 1.144 + showingPasswords = !showingPasswords; 1.145 + document.getElementById("togglePasswords").label = kSignonBundle.getString(showingPasswords ? "hidePasswords" : "showPasswords"); 1.146 + document.getElementById("togglePasswords").accessKey = kSignonBundle.getString(showingPasswords ? "hidePasswordsAccessKey" : "showPasswordsAccessKey"); 1.147 + document.getElementById("passwordCol").hidden = !showingPasswords; 1.148 + _filterPasswords(); 1.149 + } 1.150 + 1.151 + // Notify observers that the password visibility toggling is 1.152 + // completed. (Mostly useful for tests) 1.153 + Components.classes["@mozilla.org/observer-service;1"] 1.154 + .getService(Components.interfaces.nsIObserverService) 1.155 + .notifyObservers(null, "passwordmgr-password-toggle-complete", null); 1.156 +} 1.157 + 1.158 +function AskUserShowPasswords() { 1.159 + var prompter = Components.classes["@mozilla.org/embedcomp/prompt-service;1"].getService(Components.interfaces.nsIPromptService); 1.160 + var dummy = { value: false }; 1.161 + 1.162 + // Confirm the user wants to display passwords 1.163 + return prompter.confirmEx(window, 1.164 + null, 1.165 + kSignonBundle.getString("noMasterPasswordPrompt"), prompter.STD_YES_NO_BUTTONS, 1.166 + null, null, null, null, dummy) == 0; // 0=="Yes" button 1.167 +} 1.168 + 1.169 +function FinalizeSignonDeletions(syncNeeded) { 1.170 + for (var s=0; s<deletedSignons.length; s++) { 1.171 + passwordmanager.removeLogin(deletedSignons[s]); 1.172 + } 1.173 + // If the deletion has been performed in a filtered view, reflect the deletion in the unfiltered table. 1.174 + // See bug 405389. 1.175 + if (syncNeeded) { 1.176 + try { 1.177 + signons = passwordmanager.getAllLogins(); 1.178 + } catch (e) { 1.179 + signons = []; 1.180 + } 1.181 + } 1.182 + deletedSignons.length = 0; 1.183 +} 1.184 + 1.185 +function HandleSignonKeyPress(e) { 1.186 + if (e.keyCode == 46) { 1.187 + DeleteSignon(); 1.188 + } 1.189 +} 1.190 + 1.191 +function getColumnByName(column) { 1.192 + switch (column) { 1.193 + case "hostname": 1.194 + return document.getElementById("siteCol"); 1.195 + case "username": 1.196 + return document.getElementById("userCol"); 1.197 + case "password": 1.198 + return document.getElementById("passwordCol"); 1.199 + } 1.200 +} 1.201 + 1.202 +var lastSignonSortColumn = "hostname"; 1.203 +var lastSignonSortAscending = true; 1.204 + 1.205 +function SignonColumnSort(column) { 1.206 + // clear out the sortDirection attribute on the old column 1.207 + var lastSortedCol = getColumnByName(lastSignonSortColumn); 1.208 + lastSortedCol.removeAttribute("sortDirection"); 1.209 + 1.210 + // sort 1.211 + lastSignonSortAscending = 1.212 + SortTree(signonsTree, signonsTreeView, 1.213 + signonsTreeView._filterSet.length ? signonsTreeView._filterSet : signons, 1.214 + column, lastSignonSortColumn, lastSignonSortAscending); 1.215 + lastSignonSortColumn = column; 1.216 + 1.217 + // set the sortDirection attribute to get the styling going 1.218 + // first we need to get the right element 1.219 + var sortedCol = getColumnByName(column); 1.220 + sortedCol.setAttribute("sortDirection", lastSignonSortAscending ? 1.221 + "ascending" : "descending"); 1.222 +} 1.223 + 1.224 +function SignonClearFilter() { 1.225 + var singleSelection = (signonsTreeView.selection.count == 1); 1.226 + 1.227 + // Clear the Tree Display 1.228 + signonsTreeView.rowCount = 0; 1.229 + signonsTree.treeBoxObject.rowCountChanged(0, -signonsTreeView._filterSet.length); 1.230 + signonsTreeView._filterSet = []; 1.231 + 1.232 + // Just reload the list to make sure deletions are respected 1.233 + LoadSignons(); 1.234 + 1.235 + // Restore selection 1.236 + if (singleSelection) { 1.237 + signonsTreeView.selection.clearSelection(); 1.238 + for (let i = 0; i < signonsTreeView._lastSelectedRanges.length; ++i) { 1.239 + var range = signonsTreeView._lastSelectedRanges[i]; 1.240 + signonsTreeView.selection.rangedSelect(range.min, range.max, true); 1.241 + } 1.242 + } else { 1.243 + signonsTreeView.selection.select(0); 1.244 + } 1.245 + signonsTreeView._lastSelectedRanges = []; 1.246 + 1.247 + document.getElementById("signonsIntro").textContent = kSignonBundle.getString("loginsSpielAll"); 1.248 +} 1.249 + 1.250 +function FocusFilterBox() { 1.251 + var filterBox = document.getElementById("filter"); 1.252 + if (filterBox.getAttribute("focused") != "true") 1.253 + filterBox.focus(); 1.254 +} 1.255 + 1.256 +function SignonMatchesFilter(aSignon, aFilterValue) { 1.257 + if (aSignon.hostname.toLowerCase().indexOf(aFilterValue) != -1) 1.258 + return true; 1.259 + if (aSignon.username && 1.260 + aSignon.username.toLowerCase().indexOf(aFilterValue) != -1) 1.261 + return true; 1.262 + if (aSignon.httpRealm && 1.263 + aSignon.httpRealm.toLowerCase().indexOf(aFilterValue) != -1) 1.264 + return true; 1.265 + if (showingPasswords && aSignon.password && 1.266 + aSignon.password.toLowerCase().indexOf(aFilterValue) != -1) 1.267 + return true; 1.268 + 1.269 + return false; 1.270 +} 1.271 + 1.272 +function FilterPasswords(aFilterValue, view) { 1.273 + aFilterValue = aFilterValue.toLowerCase(); 1.274 + return signons.filter(function (s) SignonMatchesFilter(s, aFilterValue)); 1.275 +} 1.276 + 1.277 +function SignonSaveState() { 1.278 + // Save selection 1.279 + var seln = signonsTreeView.selection; 1.280 + signonsTreeView._lastSelectedRanges = []; 1.281 + var rangeCount = seln.getRangeCount(); 1.282 + for (var i = 0; i < rangeCount; ++i) { 1.283 + var min = {}; var max = {}; 1.284 + seln.getRangeAt(i, min, max); 1.285 + signonsTreeView._lastSelectedRanges.push({ min: min.value, max: max.value }); 1.286 + } 1.287 +} 1.288 + 1.289 +function _filterPasswords() 1.290 +{ 1.291 + var filter = document.getElementById("filter").value; 1.292 + if (filter == "") { 1.293 + SignonClearFilter(); 1.294 + return; 1.295 + } 1.296 + 1.297 + var newFilterSet = FilterPasswords(filter, signonsTreeView); 1.298 + if (!signonsTreeView._filterSet.length) { 1.299 + // Save Display Info for the Non-Filtered mode when we first 1.300 + // enter Filtered mode. 1.301 + SignonSaveState(); 1.302 + } 1.303 + signonsTreeView._filterSet = newFilterSet; 1.304 + 1.305 + // Clear the display 1.306 + let oldRowCount = signonsTreeView.rowCount; 1.307 + signonsTreeView.rowCount = 0; 1.308 + signonsTree.treeBoxObject.rowCountChanged(0, -oldRowCount); 1.309 + // Set up the filtered display 1.310 + signonsTreeView.rowCount = signonsTreeView._filterSet.length; 1.311 + signonsTree.treeBoxObject.rowCountChanged(0, signonsTreeView.rowCount); 1.312 + 1.313 + // if the view is not empty then select the first item 1.314 + if (signonsTreeView.rowCount > 0) 1.315 + signonsTreeView.selection.select(0); 1.316 + 1.317 + document.getElementById("signonsIntro").textContent = kSignonBundle.getString("loginsSpielFiltered"); 1.318 +} 1.319 + 1.320 +function CopyPassword() { 1.321 + // Don't copy passwords if we aren't already showing the passwords & a master 1.322 + // password hasn't been entered. 1.323 + if (!showingPasswords && !masterPasswordLogin()) 1.324 + return; 1.325 + // Copy selected signon's password to clipboard 1.326 + var clipboard = Components.classes["@mozilla.org/widget/clipboardhelper;1"]. 1.327 + getService(Components.interfaces.nsIClipboardHelper); 1.328 + var row = document.getElementById("signonsTree").currentIndex; 1.329 + var password = signonsTreeView.getCellText(row, {id : "passwordCol" }); 1.330 + clipboard.copyString(password, document); 1.331 +} 1.332 + 1.333 +function CopyUsername() { 1.334 + // Copy selected signon's username to clipboard 1.335 + var clipboard = Components.classes["@mozilla.org/widget/clipboardhelper;1"]. 1.336 + getService(Components.interfaces.nsIClipboardHelper); 1.337 + var row = document.getElementById("signonsTree").currentIndex; 1.338 + var username = signonsTreeView.getCellText(row, {id : "userCol" }); 1.339 + clipboard.copyString(username); 1.340 +} 1.341 + 1.342 +function UpdateCopyPassword() { 1.343 + var singleSelection = (signonsTreeView.selection.count == 1); 1.344 + var passwordMenuitem = document.getElementById("context-copypassword"); 1.345 + var usernameMenuitem = document.getElementById("context-copyusername"); 1.346 + if (singleSelection) { 1.347 + usernameMenuitem.removeAttribute("disabled"); 1.348 + passwordMenuitem.removeAttribute("disabled"); 1.349 + } else { 1.350 + usernameMenuitem.setAttribute("disabled", "true"); 1.351 + passwordMenuitem.setAttribute("disabled", "true"); 1.352 + } 1.353 +} 1.354 + 1.355 +function masterPasswordLogin(noPasswordCallback) { 1.356 + // This doesn't harm if passwords are not encrypted 1.357 + var tokendb = Components.classes["@mozilla.org/security/pk11tokendb;1"] 1.358 + .createInstance(Components.interfaces.nsIPK11TokenDB); 1.359 + var token = tokendb.getInternalKeyToken(); 1.360 + 1.361 + // If there is no master password, still give the user a chance to opt-out of displaying passwords 1.362 + if (token.checkPassword("")) 1.363 + return noPasswordCallback ? noPasswordCallback() : true; 1.364 + 1.365 + // So there's a master password. But since checkPassword didn't succeed, we're logged out (per nsIPK11Token.idl). 1.366 + try { 1.367 + // Relogin and ask for the master password. 1.368 + token.login(true); // 'true' means always prompt for token password. User will be prompted until 1.369 + // clicking 'Cancel' or entering the correct password. 1.370 + } catch (e) { 1.371 + // An exception will be thrown if the user cancels the login prompt dialog. 1.372 + // User is also logged out of Software Security Device. 1.373 + } 1.374 + 1.375 + return token.isLoggedIn(); 1.376 +}