BookmarkManager: Fix 'new folder text field size changes on clicking it' issue.
[chromium-blink-merge.git] / chrome / browser / resources / options / password_manager_list.js
blobf8ed9e8a5fdc5423ac10318d6bacaf1bb2450d75
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.passwordManager', function() {
6   /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel;
7   /** @const */ var DeletableItemList = options.DeletableItemList;
8   /** @const */ var DeletableItem = options.DeletableItem;
9   /** @const */ var List = cr.ui.List;
11   /** @const */ var URL_DATA_INDEX = 0;
12   /** @const */ var USERNAME_DATA_INDEX = 1;
13   /** @const */ var PASSWORD_DATA_INDEX = 2;
14   /** @const */ var FEDERATION_DATA_INDEX = 3;
15   /** @const */ var ORIGINAL_DATA_INDEX = 4;
17   /**
18    * Creates a new passwords list item.
19    * @param {cr.ui.ArrayDataModel} dataModel The data model that contains this
20    *     item.
21    * @param {Array} entry An array of the form [url, username, password,
22    *     federation]. When the list has been filtered, a fifth element [index]
23    *     may be present.
24    * @param {boolean} showPasswords If true, add a button to the element to
25    *     allow the user to reveal the saved password.
26    * @constructor
27    * @extends {options.DeletableItem}
28    */
29   function PasswordListItem(dataModel, entry, showPasswords) {
30     var el = cr.doc.createElement('div');
31     el.dataItem = entry;
32     el.dataModel = dataModel;
33     el.__proto__ = PasswordListItem.prototype;
34     el.showPasswords_ = showPasswords;
35     el.decorate();
37     return el;
38   }
40   PasswordListItem.prototype = {
41     __proto__: DeletableItem.prototype,
43     /** @override */
44     decorate: function() {
45       DeletableItem.prototype.decorate.call(this);
47       // The URL of the site.
48       var urlLabel = this.ownerDocument.createElement('div');
49       urlLabel.classList.add('favicon-cell');
50       urlLabel.classList.add('weakrtl');
51       urlLabel.classList.add('url');
52       urlLabel.setAttribute('title', this.url);
53       urlLabel.textContent = this.url;
55       // The favicon URL is prefixed with "origin/", which essentially removes
56       // the URL path past the top-level domain and ensures that a scheme (e.g.,
57       // http) is being used. This ensures that the favicon returned is the
58       // default favicon for the domain and that the URL has a scheme if none
59       // is present in the password manager.
60       urlLabel.style.backgroundImage = getFaviconImageSet(
61           'origin/' + this.url, 16);
62       this.contentElement.appendChild(urlLabel);
64       // The stored username.
65       var usernameLabel = this.ownerDocument.createElement('div');
66       usernameLabel.className = 'name';
67       usernameLabel.textContent = this.username;
68       usernameLabel.title = this.username;
69       this.contentElement.appendChild(usernameLabel);
71       if (this.federation) {
72         // The federation.
73         var federationDiv = this.ownerDocument.createElement('div');
74         federationDiv.className = 'federation';
75         federationDiv.textContent = this.federation;
76         this.contentElement.appendChild(federationDiv);
77       } else {
78         // The stored password.
79         var passwordInputDiv = this.ownerDocument.createElement('div');
80         passwordInputDiv.className = 'password';
82         // The password input field.
83         var passwordInput = this.ownerDocument.createElement('input');
84         passwordInput.type = 'password';
85         passwordInput.className = 'inactive-password';
86         passwordInput.readOnly = true;
87         passwordInput.value = this.showPasswords_ ? this.password : '********';
88         passwordInputDiv.appendChild(passwordInput);
89         var deletableItem = this;
90         passwordInput.addEventListener('focus', function() {
91           deletableItem.handleFocus();
92         });
93         this.passwordField = passwordInput;
94         this.setFocusable_(false);
96         // The show/hide button.
97         if (this.showPasswords_) {
98           var button = this.ownerDocument.createElement('button');
99           button.hidden = true;
100           button.className = 'list-inline-button custom-appearance';
101           button.textContent = loadTimeData.getString('passwordShowButton');
102           button.addEventListener('click', this.onClick_.bind(this), true);
103           button.addEventListener('mousedown', function(event) {
104             // Don't focus on this button by mousedown.
105             event.preventDefault();
106             // Don't handle list item selection. It causes focus change.
107             event.stopPropagation();
108           }, false);
109           button.addEventListener('focus', function() {
110             deletableItem.handleFocus();
111           });
112           passwordInputDiv.appendChild(button);
113           this.passwordShowButton = button;
114         }
115         this.contentElement.appendChild(passwordInputDiv);
116       }
118     },
120     /** @override */
121     selectionChanged: function() {
122       var input = this.passwordField;
123       var button = this.passwordShowButton;
124       // The button doesn't exist when passwords can't be shown.
125       if (!button)
126         return;
128       if (this.selected) {
129         input.classList.remove('inactive-password');
130         this.setFocusable_(true);
131         button.hidden = false;
132         input.focus();
133       } else {
134         input.classList.add('inactive-password');
135         this.setFocusable_(false);
136         button.hidden = true;
137       }
138     },
140     /**
141      * Set the focusability of this row.
142      * @param {boolean} focusable
143      * @private
144      */
145     setFocusable_: function(focusable) {
146       var tabIndex = focusable ? 0 : -1;
147       this.passwordField.tabIndex = this.closeButtonElement.tabIndex = tabIndex;
148     },
150     /**
151      * Reveals the plain text password of this entry.
152      */
153     showPassword: function(password) {
154       this.passwordField.value = password;
155       this.passwordField.type = 'text';
157       var button = this.passwordShowButton;
158       if (button)
159         button.textContent = loadTimeData.getString('passwordHideButton');
160     },
162     /**
163      * Hides the plain text password of this entry.
164      */
165     hidePassword: function() {
166       this.passwordField.type = 'password';
168       var button = this.passwordShowButton;
169       if (button)
170         button.textContent = loadTimeData.getString('passwordShowButton');
171     },
173     /**
174      * Get the original index of this item in the data model.
175      * @return {number} The index.
176      * @private
177      */
178     getOriginalIndex_: function() {
179       var index = this.dataItem[ORIGINAL_DATA_INDEX];
180       return index ? index : this.dataModel.indexOf(this.dataItem);
181     },
183     /**
184      * On-click event handler. Swaps the type of the input field from password
185      * to text and back.
186      * @private
187      */
188     onClick_: function(event) {
189       if (this.passwordField.type == 'password') {
190         // After the user is authenticated, showPassword() will be called.
191         PasswordManager.requestShowPassword(this.getOriginalIndex_());
192       } else {
193         this.hidePassword();
194       }
195     },
197     /**
198      * Get and set the URL for the entry.
199      * @type {string}
200      */
201     get url() {
202       return this.dataItem[URL_DATA_INDEX];
203     },
204     set url(url) {
205       this.dataItem[URL_DATA_INDEX] = url;
206     },
208     /**
209      * Get and set the username for the entry.
210      * @type {string}
211      */
212     get username() {
213       return this.dataItem[USERNAME_DATA_INDEX];
214     },
215     set username(username) {
216       this.dataItem[USERNAME_DATA_INDEX] = username;
217     },
219     /**
220      * Get and set the password for the entry.
221      * @type {string}
222      */
223     get password() {
224       return this.dataItem[PASSWORD_DATA_INDEX];
225     },
226     set password(password) {
227       this.dataItem[PASSWORD_DATA_INDEX] = password;
228     },
230     /**
231      * Get and set the federation for the entry.
232      * @type {string}
233      */
234     get federation() {
235       return this.dataItem[FEDERATION_DATA_INDEX];
236     },
237     set federation(federation) {
238       this.dataItem[FEDERATION_DATA_INDEX] = federation;
239     },
240   };
242   /**
243    * Creates a new PasswordExceptions list item.
244    * @param {Array} entry A pair of the form [url, username].
245    * @constructor
246    * @extends {options.DeletableItem}
247    */
248   function PasswordExceptionsListItem(entry) {
249     var el = cr.doc.createElement('div');
250     el.dataItem = entry;
251     el.__proto__ = PasswordExceptionsListItem.prototype;
252     el.decorate();
254     return el;
255   }
257   PasswordExceptionsListItem.prototype = {
258     __proto__: DeletableItem.prototype,
260     /**
261      * Call when an element is decorated as a list item.
262      */
263     decorate: function() {
264       DeletableItem.prototype.decorate.call(this);
266       // The URL of the site.
267       var urlLabel = this.ownerDocument.createElement('div');
268       urlLabel.className = 'url';
269       urlLabel.classList.add('favicon-cell');
270       urlLabel.classList.add('weakrtl');
271       urlLabel.textContent = this.url;
273       // The favicon URL is prefixed with "origin/", which essentially removes
274       // the URL path past the top-level domain and ensures that a scheme (e.g.,
275       // http) is being used. This ensures that the favicon returned is the
276       // default favicon for the domain and that the URL has a scheme if none
277       // is present in the password manager.
278       urlLabel.style.backgroundImage = getFaviconImageSet(
279           'origin/' + this.url, 16);
280       this.contentElement.appendChild(urlLabel);
281     },
283     /**
284      * Get the url for the entry.
285      * @type {string}
286      */
287     get url() {
288       return this.dataItem;
289     },
290     set url(url) {
291       this.dataItem = url;
292     },
293   };
295   /**
296    * Create a new passwords list.
297    * @constructor
298    * @extends {options.DeletableItemList}
299    */
300   var PasswordsList = cr.ui.define('list');
302   PasswordsList.prototype = {
303     __proto__: DeletableItemList.prototype,
305     /**
306      * Whether passwords can be revealed or not.
307      * @type {boolean}
308      * @private
309      */
310     showPasswords_: true,
312     /** @override */
313     decorate: function() {
314       DeletableItemList.prototype.decorate.call(this);
315       Preferences.getInstance().addEventListener(
316           'profile.password_manager_allow_show_passwords',
317           this.onPreferenceChanged_.bind(this));
318       this.addEventListener('focus', this.onFocus_.bind(this));
319     },
321     /**
322      * Listener for changes on the preference.
323      * @param {Event} event The preference update event.
324      * @private
325      */
326     onPreferenceChanged_: function(event) {
327       this.showPasswords_ = event.value.value;
328       this.redraw();
329     },
331     /**
332      * @override
333      * @param {Array} entry
334      */
335     createItem: function(entry) {
336       var showPasswords = this.showPasswords_;
338       if (loadTimeData.getBoolean('disableShowPasswords'))
339         showPasswords = false;
341       return new PasswordListItem(this.dataModel, entry, showPasswords);
342     },
344     /** @override */
345     deleteItemAtIndex: function(index) {
346       var item = this.dataModel.item(index);
347       if (item && item[ORIGINAL_DATA_INDEX] != undefined) {
348         // The fifth element, if present, is the original index to delete.
349         index = item[ORIGINAL_DATA_INDEX];
350       }
351       PasswordManager.removeSavedPassword(index);
352     },
354     /**
355      * The length of the list.
356      */
357     get length() {
358       return this.dataModel.length;
359     },
361     /**
362      * Will make to first row focusable if none are selected. This makes it
363      * possible to tab into the rows without pressing up/down first.
364      * @param {Event} e The focus event.
365      * @private
366      */
367     onFocus_: function(e) {
368       if (!this.selectedItem && this.items)
369         this.items[0].setFocusable_(true);
370     },
371   };
373   /**
374    * Create a new passwords list.
375    * @constructor
376    * @extends {options.DeletableItemList}
377    */
378   var PasswordExceptionsList = cr.ui.define('list');
380   PasswordExceptionsList.prototype = {
381     __proto__: DeletableItemList.prototype,
383     /**
384      * @override
385      * @param {Array} entry
386      */
387     createItem: function(entry) {
388       return new PasswordExceptionsListItem(entry);
389     },
391     /** @override */
392     deleteItemAtIndex: function(index) {
393       PasswordManager.removePasswordException(index);
394     },
396     /**
397      * The length of the list.
398      */
399     get length() {
400       return this.dataModel.length;
401     },
402   };
404   return {
405     PasswordListItem: PasswordListItem,
406     PasswordExceptionsListItem: PasswordExceptionsListItem,
407     PasswordsList: PasswordsList,
408     PasswordExceptionsList: PasswordExceptionsList,
409   };