Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(556)

Side by Side Diff: chrome/browser/resources/options/password_manager_list.js

Issue 1318523011: [Password Manager] Copiable username and origin. Linkable origin elided from the left. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Changes addressed to reviewer comments Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 cr.define('options.passwordManager', function() { 5 cr.define('options.passwordManager', function() {
6 /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel; 6 /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel;
7 /** @const */ var DeletableItemList = options.DeletableItemList; 7 /** @const */ var DeletableItemList = options.DeletableItemList;
8 /** @const */ var DeletableItem = options.DeletableItem; 8 /** @const */ var DeletableItem = options.DeletableItem;
9 /** @const */ var List = cr.ui.List; 9 /** @const */ var List = cr.ui.List;
10 10
11 /** @const */ var URL_DATA_INDEX = 0; 11 // The following constants should be synchronized with the constants in
12 /** @const */ var USERNAME_DATA_INDEX = 1; 12 // chrome/browser/ui/webui/options/password_manager_handler.cc.
13 /** @const */ var PASSWORD_DATA_INDEX = 2; 13 /** @const */ var ORIGIN_FIELD = 'origin';
Evan Stade 2015/10/13 17:15:01 I don't think these are worth much. What is the pu
kolos1 2015/10/15 09:34:53 It collects all information about the fields of Pa
14 /** @const */ var FEDERATION_DATA_INDEX = 3; 14 /** @const */ var SHOWN_URL_FIELD = 'shownUrl';
15 /** @const */ var ORIGINAL_DATA_INDEX = 4; 15 /** @const */ var IS_SECURE_FIELD = 'isSecure';
16 /** @const */ var USERNAME_FIELD = 'username';
17 /** @const */ var PASSWORD_FIELD = 'password';
18 /** @const */ var FEDERATION_FIELD = 'federation';
19 /** @const */ var ORIGINAL_INDEX_FIELD = 'index';
16 20
17 /** 21 /**
18 * Creates a new passwords list item. 22 * Creates a new passwords list item.
19 * @param {cr.ui.ArrayDataModel} dataModel The data model that contains this 23 * @param {cr.ui.ArrayDataModel} dataModel The data model that contains this
20 * item. 24 * item.
21 * @param {Array} entry An array of the form [url, username, password, 25 * @param {Array} entry An array of the form [url, username, password,
22 * federation]. When the list has been filtered, a fifth element [index] 26 * federation]. When the list has been filtered, a fifth element [index]
23 * may be present. 27 * may be present.
24 * @param {boolean} showPasswords If true, add a button to the element to 28 * @param {boolean} showPasswords If true, add a button to the element to
25 * allow the user to reveal the saved password. 29 * allow the user to reveal the saved password.
26 * @constructor 30 * @constructor
27 * @extends {options.DeletableItem} 31 * @extends {options.DeletableItem}
28 */ 32 */
29 function PasswordListItem(dataModel, entry, showPasswords) { 33 function PasswordListItem(dataModel, entry, showPasswords) {
30 var el = cr.doc.createElement('div'); 34 var el = cr.doc.createElement('div');
31 el.dataItem = entry; 35 el.dataItem = entry;
32 el.dataModel = dataModel; 36 el.dataModel = dataModel;
33 el.__proto__ = PasswordListItem.prototype; 37 el.__proto__ = PasswordListItem.prototype;
34 el.showPasswords_ = showPasswords; 38 el.showPasswords_ = showPasswords;
35 el.decorate(); 39 el.decorate();
36 40
37 return el; 41 return el;
38 } 42 }
39 43
40 PasswordListItem.prototype = { 44 PasswordListItem.prototype = {
41 __proto__: DeletableItem.prototype, 45 __proto__: DeletableItem.prototype,
42 46
47
43 /** @override */ 48 /** @override */
44 decorate: function() { 49 decorate: function() {
45 DeletableItem.prototype.decorate.call(this); 50 DeletableItem.prototype.decorate.call(this);
46 51
47 // The URL of the site. 52 // The URL of the site.
48 var urlLabel = this.ownerDocument.createElement('div'); 53 var urlDiv = this.ownerDocument.createElement('div');
49 urlLabel.classList.add('favicon-cell'); 54 urlDiv.className = 'favicon-cell left-elided-url url';
50 urlLabel.classList.add('weakrtl'); 55 urlDiv.setAttribute('title', this.url);
51 urlLabel.classList.add('url'); 56 var urlLink = this.ownerDocument.createElement('a');
52 urlLabel.setAttribute('title', this.url); 57 urlLink.href = this.url;
53 urlLabel.textContent = this.url; 58 urlLink.textContent = this.shownUrl.split('').reverse().join('');
59 urlDiv.appendChild(urlLink);
54 60
55 // The favicon URL is prefixed with "origin/", which essentially removes 61 // 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., 62 // 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 63 // 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 64 // default favicon for the domain and that the URL has a scheme if none
59 // is present in the password manager. 65 // is present in the password manager.
60 urlLabel.style.backgroundImage = getFaviconImageSet( 66 if (this.isUrlSecure) {
61 'origin/' + this.url, 16); 67 urlDiv.style.backgroundImage = getFaviconImageSet(
62 this.contentElement.appendChild(urlLabel); 68 'origin/' + this.url, 16);
69 }
70 this.contentElement.appendChild(urlDiv);
63 71
64 // The stored username. 72 // The stored username.
65 var usernameLabel = this.ownerDocument.createElement('div'); 73 var usernameDiv = this.ownerDocument.createElement('div');
66 usernameLabel.className = 'name'; 74 usernameDiv.className = 'name';
67 usernameLabel.textContent = this.username; 75 usernameDiv.title = this.username;
68 usernameLabel.title = this.username; 76 this.contentElement.appendChild(usernameDiv);
69 this.contentElement.appendChild(usernameLabel); 77 var usernameInput = this.ownerDocument.createElement('input');
78 usernameInput.type = 'text';
79 usernameInput.className = 'inactive-item';
80 usernameInput.readOnly = true;
81 usernameInput.value = this.username;
82 usernameDiv.appendChild(usernameInput);
83 this.usernameField = usernameInput;
70 84
71 if (this.federation) { 85 if (this.federation) {
72 // The federation. 86 // The federation.
73 var federationDiv = this.ownerDocument.createElement('div'); 87 var federationDiv = this.ownerDocument.createElement('div');
74 federationDiv.className = 'federation'; 88 federationDiv.className = 'federation';
75 federationDiv.textContent = this.federation; 89 federationDiv.textContent = this.federation;
76 this.contentElement.appendChild(federationDiv); 90 this.contentElement.appendChild(federationDiv);
77 } else { 91 } else {
78 // The stored password. 92 // The stored password.
79 var passwordInputDiv = this.ownerDocument.createElement('div'); 93 var passwordInputDiv = this.ownerDocument.createElement('div');
80 passwordInputDiv.className = 'password'; 94 passwordInputDiv.className = 'password';
81 95
82 // The password input field. 96 // The password input field.
83 var passwordInput = this.ownerDocument.createElement('input'); 97 var passwordInput = this.ownerDocument.createElement('input');
84 passwordInput.type = 'password'; 98 passwordInput.type = 'password';
85 passwordInput.className = 'inactive-password'; 99 passwordInput.className = 'inactive-item';
86 passwordInput.readOnly = true; 100 passwordInput.readOnly = true;
87 passwordInput.value = this.showPasswords_ ? this.password : '********'; 101 passwordInput.value = this.showPasswords_ ? this.password : '********';
88 passwordInputDiv.appendChild(passwordInput); 102 passwordInputDiv.appendChild(passwordInput);
89 var deletableItem = this; 103 var deletableItem = this;
90 passwordInput.addEventListener('focus', function() { 104 passwordInput.addEventListener('focus', function() {
91 deletableItem.handleFocus(); 105 deletableItem.handleFocus();
92 }); 106 });
93 this.passwordField = passwordInput; 107 this.passwordField = passwordInput;
94 this.setFocusable_(false); 108 this.setFocusable_(false);
95 109
(...skipping 11 matching lines...) Expand all
107 event.stopPropagation(); 121 event.stopPropagation();
108 }, false); 122 }, false);
109 button.addEventListener('focus', function() { 123 button.addEventListener('focus', function() {
110 deletableItem.handleFocus(); 124 deletableItem.handleFocus();
111 }); 125 });
112 passwordInputDiv.appendChild(button); 126 passwordInputDiv.appendChild(button);
113 this.passwordShowButton = button; 127 this.passwordShowButton = button;
114 } 128 }
115 this.contentElement.appendChild(passwordInputDiv); 129 this.contentElement.appendChild(passwordInputDiv);
116 } 130 }
117
118 }, 131 },
119 132
120 /** @override */ 133 /** @override */
121 selectionChanged: function() { 134 selectionChanged: function() {
122 var input = this.passwordField; 135 var usernameInput = this.usernameField;
136 var passwordInput = this.passwordField;
123 var button = this.passwordShowButton; 137 var button = this.passwordShowButton;
124 // The button doesn't exist when passwords can't be shown. 138 // The button doesn't exist when passwords can't be shown.
125 if (!button) 139 if (!button)
126 return; 140 return;
127 141
128 if (this.selected) { 142 if (this.selected) {
129 input.classList.remove('inactive-password'); 143 usernameInput.classList.remove('inactive-item');
144 passwordInput.classList.remove('inactive-item');
130 this.setFocusable_(true); 145 this.setFocusable_(true);
131 button.hidden = false; 146 button.hidden = false;
132 input.focus(); 147 passwordInput.focus();
133 } else { 148 } else {
134 input.classList.add('inactive-password'); 149 usernameInput.classList.add('inactive-item');
150 passwordInput.classList.add('inactive-item');
135 this.setFocusable_(false); 151 this.setFocusable_(false);
136 button.hidden = true; 152 button.hidden = true;
137 } 153 }
138 }, 154 },
139 155
140 /** 156 /**
141 * Set the focusability of this row. 157 * Set the focusability of this row.
142 * @param {boolean} focusable 158 * @param {boolean} focusable
143 * @private 159 * @private
144 */ 160 */
(...skipping 24 matching lines...) Expand all
169 if (button) 185 if (button)
170 button.textContent = loadTimeData.getString('passwordShowButton'); 186 button.textContent = loadTimeData.getString('passwordShowButton');
171 }, 187 },
172 188
173 /** 189 /**
174 * Get the original index of this item in the data model. 190 * Get the original index of this item in the data model.
175 * @return {number} The index. 191 * @return {number} The index.
176 * @private 192 * @private
177 */ 193 */
178 getOriginalIndex_: function() { 194 getOriginalIndex_: function() {
179 var index = this.dataItem[ORIGINAL_DATA_INDEX]; 195 var index = this.dataItem[ORIGINAL_INDEX_FIELD];
180 return index ? index : this.dataModel.indexOf(this.dataItem); 196 return index ? index : this.dataModel.indexOf(this.dataItem);
181 }, 197 },
182 198
183 /** 199 /**
184 * On-click event handler. Swaps the type of the input field from password 200 * On-click event handler. Swaps the type of the input field from password
185 * to text and back. 201 * to text and back.
186 * @private 202 * @private
187 */ 203 */
188 onClick_: function(event) { 204 onClick_: function(event) {
189 if (this.passwordField.type == 'password') { 205 if (this.passwordField.type == 'password') {
190 // After the user is authenticated, showPassword() will be called. 206 // After the user is authenticated, showPassword() will be called.
191 PasswordManager.requestShowPassword(this.getOriginalIndex_()); 207 PasswordManager.requestShowPassword(this.getOriginalIndex_());
192 } else { 208 } else {
193 this.hidePassword(); 209 this.hidePassword();
194 } 210 }
195 }, 211 },
196 212
197 /** 213 /**
198 * Get and set the URL for the entry. 214 * Get and set the URL for the entry.
199 * @type {string} 215 * @type {string}
200 */ 216 */
201 get url() { 217 get url() {
202 return this.dataItem[URL_DATA_INDEX]; 218 return this.dataItem[ORIGIN_FIELD];
203 }, 219 },
204 set url(url) { 220 set url(url) {
205 this.dataItem[URL_DATA_INDEX] = url; 221 this.dataItem[ORIGIN_FIELD] = url;
206 }, 222 },
207 223
208 /** 224 /**
225 * Get and set the shown url for the entry.
226 * @type {string}
227 */
228 get shownUrl() {
229 return this.dataItem[SHOWN_URL_FIELD];
230 },
231 set shownUrl(shownUrl) {
232 this.dataItem[SHOWN_URL_FIELD] = shownUrl;
233 },
234
235 /**
236 * Get and set whether the origin uses secure scheme.
237 * @type {boolean}
238 */
239 get isUrlSecure() {
240 return this.dataItem[IS_SECURE_FIELD];
241 },
242 set isUrlSecure(isUrlSecure) {
243 this.dataItem[IS_SECURE_FIELD] = isUrlSecure;
244 },
245
246 /**
209 * Get and set the username for the entry. 247 * Get and set the username for the entry.
210 * @type {string} 248 * @type {string}
211 */ 249 */
212 get username() { 250 get username() {
213 return this.dataItem[USERNAME_DATA_INDEX]; 251 return this.dataItem[USERNAME_FIELD];
214 }, 252 },
215 set username(username) { 253 set username(username) {
216 this.dataItem[USERNAME_DATA_INDEX] = username; 254 this.dataItem[USERNAME_FIELD] = username;
217 }, 255 },
218 256
219 /** 257 /**
220 * Get and set the password for the entry. 258 * Get and set the password for the entry.
221 * @type {string} 259 * @type {string}
222 */ 260 */
223 get password() { 261 get password() {
224 return this.dataItem[PASSWORD_DATA_INDEX]; 262 return this.dataItem[PASSWORD_FIELD];
225 }, 263 },
226 set password(password) { 264 set password(password) {
227 this.dataItem[PASSWORD_DATA_INDEX] = password; 265 this.dataItem[PASSWORD_FIELD] = password;
228 }, 266 },
229 267
230 /** 268 /**
231 * Get and set the federation for the entry. 269 * Get and set the federation for the entry.
232 * @type {string} 270 * @type {string}
233 */ 271 */
234 get federation() { 272 get federation() {
235 return this.dataItem[FEDERATION_DATA_INDEX]; 273 return this.dataItem[FEDERATION_FIELD];
236 }, 274 },
237 set federation(federation) { 275 set federation(federation) {
238 this.dataItem[FEDERATION_DATA_INDEX] = federation; 276 this.dataItem[FEDERATION_FIELD] = federation;
239 }, 277 },
240 }; 278 };
241 279
242 /** 280 /**
243 * Creates a new PasswordExceptions list item. 281 * Creates a new PasswordExceptions list item.
244 * @param {Array} entry A pair of the form [url, username]. 282 * @param {Array} entry A pair of the form [url, username].
245 * @constructor 283 * @constructor
246 * @extends {options.DeletableItem} 284 * @extends {options.DeletableItem}
247 */ 285 */
248 function PasswordExceptionsListItem(entry) { 286 function PasswordExceptionsListItem(entry) {
249 var el = cr.doc.createElement('div'); 287 var el = cr.doc.createElement('div');
250 el.dataItem = entry; 288 el.dataItem = entry;
251 el.__proto__ = PasswordExceptionsListItem.prototype; 289 el.__proto__ = PasswordExceptionsListItem.prototype;
252 el.decorate(); 290 el.decorate();
253 291
254 return el; 292 return el;
255 } 293 }
256 294
257 PasswordExceptionsListItem.prototype = { 295 PasswordExceptionsListItem.prototype = {
258 __proto__: DeletableItem.prototype, 296 __proto__: DeletableItem.prototype,
259 297
260 /** 298 /**
261 * Call when an element is decorated as a list item. 299 * Call when an element is decorated as a list item.
262 */ 300 */
263 decorate: function() { 301 decorate: function() {
264 DeletableItem.prototype.decorate.call(this); 302 DeletableItem.prototype.decorate.call(this);
265 303
266 // The URL of the site. 304 // The URL of the site.
267 var urlLabel = this.ownerDocument.createElement('div'); 305 var urlDiv = this.ownerDocument.createElement('div');
268 urlLabel.className = 'url'; 306 urlDiv.className = 'url';
269 urlLabel.classList.add('favicon-cell'); 307 urlDiv.classList.add('favicon-cell');
270 urlLabel.classList.add('weakrtl'); 308 urlDiv.classList.add('left-elided-url');
271 urlLabel.textContent = this.url; 309 urlDiv.setAttribute('title', this.url);
310 var urlLink = this.ownerDocument.createElement('a');
311 urlLink.href = this.url;
312 urlLink.textContent = this.shownUrl.split('').reverse().join('');
313 urlDiv.appendChild(urlLink);
272 314
273 // The favicon URL is prefixed with "origin/", which essentially removes 315 // 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., 316 // 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 317 // 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 318 // default favicon for the domain and that the URL has a scheme if none
277 // is present in the password manager. 319 // is present in the password manager.
278 urlLabel.style.backgroundImage = getFaviconImageSet( 320 if (this.isUrlSecure) {
279 'origin/' + this.url, 16); 321 urlDiv.style.backgroundImage = getFaviconImageSet(
280 this.contentElement.appendChild(urlLabel); 322 'origin/' + this.url, 16);
323 }
324 this.contentElement.appendChild(urlDiv);
281 }, 325 },
282 326
283 /** 327 /**
284 * Get the url for the entry. 328 * Get the url for the entry.
285 * @type {string} 329 * @type {string}
286 */ 330 */
287 get url() { 331 get url() {
288 return this.dataItem; 332 return this.dataItem[ORIGIN_FIELD];
289 }, 333 },
290 set url(url) { 334 set url(url) {
291 this.dataItem = url; 335 this.dataItem[ORIGIN_FIELD] = url;
336 },
337
338 /**
339 * Get and set the shown url for the entry.
340 * @type {string}
341 */
342 get shownUrl() {
343 return this.dataItem[SHOWN_URL_FIELD];
344 },
345 set shownUrl(shownUrl) {
346 this.dataItem[SHOWN_URL_FIELD] = shownUrl;
347 },
348
349 /**
350 * Get and set whether the origin uses secure scheme.
351 * @type {boolean}
352 */
353 get isUrlSecure() {
354 return this.dataItem[IS_SECURE_FIELD];
355 },
356 set isUrlSecure(isUrlSecure) {
357 this.dataItem[IS_SECURE_FIELD] = isUrlSecure;
292 }, 358 },
293 }; 359 };
294 360
295 /** 361 /**
296 * Create a new passwords list. 362 * Create a new passwords list.
297 * @constructor 363 * @constructor
298 * @extends {options.DeletableItemList} 364 * @extends {options.DeletableItemList}
299 */ 365 */
300 var PasswordsList = cr.ui.define('list'); 366 var PasswordsList = cr.ui.define('list');
301 367
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
337 403
338 if (loadTimeData.getBoolean('disableShowPasswords')) 404 if (loadTimeData.getBoolean('disableShowPasswords'))
339 showPasswords = false; 405 showPasswords = false;
340 406
341 return new PasswordListItem(this.dataModel, entry, showPasswords); 407 return new PasswordListItem(this.dataModel, entry, showPasswords);
342 }, 408 },
343 409
344 /** @override */ 410 /** @override */
345 deleteItemAtIndex: function(index) { 411 deleteItemAtIndex: function(index) {
346 var item = this.dataModel.item(index); 412 var item = this.dataModel.item(index);
347 if (item && item[ORIGINAL_DATA_INDEX] != undefined) { 413 if (item && item[ORIGINAL_INDEX_FIELD] != undefined) {
348 // The fifth element, if present, is the original index to delete. 414 // The fifth element, if present, is the original index to delete.
349 index = item[ORIGINAL_DATA_INDEX]; 415 index = item[ORIGINAL_INDEX_FIELD];
350 } 416 }
351 PasswordManager.removeSavedPassword(index); 417 PasswordManager.removeSavedPassword(index);
352 }, 418 },
353 419
354 /** 420 /**
355 * The length of the list. 421 * The length of the list.
356 */ 422 */
357 get length() { 423 get length() {
358 return this.dataModel.length; 424 return this.dataModel.length;
359 }, 425 },
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
399 get length() { 465 get length() {
400 return this.dataModel.length; 466 return this.dataModel.length;
401 }, 467 },
402 }; 468 };
403 469
404 return { 470 return {
405 PasswordListItem: PasswordListItem, 471 PasswordListItem: PasswordListItem,
406 PasswordExceptionsListItem: PasswordExceptionsListItem, 472 PasswordExceptionsListItem: PasswordExceptionsListItem,
407 PasswordsList: PasswordsList, 473 PasswordsList: PasswordsList,
408 PasswordExceptionsList: PasswordExceptionsList, 474 PasswordExceptionsList: PasswordExceptionsList,
475 ORIGIN_FIELD: ORIGIN_FIELD,
476 SHOWN_URL_FIELD: SHOWN_URL_FIELD,
477 IS_SECURE_FIELD: IS_SECURE_FIELD,
478 USERNAME_FIELD: USERNAME_FIELD,
479 PASSWORD_FIELD: PASSWORD_FIELD,
480 FEDERATION_FIELD: FEDERATION_FIELD,
481 ORIGINAL_INDEX_FIELD: ORIGINAL_INDEX_FIELD
409 }; 482 };
410 }); 483 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698