password_manager_list.js revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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.
4
5cr.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;
10
11  /**
12   * Creates a new passwords list item.
13   * @param {Array} entry An array of the form [url, username, password]. When
14   *     the list has been filtered, a fourth element [index] may be present.
15   * @constructor
16   * @extends {cr.ui.ListItem}
17   */
18  function PasswordListItem(entry, showPasswords) {
19    var el = cr.doc.createElement('div');
20    el.dataItem = entry;
21    el.__proto__ = PasswordListItem.prototype;
22    el.decorate(showPasswords);
23
24    return el;
25  }
26
27  PasswordListItem.prototype = {
28    __proto__: DeletableItem.prototype,
29
30    /** @override */
31    decorate: function(showPasswords) {
32      DeletableItem.prototype.decorate.call(this);
33
34      // The URL of the site.
35      var urlLabel = this.ownerDocument.createElement('div');
36      urlLabel.classList.add('favicon-cell');
37      urlLabel.classList.add('weakrtl');
38      urlLabel.classList.add('url');
39      urlLabel.setAttribute('title', this.url);
40      urlLabel.textContent = this.url;
41
42      // The favicon URL is prefixed with "origin/", which essentially removes
43      // the URL path past the top-level domain and ensures that a scheme (e.g.,
44      // http) is being used. This ensures that the favicon returned is the
45      // default favicon for the domain and that the URL has a scheme if none
46      // is present in the password manager.
47      urlLabel.style.backgroundImage = getFaviconImageSet(
48          'origin/' + this.url, 16);
49      this.contentElement.appendChild(urlLabel);
50
51      // The stored username.
52      var usernameLabel = this.ownerDocument.createElement('div');
53      usernameLabel.className = 'name';
54      usernameLabel.textContent = this.username;
55      this.contentElement.appendChild(usernameLabel);
56
57      // The stored password.
58      var passwordInputDiv = this.ownerDocument.createElement('div');
59      passwordInputDiv.className = 'password';
60
61      // The password input field.
62      var passwordInput = this.ownerDocument.createElement('input');
63      passwordInput.type = 'password';
64      passwordInput.className = 'inactive-password';
65      passwordInput.readOnly = true;
66      passwordInput.value = showPasswords ? this.password : '********';
67      passwordInputDiv.appendChild(passwordInput);
68
69      // The show/hide button.
70      if (showPasswords) {
71        var button = this.ownerDocument.createElement('button');
72        button.hidden = true;
73        button.className = 'list-inline-button custom-appearance';
74        button.textContent = loadTimeData.getString('passwordShowButton');
75        button.addEventListener('click', this.onClick_, true);
76        passwordInputDiv.appendChild(button);
77      }
78
79      this.contentElement.appendChild(passwordInputDiv);
80    },
81
82    /** @override */
83    selectionChanged: function() {
84      var passwordInput = this.querySelector('input[type=password]');
85      var textInput = this.querySelector('input[type=text]');
86      var input = passwordInput || textInput;
87      var button = input.nextSibling;
88      // |button| doesn't exist when passwords can't be shown.
89      if (!button)
90        return;
91      if (this.selected) {
92        input.classList.remove('inactive-password');
93        button.hidden = false;
94      } else {
95        input.classList.add('inactive-password');
96        button.hidden = true;
97      }
98    },
99
100    /**
101     * On-click event handler. Swaps the type of the input field from password
102     * to text and back.
103     * @private
104     */
105    onClick_: function(event) {
106      // The password is the input element previous to the button.
107      var button = event.currentTarget;
108      var passwordInput = button.previousSibling;
109      if (passwordInput.type == 'password') {
110        passwordInput.type = 'text';
111        button.textContent = loadTimeData.getString('passwordHideButton');
112      } else {
113        passwordInput.type = 'password';
114        button.textContent = loadTimeData.getString('passwordShowButton');
115      }
116    },
117
118    /**
119     * Get and set the URL for the entry.
120     * @type {string}
121     */
122    get url() {
123      return this.dataItem[0];
124    },
125    set url(url) {
126      this.dataItem[0] = url;
127    },
128
129    /**
130     * Get and set the username for the entry.
131     * @type {string}
132     */
133    get username() {
134      return this.dataItem[1];
135    },
136    set username(username) {
137      this.dataItem[1] = username;
138    },
139
140    /**
141     * Get and set the password for the entry.
142     * @type {string}
143     */
144    get password() {
145      return this.dataItem[2];
146    },
147    set password(password) {
148      this.dataItem[2] = password;
149    },
150  };
151
152  /**
153   * Creates a new PasswordExceptions list item.
154   * @param {Array} entry A pair of the form [url, username].
155   * @constructor
156   * @extends {Deletable.ListItem}
157   */
158  function PasswordExceptionsListItem(entry) {
159    var el = cr.doc.createElement('div');
160    el.dataItem = entry;
161    el.__proto__ = PasswordExceptionsListItem.prototype;
162    el.decorate();
163
164    return el;
165  }
166
167  PasswordExceptionsListItem.prototype = {
168    __proto__: DeletableItem.prototype,
169
170    /**
171     * Call when an element is decorated as a list item.
172     */
173    decorate: function() {
174      DeletableItem.prototype.decorate.call(this);
175
176      // The URL of the site.
177      var urlLabel = this.ownerDocument.createElement('div');
178      urlLabel.className = 'url';
179      urlLabel.classList.add('favicon-cell');
180      urlLabel.classList.add('weakrtl');
181      urlLabel.textContent = this.url;
182
183      // The favicon URL is prefixed with "origin/", which essentially removes
184      // the URL path past the top-level domain and ensures that a scheme (e.g.,
185      // http) is being used. This ensures that the favicon returned is the
186      // default favicon for the domain and that the URL has a scheme if none
187      // is present in the password manager.
188      urlLabel.style.backgroundImage = getFaviconImageSet(
189          'origin/' + this.url, 16);
190      this.contentElement.appendChild(urlLabel);
191    },
192
193    /**
194     * Get the url for the entry.
195     * @type {string}
196     */
197    get url() {
198      return this.dataItem;
199    },
200    set url(url) {
201      this.dataItem = url;
202    },
203  };
204
205  /**
206   * Create a new passwords list.
207   * @constructor
208   * @extends {cr.ui.List}
209   */
210  var PasswordsList = cr.ui.define('list');
211
212  PasswordsList.prototype = {
213    __proto__: DeletableItemList.prototype,
214
215    /**
216     * Whether passwords can be revealed or not.
217     * @type {boolean}
218     * @private
219     */
220    showPasswords_: true,
221
222    /** @override */
223    decorate: function() {
224      DeletableItemList.prototype.decorate.call(this);
225      Preferences.getInstance().addEventListener(
226          'profile.password_manager_allow_show_passwords',
227          this.onPreferenceChanged_.bind(this));
228    },
229
230    /**
231     * Listener for changes on the preference.
232     * @param {Event} event The preference update event.
233     * @private
234     */
235    onPreferenceChanged_: function(event) {
236      this.showPasswords_ = event.value.value;
237      this.redraw();
238    },
239
240    /** @override */
241    createItem: function(entry) {
242      return new PasswordListItem(entry, this.showPasswords_);
243    },
244
245    /** @override */
246    deleteItemAtIndex: function(index) {
247      var item = this.dataModel.item(index);
248      if (item && item.length > 3) {
249        // The fourth element, if present, is the original index to delete.
250        index = item[3];
251      }
252      PasswordManager.removeSavedPassword(index);
253    },
254
255    /**
256     * The length of the list.
257     */
258    get length() {
259      return this.dataModel.length;
260    },
261  };
262
263  /**
264   * Create a new passwords list.
265   * @constructor
266   * @extends {cr.ui.List}
267   */
268  var PasswordExceptionsList = cr.ui.define('list');
269
270  PasswordExceptionsList.prototype = {
271    __proto__: DeletableItemList.prototype,
272
273    /** @override */
274    createItem: function(entry) {
275      return new PasswordExceptionsListItem(entry);
276    },
277
278    /** @override */
279    deleteItemAtIndex: function(index) {
280      PasswordManager.removePasswordException(index);
281    },
282
283    /**
284     * The length of the list.
285     */
286    get length() {
287      return this.dataModel.length;
288    },
289  };
290
291  return {
292    PasswordListItem: PasswordListItem,
293    PasswordExceptionsListItem: PasswordExceptionsListItem,
294    PasswordsList: PasswordsList,
295    PasswordExceptionsList: PasswordExceptionsList,
296  };
297});
298