BookmarkManager: Fix 'new folder text field size changes on clicking it' issue.
[chromium-blink-merge.git] / chrome / browser / resources / options / password_manager.js
blob6e16ebac5e14d48e2cba966de98e8638f585fa26
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:
13   /**
14    * Encapsulated handling of password and exceptions page.
15    * @constructor
16    * @extends {cr.ui.pageManager.Page}
17    */
18   function PasswordManager() {
19     this.activeNavTab = null;
20     Page.call(this, 'passwords',
21               loadTimeData.getString('passwordsPageTabTitle'),
22               'password-manager');
23   }
25   cr.addSingletonGetter(PasswordManager);
27   PasswordManager.prototype = {
28     __proto__: Page.prototype,
30     /**
31      * The saved passwords list.
32      * @type {options.DeletableItemList}
33      * @private
34      */
35     savedPasswordsList_: null,
37     /**
38      * The password exceptions list.
39      * @type {options.DeletableItemList}
40      * @private
41      */
42     passwordExceptionsList_: null,
44     /**
45      * The timer id of the timer set on search query change events.
46      * @type {number}
47      * @private
48      */
49     queryDelayTimerId_: 0,
51     /**
52      * The most recent search query, or null if the query is empty.
53      * @type {?string}
54      * @private
55      */
56     lastQuery_: null,
58     /** @override */
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();
67       };
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
76       };
78       this.createSavedPasswordsList_();
79       this.createPasswordExceptionsList_();
80     },
82     /** @override */
83     canShowPage: function() {
84       return !(cr.isChromeOS && UIAccountTweaks.loggedInAsGuest());
85     },
87     /** @override */
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();
93     },
95     /**
96      * Creates, decorates and initializes the saved passwords list.
97      * @private
98      */
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;
105     },
107     /**
108      * Creates, decorates and initializes the password exceptions list.
109      * @private
110      */
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;
118     },
120     /**
121      * Handles search query changes.
122      * @param {!Event} e The event object.
123      * @private
124      */
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']);
137     },
139     /**
140      * Search passwords using text in |password-search-box|.
141      * @private
142      */
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');
153       }
154     },
156     /**
157      * Updates the visibility of the list and empty list placeholder.
158      * @param {!cr.ui.List} list The list to toggle visilibility for.
159      */
160     updateListVisibility_: function(list) {
161       var empty = list.dataModel.length == 0;
162       var listPlaceHolderID = list.id + '-empty-placeholder';
163       list.hidden = empty;
164       $(listPlaceHolderID).hidden = !empty;
165     },
167     /**
168      * Updates the data model for the saved passwords list with the values from
169      * |entries|.
170      * @param {!Array} entries The list of saved password data.
171      */
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.
183             entry[4] = index;
184             return true;
185           }
186           return false;
187         };
188         entries = entries.filter(filter);
189       }
190       this.savedPasswordsList_.dataModel = new ArrayDataModel(entries);
191       this.updateListVisibility_(this.savedPasswordsList_);
192     },
194     /**
195      * Updates the data model for the password exceptions list with the values
196      * from |entries|.
197      * @param {!Array} entries The list of password exception data.
198      */
199     setPasswordExceptionsList_: function(entries) {
200       this.passwordExceptionsList_.dataModel = new ArrayDataModel(entries);
201       this.updateListVisibility_(this.passwordExceptionsList_);
202     },
204     /**
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.
209      */
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) {
218             index = i;
219             break;
220           }
221         }
222       }
224       // Reveal the password in the UI.
225       var item = this.savedPasswordsList_.getListItemByIndex(index);
226       item.showPassword(password);
227     },
229     /**
230      * @param {boolean} visible Whether the link should be visible.
231      * @private
232      */
233     setManageAccountLinkVisibility_: function(visible) {
234       $('manage-passwords-span').hidden = !visible;
235     },
236   };
238   /**
239    * Removes a saved password.
240    * @param {number} rowIndex indicating the row to remove.
241    */
242   PasswordManager.removeSavedPassword = function(rowIndex) {
243       chrome.send('removeSavedPassword', [String(rowIndex)]);
244       chrome.send('coreOptionsUserMetricsAction',
245                   ['Options_PasswordManagerDeletePassword']);
246   };
248   /**
249    * Removes a password exception.
250    * @param {number} rowIndex indicating the row to remove.
251    */
252   PasswordManager.removePasswordException = function(rowIndex) {
253       chrome.send('removePasswordException', [String(rowIndex)]);
254   };
256   PasswordManager.requestShowPassword = function(index) {
257     chrome.send('requestShowPassword', [index]);
258   };
260   // Forward public APIs to private implementations on the singleton instance.
261   cr.makePublic(PasswordManager, [
262     'setManageAccountLinkVisibility',
263     'setSavedPasswordsList',
264     'setPasswordExceptionsList',
265     'showPassword'
266   ]);
268   // Export
269   return {
270     PasswordManager: PasswordManager
271   };