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