1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 cr
.define('options', function() {
6 /** @const */ var Page
= cr
.ui
.pageManager
.Page
;
7 /** @const */ var PageManager
= cr
.ui
.pageManager
.PageManager
;
8 /** @const */ var ArrayDataModel
= cr
.ui
.ArrayDataModel
;
10 /////////////////////////////////////////////////////////////////////////////
11 // PasswordManager class:
14 * Encapsulated handling of password and exceptions page.
16 * @extends {cr.ui.pageManager.Page}
18 function PasswordManager() {
19 this.activeNavTab
= null;
20 Page
.call(this, 'passwords',
21 loadTimeData
.getString('passwordsPageTabTitle'),
25 cr
.addSingletonGetter(PasswordManager
);
27 PasswordManager
.prototype = {
28 __proto__
: Page
.prototype,
31 * The saved passwords list.
32 * @type {options.DeletableItemList}
35 savedPasswordsList_
: null,
38 * The password exceptions list.
39 * @type {options.DeletableItemList}
42 passwordExceptionsList_
: null,
45 * The timer id of the timer set on search query change events.
49 queryDelayTimerId_
: 0,
52 * The most recent search query, or null if the query is empty.
59 initializePage: function() {
60 Page
.prototype.initializePage
.call(this);
62 $('auto-signin-block').hidden
=
63 !loadTimeData
.getBoolean('enableCredentialManagerAPI');
65 $('password-manager-confirm').onclick = function() {
66 PageManager
.closeOverlay();
69 $('password-search-box').addEventListener('search',
70 this.handleSearchQueryChange_
.bind(this));
72 $('exceptions-learn-more').onclick = function() {
73 chrome
.send('coreOptionsUserMetricsAction',
74 ['Options_PasswordManagerExceptionsLearnMore']);
75 return true; // Always follow the href
78 this.createSavedPasswordsList_();
79 this.createPasswordExceptionsList_();
83 canShowPage: function() {
84 return !(cr
.isChromeOS
&& UIAccountTweaks
.loggedInAsGuest());
88 didShowPage: function() {
89 // Updating the password lists may cause a blocking platform dialog pop up
90 // (Mac, Linux), so we delay this operation until the page is shown.
91 chrome
.send('updatePasswordLists');
92 $('password-search-box').focus();
96 * Creates, decorates and initializes the saved passwords list.
99 createSavedPasswordsList_: function() {
100 var savedPasswordsList
= $('saved-passwords-list');
101 options
.passwordManager
.PasswordsList
.decorate(savedPasswordsList
);
102 this.savedPasswordsList_
= assertInstanceof(savedPasswordsList
,
103 options
.DeletableItemList
);
104 this.savedPasswordsList_
.autoExpands
= true;
108 * Creates, decorates and initializes the password exceptions list.
111 createPasswordExceptionsList_: function() {
112 var passwordExceptionsList
= $('password-exceptions-list');
113 options
.passwordManager
.PasswordExceptionsList
.decorate(
114 passwordExceptionsList
);
115 this.passwordExceptionsList_
= assertInstanceof(passwordExceptionsList
,
116 options
.DeletableItemList
);
117 this.passwordExceptionsList_
.autoExpands
= true;
121 * Handles search query changes.
122 * @param {!Event} e The event object.
125 handleSearchQueryChange_: function(e
) {
126 if (this.queryDelayTimerId_
)
127 window
.clearTimeout(this.queryDelayTimerId_
);
129 // Searching cookies uses a timeout of 500ms. We use a shorter timeout
130 // because there are probably fewer passwords and we want the UI to be
131 // snappier since users will expect that it's "less work."
132 this.queryDelayTimerId_
= window
.setTimeout(
133 this.searchPasswords_
.bind(this), 250);
135 chrome
.send('coreOptionsUserMetricsAction',
136 ['Options_PasswordManagerSearch']);
140 * Search passwords using text in |password-search-box|.
143 searchPasswords_: function() {
144 this.queryDelayTimerId_
= 0;
145 var filter
= $('password-search-box').value
;
146 filter
= (filter
== '') ? null : filter
;
147 if (this.lastQuery_
!= filter
) {
148 this.lastQuery_
= filter
;
149 // Searching for passwords has the side effect of requerying the
150 // underlying password store. This is done intentionally, as on OS X and
151 // Linux they can change from outside and we won't be notified of it.
152 chrome
.send('updatePasswordLists');
157 * Updates the visibility of the list and empty list placeholder.
158 * @param {!cr.ui.List} list The list to toggle visilibility for.
160 updateListVisibility_: function(list
) {
161 var empty
= list
.dataModel
.length
== 0;
162 var listPlaceHolderID
= list
.id
+ '-empty-placeholder';
164 $(listPlaceHolderID
).hidden
= !empty
;
168 * Updates the data model for the saved passwords list with the values from
170 * @param {!Array} entries The list of saved password data.
172 setSavedPasswordsList_: function(entries
) {
173 if (this.lastQuery_
) {
174 // Implement password searching here in javascript, rather than in C++.
175 // The number of saved passwords shouldn't be too big for us to handle.
176 var query
= this.lastQuery_
;
177 var filter = function(entry
, index
, list
) {
178 // Search both URL and username.
179 if (entry
[0].toLowerCase().indexOf(query
.toLowerCase()) >= 0 ||
180 entry
[1].toLowerCase().indexOf(query
.toLowerCase()) >= 0) {
181 // Keep the original index so we can delete correctly. See also
182 // deleteItemAtIndex() in password_manager_list.js that uses this.
188 entries
= entries
.filter(filter
);
190 this.savedPasswordsList_
.dataModel
= new ArrayDataModel(entries
);
191 this.updateListVisibility_(this.savedPasswordsList_
);
195 * Updates the data model for the password exceptions list with the values
197 * @param {!Array} entries The list of password exception data.
199 setPasswordExceptionsList_: function(entries
) {
200 this.passwordExceptionsList_
.dataModel
= new ArrayDataModel(entries
);
201 this.updateListVisibility_(this.passwordExceptionsList_
);
205 * Reveals the password for a saved password entry. This is called by the
206 * backend after it has authenticated the user.
207 * @param {number} index The original index of the entry in the model.
208 * @param {string} password The saved password.
210 showPassword_: function(index
, password
) {
211 var model
= this.savedPasswordsList_
.dataModel
;
212 if (this.lastQuery_
) {
213 // When a filter is active, |index| does not represent the current
214 // index in the model, but each entry stores its original index, so
215 // we can find the item using a linear search.
216 for (var i
= 0; i
< model
.length
; ++i
) {
217 if (model
.item(i
)[4] == index
) {
224 // Reveal the password in the UI.
225 var item
= this.savedPasswordsList_
.getListItemByIndex(index
);
226 item
.showPassword(password
);
230 * @param {boolean} visible Whether the link should be visible.
233 setManageAccountLinkVisibility_: function(visible
) {
234 $('manage-passwords-span').hidden
= !visible
;
239 * Removes a saved password.
240 * @param {number} rowIndex indicating the row to remove.
242 PasswordManager
.removeSavedPassword = function(rowIndex
) {
243 chrome
.send('removeSavedPassword', [String(rowIndex
)]);
244 chrome
.send('coreOptionsUserMetricsAction',
245 ['Options_PasswordManagerDeletePassword']);
249 * Removes a password exception.
250 * @param {number} rowIndex indicating the row to remove.
252 PasswordManager
.removePasswordException = function(rowIndex
) {
253 chrome
.send('removePasswordException', [String(rowIndex
)]);
256 PasswordManager
.requestShowPassword = function(index
) {
257 chrome
.send('requestShowPassword', [index
]);
260 // Forward public APIs to private implementations on the singleton instance.
261 cr
.makePublic(PasswordManager
, [
262 'setManageAccountLinkVisibility',
263 'setSavedPasswordsList',
264 'setPasswordExceptionsList',
270 PasswordManager
: PasswordManager