Home | History | Annotate | Download | only in options
      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 
      5 cr.define('options', function() {
      6   /** @const */ var Page = cr.ui.pageManager.Page;
      7   /** @const */ var PageManager = cr.ui.pageManager.PageManager;
      8   /** @const */ var ArrayDataModel = cr.ui.ArrayDataModel;
      9 
     10   /////////////////////////////////////////////////////////////////////////////
     11   // PasswordManager class:
     12 
     13   /**
     14    * Encapsulated handling of password and exceptions page.
     15    * @constructor
     16    * @extends {cr.ui.pageManager.Page}
     17    */
     18   function PasswordManager() {
     19     this.activeNavTab = null;
     20     Page.call(this, 'passwords',
     21               loadTimeData.getString('passwordsPageTabTitle'),
     22               'password-manager');
     23   }
     24 
     25   cr.addSingletonGetter(PasswordManager);
     26 
     27   PasswordManager.prototype = {
     28     __proto__: Page.prototype,
     29 
     30     /**
     31      * The saved passwords list.
     32      * @type {options.DeletableItemList}
     33      * @private
     34      */
     35     savedPasswordsList_: null,
     36 
     37     /**
     38      * The password exceptions list.
     39      * @type {options.DeletableItemList}
     40      * @private
     41      */
     42     passwordExceptionsList_: null,
     43 
     44     /**
     45      * The timer id of the timer set on search query change events.
     46      * @type {number}
     47      * @private
     48      */
     49     queryDelayTimerId_: 0,
     50 
     51     /**
     52      * The most recent search query, or null if the query is empty.
     53      * @type {?string}
     54      * @private
     55      */
     56     lastQuery_: null,
     57 
     58     /** @override */
     59     initializePage: function() {
     60       Page.prototype.initializePage.call(this);
     61 
     62       $('password-manager-confirm').onclick = function() {
     63         PageManager.closeOverlay();
     64       };
     65 
     66       $('password-search-box').addEventListener('search',
     67           this.handleSearchQueryChange_.bind(this));
     68 
     69       this.createSavedPasswordsList_();
     70       this.createPasswordExceptionsList_();
     71     },
     72 
     73     /** @override */
     74     canShowPage: function() {
     75       return !(cr.isChromeOS && UIAccountTweaks.loggedInAsGuest());
     76     },
     77 
     78     /** @override */
     79     didShowPage: function() {
     80       // Updating the password lists may cause a blocking platform dialog pop up
     81       // (Mac, Linux), so we delay this operation until the page is shown.
     82       chrome.send('updatePasswordLists');
     83       $('password-search-box').focus();
     84     },
     85 
     86     /**
     87      * Creates, decorates and initializes the saved passwords list.
     88      * @private
     89      */
     90     createSavedPasswordsList_: function() {
     91       var savedPasswordsList = $('saved-passwords-list');
     92       options.passwordManager.PasswordsList.decorate(savedPasswordsList);
     93       this.savedPasswordsList_ = assertInstanceof(savedPasswordsList,
     94           options.DeletableItemList);
     95       this.savedPasswordsList_.autoExpands = true;
     96     },
     97 
     98     /**
     99      * Creates, decorates and initializes the password exceptions list.
    100      * @private
    101      */
    102     createPasswordExceptionsList_: function() {
    103       var passwordExceptionsList = $('password-exceptions-list');
    104       options.passwordManager.PasswordExceptionsList.decorate(
    105           passwordExceptionsList);
    106       this.passwordExceptionsList_ = assertInstanceof(passwordExceptionsList,
    107           options.DeletableItemList);
    108       this.passwordExceptionsList_.autoExpands = true;
    109     },
    110 
    111     /**
    112      * Handles search query changes.
    113      * @param {!Event} e The event object.
    114      * @private
    115      */
    116     handleSearchQueryChange_: function(e) {
    117       if (this.queryDelayTimerId_)
    118         window.clearTimeout(this.queryDelayTimerId_);
    119 
    120       // Searching cookies uses a timeout of 500ms. We use a shorter timeout
    121       // because there are probably fewer passwords and we want the UI to be
    122       // snappier since users will expect that it's "less work."
    123       this.queryDelayTimerId_ = window.setTimeout(
    124           this.searchPasswords_.bind(this), 250);
    125     },
    126 
    127     /**
    128      * Search passwords using text in |password-search-box|.
    129      * @private
    130      */
    131     searchPasswords_: function() {
    132       this.queryDelayTimerId_ = 0;
    133       var filter = $('password-search-box').value;
    134       filter = (filter == '') ? null : filter;
    135       if (this.lastQuery_ != filter) {
    136         this.lastQuery_ = filter;
    137         // Searching for passwords has the side effect of requerying the
    138         // underlying password store. This is done intentionally, as on OS X and
    139         // Linux they can change from outside and we won't be notified of it.
    140         chrome.send('updatePasswordLists');
    141       }
    142     },
    143 
    144     /**
    145      * Updates the visibility of the list and empty list placeholder.
    146      * @param {!cr.ui.List} list The list to toggle visilibility for.
    147      */
    148     updateListVisibility_: function(list) {
    149       var empty = list.dataModel.length == 0;
    150       var listPlaceHolderID = list.id + '-empty-placeholder';
    151       list.hidden = empty;
    152       $(listPlaceHolderID).hidden = !empty;
    153     },
    154 
    155     /**
    156      * Updates the data model for the saved passwords list with the values from
    157      * |entries|.
    158      * @param {!Array} entries The list of saved password data.
    159      */
    160     setSavedPasswordsList_: function(entries) {
    161       if (this.lastQuery_) {
    162         // Implement password searching here in javascript, rather than in C++.
    163         // The number of saved passwords shouldn't be too big for us to handle.
    164         var query = this.lastQuery_;
    165         var filter = function(entry, index, list) {
    166           // Search both URL and username.
    167           if (entry[0].toLowerCase().indexOf(query.toLowerCase()) >= 0 ||
    168               entry[1].toLowerCase().indexOf(query.toLowerCase()) >= 0) {
    169             // Keep the original index so we can delete correctly. See also
    170             // deleteItemAtIndex() in password_manager_list.js that uses this.
    171             entry[3] = index;
    172             return true;
    173           }
    174           return false;
    175         };
    176         entries = entries.filter(filter);
    177       }
    178       this.savedPasswordsList_.dataModel = new ArrayDataModel(entries);
    179       this.updateListVisibility_(this.savedPasswordsList_);
    180     },
    181 
    182     /**
    183      * Updates the data model for the password exceptions list with the values
    184      * from |entries|.
    185      * @param {!Array} entries The list of password exception data.
    186      */
    187     setPasswordExceptionsList_: function(entries) {
    188       this.passwordExceptionsList_.dataModel = new ArrayDataModel(entries);
    189       this.updateListVisibility_(this.passwordExceptionsList_);
    190     },
    191 
    192     /**
    193      * Reveals the password for a saved password entry. This is called by the
    194      * backend after it has authenticated the user.
    195      * @param {number} index The original index of the entry in the model.
    196      * @param {string} password The saved password.
    197      */
    198     showPassword_: function(index, password) {
    199       var model = this.savedPasswordsList_.dataModel;
    200       if (this.lastQuery_) {
    201         // When a filter is active, |index| does not represent the current
    202         // index in the model, but each entry stores its original index, so
    203         // we can find the item using a linear search.
    204         for (var i = 0; i < model.length; ++i) {
    205           if (model.item(i)[3] == index) {
    206             index = i;
    207             break;
    208           }
    209         }
    210       }
    211 
    212       // Reveal the password in the UI.
    213       var item = this.savedPasswordsList_.getListItemByIndex(index);
    214       item.showPassword(password);
    215     },
    216   };
    217 
    218   /**
    219    * Removes a saved password.
    220    * @param {number} rowIndex indicating the row to remove.
    221    */
    222   PasswordManager.removeSavedPassword = function(rowIndex) {
    223       chrome.send('removeSavedPassword', [String(rowIndex)]);
    224   };
    225 
    226   /**
    227    * Removes a password exception.
    228    * @param {number} rowIndex indicating the row to remove.
    229    */
    230   PasswordManager.removePasswordException = function(rowIndex) {
    231       chrome.send('removePasswordException', [String(rowIndex)]);
    232   };
    233 
    234   PasswordManager.requestShowPassword = function(index) {
    235     chrome.send('requestShowPassword', [index]);
    236   };
    237 
    238   // Forward public APIs to private implementations on the singleton instance.
    239   cr.makePublic(PasswordManager, [
    240     'setSavedPasswordsList',
    241     'setPasswordExceptionsList',
    242     'showPassword'
    243   ]);
    244 
    245   // Export
    246   return {
    247     PasswordManager: PasswordManager
    248   };
    249 
    250 });
    251