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