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