| Index: chrome/browser/resources/options/pref_ui.js | 
| diff --git a/chrome/browser/resources/options/pref_ui.js b/chrome/browser/resources/options/pref_ui.js | 
| index 539c4d83f9b7ce2251e1e8990ddf545eb6065a9a..f1a2194361e92491120fed23405022202f46d554 100644 | 
| --- a/chrome/browser/resources/options/pref_ui.js | 
| +++ b/chrome/browser/resources/options/pref_ui.js | 
| @@ -39,26 +39,13 @@ cr.define('options', function() { | 
| el.disabled = Object.keys(el.disabledReasons).length > 0; | 
| } | 
|  | 
| -  /** | 
| -   * Helper function to update element's state from pref change event. | 
| -   * @private | 
| -   * @param {!HTMLElement} el The element to update. | 
| -   * @param {!Event} event The pref change event. | 
| -   */ | 
| -  function updateElementState_(el, event) { | 
| -    updateDisabledState_(el, 'notUserModifiable', event.value.disabled); | 
| -    el.controlledBy = event.value.controlledBy; | 
| -    OptionsPage.updateManagedBannerVisibility(); | 
| -  } | 
| - | 
| ///////////////////////////////////////////////////////////////////////////// | 
| -  // PrefCheckbox class: | 
| -  // TODO(jhawkins): Refactor all this copy-pasted code! | 
| +  // PrefInputElement class: | 
|  | 
| // Define a constructor that uses an input element as its underlying element. | 
| -  var PrefCheckbox = cr.ui.define('input'); | 
| +  var PrefInputElement = function() {}; | 
|  | 
| -  PrefCheckbox.prototype = { | 
| +  PrefInputElement.prototype = { | 
| // Set up the prototype chain | 
| __proto__: HTMLInputElement.prototype, | 
|  | 
| @@ -66,51 +53,39 @@ cr.define('options', function() { | 
| * Initialization function for the cr.ui framework. | 
| */ | 
| decorate: function() { | 
| -      this.type = 'checkbox'; | 
| var self = this; | 
|  | 
| -      this.initializeValueType(this.getAttribute('value-type')); | 
| +      // Listen for user events. | 
| +      this.addEventListener('change', this.handleChange_.bind(this)); | 
|  | 
| -      // Listen to pref changes. | 
| +      // Listen for pref changes. | 
| Preferences.getInstance().addEventListener(this.pref, function(event) { | 
| -        var value = event.value.value; | 
| -        self.checked = self.inverted_pref ? !value : value; | 
| -        updateElementState_(self, event); | 
| +        self.updateStateFromPref_(event); | 
| +        updateDisabledState_(self, 'notUserModifiable', event.value.disabled); | 
| +        self.controlledBy = event.value.controlledBy; | 
| +        OptionsPage.updateManagedBannerVisibility(); | 
| }); | 
| - | 
| -      // Listen to user events. | 
| -      this.addEventListener('change', this.updatePreferenceValue_.bind(this)); | 
| }, | 
|  | 
| /** | 
| -     * Handler that updates the associated pref when the user changes the input | 
| -     * element's value. | 
| +     * Handle changes to the input element's state made by the user. If a custom | 
| +     * change handler does not suppress it, a default handler is invoked that | 
| +     * updates the associated pref. | 
| * @param {Event} event Change event. | 
| * @private | 
| */ | 
| -    updatePreferenceValue_: function(event) { | 
| -      if (this.customChangeHandler(event)) | 
| -        return; | 
| - | 
| -      var value = this.inverted_pref ? !this.checked : this.checked; | 
| -      switch (this.valueType) { | 
| -        case 'number': | 
| -          Preferences.setIntegerPref(this.pref, Number(value), | 
| -                                     !this.dialogPref, this.metric); | 
| -          break; | 
| -        case 'boolean': | 
| -          Preferences.setBooleanPref(this.pref, value, | 
| -                                     !this.dialogPref, this.metric); | 
| -          break; | 
| -      } | 
| +    handleChange_: function(event) { | 
| +      if (!this.customChangeHandler(event)) | 
| +        this.updatePrefFromState_(); | 
| }, | 
|  | 
| /** | 
| -     * Sets up options in checkbox element. | 
| -     * @param {String} valueType The preference type for this checkbox. | 
| +     * Update the input element's state when the associated pref changes. | 
| +     * @param {Event} event Pref change event. | 
| +     * @private | 
| */ | 
| -    initializeValueType: function(valueType) { | 
| -      this.valueType = valueType || 'boolean'; | 
| +    updateStateFromPref_: function(event) { | 
| +      this.value = event.value.value; | 
| }, | 
|  | 
| /** | 
| @@ -121,11 +96,12 @@ cr.define('options', function() { | 
| }, | 
|  | 
| /** | 
| -     * Handler that is called when the user changes the input element's value. | 
| -     * If it returns false, the default handler is executed next, updating the | 
| -     * associated pref to its new value. If it returns true, the default handler | 
| -     * is skipped (i.e., this works like stopPropagation or cancelBubble). | 
| -     * @param {Event} event Change event. | 
| +     * Custom change handler that is invoked first when the user makes changes | 
| +     * to the input element's state. If it returns false, a default handler is | 
| +     * invoked next that updates the associated pref. If it returns true, the | 
| +     * default handler is suppressed (i.e., this works like stopPropagation or | 
| +     * cancelBubble). | 
| +     * @param {Event} event Input element change event. | 
| */ | 
| customChangeHandler: function(event) { | 
| return false; | 
| @@ -133,236 +109,156 @@ cr.define('options', function() { | 
| }; | 
|  | 
| /** | 
| -   * The preference name. | 
| +   * The name of the associated preference. | 
| * @type {string} | 
| */ | 
| -  cr.defineProperty(PrefCheckbox, 'pref', cr.PropertyKind.ATTR); | 
| +  cr.defineProperty(PrefInputElement, 'pref', cr.PropertyKind.ATTR); | 
|  | 
| /** | 
| -   * A special preference type specific to dialogs. Changes take effect in the | 
| -   * settings UI immediately but are only actually committed when the user | 
| -   * confirms the dialog. If the user cancels the dialog instead, the changes | 
| -   * are rolled back in the settings UI and never committed. | 
| -   * @type {boolean} | 
| +   * The data type of the associated preference, only relevant for derived | 
| +   * classes that support different data types. | 
| +   * @type {string} | 
| */ | 
| -  cr.defineProperty(PrefCheckbox, 'dialogPref', cr.PropertyKind.BOOL_ATTR); | 
| +  cr.defineProperty(PrefInputElement, 'dataType', cr.PropertyKind.ATTR); | 
|  | 
| /** | 
| -   * Whether the preference is controlled by something else than the user's | 
| -   * settings (either 'policy' or 'extension'). | 
| -   * @type {string} | 
| +   * Whether this input element is part of a dialog. If so, changes take effect | 
| +   * in the settings UI immediately but are only actually committed when the | 
| +   * user confirms the dialog. If the user cancels the dialog instead, the | 
| +   * changes are rolled back in the settings UI and never committed. | 
| +   * @type {boolean} | 
| */ | 
| -  cr.defineProperty(PrefCheckbox, 'controlledBy', cr.PropertyKind.ATTR); | 
| +  cr.defineProperty(PrefInputElement, 'dialogPref', cr.PropertyKind.BOOL_ATTR); | 
|  | 
| /** | 
| -   * The user metric string. | 
| +   * Whether the associated preference is controlled by a source other than the | 
| +   * user's setting (can be 'policy', 'extension' or 'recommended'). | 
| * @type {string} | 
| */ | 
| -  cr.defineProperty(PrefCheckbox, 'metric', cr.PropertyKind.ATTR); | 
| +  cr.defineProperty(PrefInputElement, 'controlledBy', cr.PropertyKind.ATTR); | 
|  | 
| /** | 
| -   * Whether to use inverted pref value. | 
| -   * @type {boolean} | 
| +   * The user metric string. | 
| +   * @type {string} | 
| */ | 
| -  cr.defineProperty(PrefCheckbox, 'inverted_pref', cr.PropertyKind.BOOL_ATTR); | 
| +  cr.defineProperty(PrefInputElement, 'metric', cr.PropertyKind.ATTR); | 
|  | 
| ///////////////////////////////////////////////////////////////////////////// | 
| -  // PrefRadio class: | 
| +  // PrefCheckbox class: | 
|  | 
| -  //Define a constructor that uses an input element as its underlying element. | 
| -  var PrefRadio = cr.ui.define('input'); | 
| +  // Define a constructor that uses an input element as its underlying element. | 
| +  var PrefCheckbox = cr.ui.define('input'); | 
|  | 
| -  PrefRadio.prototype = { | 
| +  PrefCheckbox.prototype = { | 
| // Set up the prototype chain | 
| -    __proto__: HTMLInputElement.prototype, | 
| - | 
| -    // Stores the initialized value of the preference used to reset the input | 
| -    // in resetPrefState(). | 
| -    storedValue_: null, | 
| +    __proto__: PrefInputElement.prototype, | 
|  | 
| /** | 
| * Initialization function for the cr.ui framework. | 
| */ | 
| decorate: function() { | 
| -      this.type = 'radio'; | 
| -      var self = this; | 
| - | 
| -      // Listen to pref changes. | 
| -      Preferences.getInstance().addEventListener(this.pref, function(event) { | 
| -        self.checked = String(event.value.value) == self.value; | 
| -        updateElementState_(self, event); | 
| -      }); | 
| - | 
| -      // Listen to user events. | 
| -      this.addEventListener('change', this.updatePreferenceValue_.bind(this)); | 
| +      PrefInputElement.prototype.decorate.call(this); | 
| +      this.type = 'checkbox'; | 
| }, | 
|  | 
| /** | 
| -     * Handler that updates the associated pref when the user changes the input | 
| -     * element's value. | 
| -     * @param {Event} event Change event. | 
| +     * Update the associated pref when when the user makes changes to the | 
| +     * checkbox state. | 
| * @private | 
| */ | 
| -    updatePreferenceValue_: function(event) { | 
| -      if (this.customChangeHandler(event)) | 
| -        return; | 
| - | 
| -      if (this.value == 'true' || this.value == 'false') { | 
| -        var value = String(this.value); | 
| -        Preferences.setBooleanPref(this.pref, value == String(this.checked), | 
| -                                   !this.dialogPref, this.metric); | 
| -      } else { | 
| -        Preferences.setIntegerPref(this.pref, parseInt(this.value, 10), | 
| -                                   !this.dialogPref, this.metric); | 
| -      } | 
| -    }, | 
| - | 
| -    /** | 
| -     * See |updateDisabledState_| above. | 
| -     */ | 
| -    setDisabled: function(reason, disabled) { | 
| -      updateDisabledState_(this, reason, disabled); | 
| +    updatePrefFromState_: function() { | 
| +      var value = this.inverted_pref ? !this.checked : this.checked; | 
| +      Preferences.setBooleanPref(this.pref, value, | 
| +                                 !this.dialogPref, this.metric); | 
| }, | 
|  | 
| /** | 
| -     * Handler that is called when the user changes the input element's value. | 
| -     * If it returns false, the default handler is executed next, updating the | 
| -     * associated pref to its new value. If it returns true, the default handler | 
| -     * is skipped (i.e., this works like stopPropagation or cancelBubble). | 
| -     * @param {Event} event Change event. | 
| +     * Update the checkbox state when the associated pref changes. | 
| +     * @param {Event} event Pref change event. | 
| +     * @private | 
| */ | 
| -    customChangeHandler: function(event) { | 
| -      return false; | 
| +    updateStateFromPref_: function(event) { | 
| +      var value = Boolean(event.value.value); | 
| +      this.checked = this.inverted_pref ? !value : value; | 
| }, | 
| }; | 
|  | 
| /** | 
| -   * The preference name. | 
| -   * @type {string} | 
| -   */ | 
| -  cr.defineProperty(PrefRadio, 'pref', cr.PropertyKind.ATTR); | 
| - | 
| -  /** | 
| -   * A special preference type specific to dialogs. Changes take effect in the | 
| -   * settings UI immediately but are only actually committed when the user | 
| -   * confirms the dialog. If the user cancels the dialog instead, the changes | 
| -   * are rolled back in the settings UI and never committed. | 
| +   * Whether the mapping between checkbox state and associated pref is inverted. | 
| * @type {boolean} | 
| */ | 
| -  cr.defineProperty(PrefRadio, 'dialogPref', cr.PropertyKind.BOOL_ATTR); | 
| - | 
| -  /** | 
| -   * Whether the preference is controlled by something else than the user's | 
| -   * settings (either 'policy' or 'extension'). | 
| -   * @type {string} | 
| -   */ | 
| -  cr.defineProperty(PrefRadio, 'controlledBy', cr.PropertyKind.ATTR); | 
| - | 
| -  /** | 
| -   * The user metric string. | 
| -   * @type {string} | 
| -   */ | 
| -  cr.defineProperty(PrefRadio, 'metric', cr.PropertyKind.ATTR); | 
| +  cr.defineProperty(PrefCheckbox, 'inverted_pref', cr.PropertyKind.BOOL_ATTR); | 
|  | 
| ///////////////////////////////////////////////////////////////////////////// | 
| -  // PrefNumeric class: | 
| +  // PrefNumber class: | 
|  | 
| // Define a constructor that uses an input element as its underlying element. | 
| -  var PrefNumeric = function() {}; | 
| -  PrefNumeric.prototype = { | 
| +  var PrefNumber = cr.ui.define('input'); | 
| + | 
| +  PrefNumber.prototype = { | 
| // Set up the prototype chain | 
| -    __proto__: HTMLInputElement.prototype, | 
| +    __proto__: PrefInputElement.prototype, | 
|  | 
| /** | 
| * Initialization function for the cr.ui framework. | 
| */ | 
| decorate: function() { | 
| -      var self = this; | 
| - | 
| -      // Listen to pref changes. | 
| -      Preferences.getInstance().addEventListener(this.pref, function(event) { | 
| -        self.value = event.value.value; | 
| -        updateElementState_(self, event); | 
| -      }); | 
| - | 
| -      // Listen to user events. | 
| -      this.addEventListener('change', function(event) { | 
| -        if (self.validity.valid) { | 
| -          Preferences.setIntegerPref(self.pref, self.value, | 
| -                                     !self.dialogPref, self.metric); | 
| -        } | 
| -      }); | 
| +      PrefInputElement.prototype.decorate.call(this); | 
| +      this.type = 'number'; | 
| }, | 
|  | 
| /** | 
| -     * See |updateDisabledState_| above. | 
| +     * Update the associated pref when when the user inputs a number. | 
| +     * @private | 
| */ | 
| -    setDisabled: function(reason, disabled) { | 
| -      updateDisabledState_(this, reason, disabled); | 
| +    updatePrefFromState_: function() { | 
| +      if (this.validity.valid) { | 
| +        Preferences.setIntegerPref(this.pref, this.value, | 
| +                                   !this.dialogPref, this.metric); | 
| +      } | 
| }, | 
| }; | 
|  | 
| -  /** | 
| -   * The preference name. | 
| -   * @type {string} | 
| -   */ | 
| -  cr.defineProperty(PrefNumeric, 'pref', cr.PropertyKind.ATTR); | 
| - | 
| -  /** | 
| -   * A special preference type specific to dialogs. Changes take effect in the | 
| -   * settings UI immediately but are only actually committed when the user | 
| -   * confirms the dialog. If the user cancels the dialog instead, the changes | 
| -   * are rolled back in the settings UI and never committed. | 
| -   * @type {boolean} | 
| -   */ | 
| -  cr.defineProperty(PrefRadio, 'dialogPref', cr.PropertyKind.BOOL_ATTR); | 
| - | 
| -  /** | 
| -   * Whether the preference is controlled by something else than the user's | 
| -   * settings (either 'policy' or 'extension'). | 
| -   * @type {string} | 
| -   */ | 
| -  cr.defineProperty(PrefNumeric, 'controlledBy', cr.PropertyKind.ATTR); | 
| - | 
| -  /** | 
| -   * The user metric string. | 
| -   * @type {string} | 
| -   */ | 
| -  cr.defineProperty(PrefNumeric, 'metric', cr.PropertyKind.ATTR); | 
| - | 
| ///////////////////////////////////////////////////////////////////////////// | 
| -  // PrefNumber class: | 
| +  // PrefRadio class: | 
|  | 
| -  // Define a constructor that uses an input element as its underlying element. | 
| -  var PrefNumber = cr.ui.define('input'); | 
| +  //Define a constructor that uses an input element as its underlying element. | 
| +  var PrefRadio = cr.ui.define('input'); | 
|  | 
| -  PrefNumber.prototype = { | 
| +  PrefRadio.prototype = { | 
| // Set up the prototype chain | 
| -    __proto__: PrefNumeric.prototype, | 
| +    __proto__: PrefInputElement.prototype, | 
|  | 
| /** | 
| * Initialization function for the cr.ui framework. | 
| */ | 
| decorate: function() { | 
| -      this.type = 'number'; | 
| -      PrefNumeric.prototype.decorate.call(this); | 
| -      var self = this; | 
| +      PrefInputElement.prototype.decorate.call(this); | 
| +      this.type = 'radio'; | 
| +    }, | 
|  | 
| -      // Listen to user events. | 
| -      this.addEventListener('input', | 
| -          function(e) { | 
| -            if (self.validity.valid) { | 
| -              Preferences.setIntegerPref(self.pref, self.value, | 
| -                                         !self.dialogPref, self.metric); | 
| -            } | 
| -          }); | 
| +    /** | 
| +     * Update the associated pref when when the user selects the radio button. | 
| +     * @private | 
| +     */ | 
| +    updatePrefFromState_: function() { | 
| +      if (this.value == 'true' || this.value == 'false') { | 
| +        Preferences.setBooleanPref(this.pref, | 
| +                                   this.value == String(this.checked), | 
| +                                   !this.dialogPref, this.metric); | 
| +      } else { | 
| +        Preferences.setIntegerPref(this.pref, this.value, | 
| +                                   !this.dialogPref, this.metric); | 
| +      } | 
| }, | 
|  | 
| /** | 
| -     * See |updateDisabledState_| above. | 
| +     * Update the radio button state when the associated pref changes. | 
| +     * @param {Event} event Pref change event. | 
| +     * @private | 
| */ | 
| -    setDisabled: function(reason, disabled) { | 
| -      updateDisabledState_(this, reason, disabled); | 
| +    updateStateFromPref_: function(event) { | 
| +      this.checked = this.value == String(event.value.value); | 
| }, | 
| }; | 
|  | 
| @@ -374,10 +270,10 @@ cr.define('options', function() { | 
|  | 
| PrefRange.prototype = { | 
| // Set up the prototype chain | 
| -    __proto__: HTMLInputElement.prototype, | 
| +    __proto__: PrefInputElement.prototype, | 
|  | 
| /** | 
| -     * The map from input range value to the corresponding preference value. | 
| +     * The map from slider position to corresponding pref value. | 
| */ | 
| valueMap: undefined, | 
|  | 
| @@ -385,105 +281,66 @@ cr.define('options', function() { | 
| * Initialization function for the cr.ui framework. | 
| */ | 
| decorate: function() { | 
| +      PrefInputElement.prototype.decorate.call(this); | 
| this.type = 'range'; | 
|  | 
| -      // Update the UI when the pref changes. | 
| -      Preferences.getInstance().addEventListener( | 
| -          this.pref, this.onPrefChange_.bind(this)); | 
| - | 
| -      // Listen to user events. | 
| +      // Listen for user events. | 
| // TODO(jhawkins): Add onmousewheel handling once the associated WK bug is | 
| // fixed. | 
| // https://bugs.webkit.org/show_bug.cgi?id=52256 | 
| -      this.addEventListener('keyup', this.updatePreferenceValue_.bind(this)); | 
| -      this.addEventListener('mouseup', this.updatePreferenceValue_.bind(this)); | 
| +      this.addEventListener('keyup', this.handleRelease_.bind(this)); | 
| +      this.addEventListener('mouseup', this.handleRelease_.bind(this)); | 
| }, | 
|  | 
| /** | 
| -     * Event listener that updates the input element when the associated pref | 
| -     * changes. | 
| -     * @param {Event} event The event that details the pref change. | 
| +     * Update the associated pref when when the user releases the slider. | 
| * @private | 
| */ | 
| -    onPrefChange_: function(event) { | 
| -      var value = event.value.value; | 
| -      this.value = this.valueMap ? this.valueMap.indexOf(value) : value; | 
| +    updatePrefFromState_: function() { | 
| +      Preferences.setIntegerPref(this.pref, this.mapPositionToPref(this.value), | 
| +                                 !this.dialogPref, this.metric); | 
| }, | 
|  | 
| /** | 
| -     * Handler that updates the associated pref when the user changes the input | 
| -     * element's value. | 
| -     * This handler is called when the user completes the change by releasing | 
| -     * the slider. | 
| -     * @param {Event} event Change event. | 
| +     * Ignore changes to the slider position made by the user while the slider | 
| +     * has not been released. | 
| * @private | 
| */ | 
| -    updatePreferenceValue_: function(event) { | 
| -      if (this.customChangeHandler(event)) | 
| -        return; | 
| - | 
| -      Preferences.setIntegerPref(this.pref, this.mapValueToRange(this.value), | 
| -                                 !this.dialogPref, this.metric); | 
| +    handleChange_: function() { | 
| }, | 
|  | 
| /** | 
| -     * Maps the value of this element into the range provided by the client, | 
| -     * represented by |valueMap|. | 
| -     * @param {number} value The value to map. | 
| +     * Handle changes to the slider position made by the user when the slider is | 
| +     * released. If a custom change handler does not suppress it, a default | 
| +     * handler is invoked that updates the associated pref. | 
| +     * @param {Event} event Change event. | 
| +     * @private | 
| */ | 
| -    mapValueToRange: function(value) { | 
| -      return this.valueMap ? this.valueMap[value] : value; | 
| +    handleRelease_: function(event) { | 
| +      if (!this.customChangeHandler(event)) | 
| +        this.updatePrefFromState_(); | 
| }, | 
|  | 
| /** | 
| -     * See |updateDisabledState_| above. | 
| +     * Update the slider position when the associated pref changes. | 
| +     * @param {Event} event Pref change event. | 
| +     * @private | 
| */ | 
| -    setDisabled: function(reason, disabled) { | 
| -      updateDisabledState_(this, reason, disabled); | 
| +    updateStateFromPref_: function(event) { | 
| +      var value = event.value.value; | 
| +      this.value = this.valueMap ? this.valueMap.indexOf(value) : value; | 
| }, | 
|  | 
| /** | 
| -     * Handler that is called when the user changes the input element's value. | 
| -     * If it returns false, the default handler is executed next, updating the | 
| -     * associated pref to its new value. If it returns true, the default handler | 
| -     * is skipped (i.e., this works like stopPropagation or cancelBubble). | 
| -     * This handler is called when the user completes the change by releasing | 
| -     * the slider. | 
| -     * @param {Event} event Change event. | 
| +     * Map slider position to the range of values provided by the client, | 
| +     * represented by |valueMap|. | 
| +     * @param {number} position The slider position to map. | 
| */ | 
| -    customChangeHandler: function(event) { | 
| -      return false; | 
| +    mapPositionToPref: function(position) { | 
| +      return this.valueMap ? this.valueMap[position] : position; | 
| }, | 
| }; | 
|  | 
| -  /** | 
| -   * The preference name. | 
| -   * @type {string} | 
| -   */ | 
| -  cr.defineProperty(PrefRange, 'pref', cr.PropertyKind.ATTR); | 
| - | 
| -  /** | 
| -   * A special preference type specific to dialogs. Changes take effect in the | 
| -   * settings UI immediately but are only actually committed when the user | 
| -   * confirms the dialog. If the user cancels the dialog instead, the changes | 
| -   * are rolled back in the settings UI and never committed. | 
| -   * @type {boolean} | 
| -   */ | 
| -  cr.defineProperty(PrefRadio, 'dialogPref', cr.PropertyKind.BOOL_ATTR); | 
| - | 
| -  /** | 
| -   * Whether the preference is controlled by something else than the user's | 
| -   * settings (either 'policy' or 'extension'). | 
| -   * @type {string} | 
| -   */ | 
| -  cr.defineProperty(PrefRange, 'controlledBy', cr.PropertyKind.ATTR); | 
| - | 
| -  /** | 
| -   * The user metric string. | 
| -   * @type {string} | 
| -   */ | 
| -  cr.defineProperty(PrefRange, 'metric', cr.PropertyKind.ATTR); | 
| - | 
| ///////////////////////////////////////////////////////////////////////////// | 
| // PrefSelect class: | 
|  | 
| @@ -492,127 +349,67 @@ cr.define('options', function() { | 
|  | 
| PrefSelect.prototype = { | 
| // Set up the prototype chain | 
| -    __proto__: HTMLSelectElement.prototype, | 
| +    __proto__: PrefInputElement.prototype, | 
|  | 
| /** | 
| -    * Initialization function for the cr.ui framework. | 
| -    */ | 
| -    decorate: function() { | 
| -      // Listen to pref changes. | 
| -      Preferences.getInstance().addEventListener( | 
| -          this.pref, this.onPrefChange_.bind(this)); | 
| - | 
| -      // Listen to user events. | 
| -      this.addEventListener('change', this.updatePreferenceValue_.bind(this)); | 
| -    }, | 
| - | 
| -    /** | 
| -     * Event listener that updates the input element when the associated pref | 
| -     * changes. | 
| -     * @param {Event} event The event that details the pref change. | 
| +     * Update the associated pref when when the user selects an item. | 
| * @private | 
| */ | 
| -    onPrefChange_: function(event) { | 
| -      // Make sure |value| is a string, because the value is stored as a string | 
| -      // in the HTMLOptionElement. | 
| -      var value = event.value.value.toString(); | 
| - | 
| -      updateElementState_(this, event); | 
| - | 
| -      var found = false; | 
| -      for (var i = 0; i < this.options.length; i++) { | 
| -        if (this.options[i].value == value) { | 
| -          this.selectedIndex = i; | 
| -          found = true; | 
| -        } | 
| -      } | 
| - | 
| -      // Item not found, select first item. | 
| -      if (!found) | 
| -        this.selectedIndex = 0; | 
| - | 
| -      if (this.onchange) | 
| -        this.onchange(event); | 
| -    }, | 
| - | 
| -    /** | 
| -     * Handler that updates the associated pref when the user changes the input | 
| -     * element's value. | 
| -     * @param {Event} event Change event. | 
| -     * @private | 
| -     */ | 
| -    updatePreferenceValue_: function(event) { | 
| -      if (!this.dataType) { | 
| -        console.error('undefined data type for <select> pref'); | 
| -        return; | 
| -      } | 
| - | 
| -      var prefValue = this.options[this.selectedIndex].value; | 
| +    updatePrefFromState_: function() { | 
| +      var value = this.options[this.selectedIndex].value; | 
| switch (this.dataType) { | 
| case 'number': | 
| -          Preferences.setIntegerPref(this.pref, prefValue, | 
| +          Preferences.setIntegerPref(this.pref, value, | 
| !this.dialogPref, this.metric); | 
| break; | 
| case 'double': | 
| -          Preferences.setDoublePref(this.pref, prefValue, | 
| +          Preferences.setDoublePref(this.pref, value, | 
| !this.dialogPref, this.metric); | 
| break; | 
| case 'boolean': | 
| -          var value = (prefValue == 'true'); | 
| -          Preferences.setBooleanPref(this.pref, value, | 
| +          Preferences.setBooleanPref(this.pref, value == 'true', | 
| !this.dialogPref, this.metric); | 
| break; | 
| case 'string': | 
| -          Preferences.setStringPref(this.pref, prefValue, | 
| +          Preferences.setStringPref(this.pref, value, | 
| !this.dialogPref, this.metric); | 
| break; | 
| default: | 
| -          console.error('unknown data type for <select> pref: ' + | 
| +          console.error('Unknown data type for <select> UI element: ' + | 
| this.dataType); | 
| } | 
| }, | 
|  | 
| /** | 
| -     * See |updateDisabledState_| above. | 
| +     * Update the selected item when the associated pref changes. | 
| +     * @param {Event} event Pref change event. | 
| +     * @private | 
| */ | 
| -    setDisabled: function(reason, disabled) { | 
| -      updateDisabledState_(this, reason, disabled); | 
| -    }, | 
| -  }; | 
| - | 
| -  /** | 
| -   * The preference name. | 
| -   * @type {string} | 
| -   */ | 
| -  cr.defineProperty(PrefSelect, 'pref', cr.PropertyKind.ATTR); | 
| - | 
| -  /** | 
| -   * Whether the preference is controlled by something else than the user's | 
| -   * settings (either 'policy' or 'extension'). | 
| -   * @type {string} | 
| -   */ | 
| -  cr.defineProperty(PrefSelect, 'controlledBy', cr.PropertyKind.ATTR); | 
| +    updateStateFromPref_: function(event) { | 
| +      // Make sure the value is a string, because the value is stored as a | 
| +      // string in the HTMLOptionElement. | 
| +      value = String(event.value.value); | 
|  | 
| -  /** | 
| -   * A special preference type specific to dialogs. Changes take effect in the | 
| -   * settings UI immediately but are only actually committed when the user | 
| -   * confirms the dialog. If the user cancels the dialog instead, the changes | 
| -   * are rolled back in the settings UI and never committed. | 
| -   * @type {boolean} | 
| -   */ | 
| -  cr.defineProperty(PrefSelect, 'dialogPref', cr.PropertyKind.BOOL_ATTR); | 
| +      var found = false; | 
| +      for (var i = 0; i < this.options.length; i++) { | 
| +        if (this.options[i].value == value) { | 
| +          this.selectedIndex = i; | 
| +          found = true; | 
| +        } | 
| +      } | 
|  | 
| -  /** | 
| -   * The user metric string. | 
| -   * @type {string} | 
| -   */ | 
| -  cr.defineProperty(PrefSelect, 'metric', cr.PropertyKind.ATTR); | 
| +      // Item not found, select first item. | 
| +      if (!found) | 
| +        this.selectedIndex = 0; | 
|  | 
| -  /** | 
| -   * The data type for the preference options. | 
| -   * @type {string} | 
| -   */ | 
| -  cr.defineProperty(PrefSelect, 'dataType', cr.PropertyKind.ATTR); | 
| +      // The "onchange" event automatically fires when the user makes a manual | 
| +      // change. It should never be fired for a programmatic change. However, | 
| +      // these two lines were here already and it is hard to tell who may be | 
| +      // relying on them. | 
| +      if (this.onchange) | 
| +        this.onchange(event); | 
| +    }, | 
| +  }; | 
|  | 
| ///////////////////////////////////////////////////////////////////////////// | 
| // PrefTextField class: | 
| @@ -622,36 +419,27 @@ cr.define('options', function() { | 
|  | 
| PrefTextField.prototype = { | 
| // Set up the prototype chain | 
| -    __proto__: HTMLInputElement.prototype, | 
| +    __proto__: PrefInputElement.prototype, | 
|  | 
| /** | 
| * Initialization function for the cr.ui framework. | 
| */ | 
| decorate: function() { | 
| +      PrefInputElement.prototype.decorate.call(this); | 
| var self = this; | 
|  | 
| -      // Listen to pref changes. | 
| -      Preferences.getInstance().addEventListener(this.pref, function(event) { | 
| -        self.value = event.value.value; | 
| -        updateElementState_(self, event); | 
| -      }); | 
| - | 
| -      // Listen to user events. | 
| -      this.addEventListener('change', this.updatePreferenceValue_.bind(this)); | 
| - | 
| -      window.addEventListener('unload', function(event) { | 
| +      // Listen for user events. | 
| +      window.addEventListener('unload', function() { | 
| if (document.activeElement == self) | 
| self.blur(); | 
| }); | 
| }, | 
|  | 
| /** | 
| -     * Handler that updates the associated pref when the user changes the input | 
| -     * element's value. | 
| -     * @param {Event} event Change event. | 
| +     * Update the associated pref when when the user inputs text. | 
| * @private | 
| */ | 
| -    updatePreferenceValue_: function(event) { | 
| +    updatePrefFromState_: function(event) { | 
| switch (this.dataType) { | 
| case 'number': | 
| Preferences.setIntegerPref(this.pref, this.value, | 
| @@ -671,49 +459,8 @@ cr.define('options', function() { | 
| break; | 
| } | 
| }, | 
| - | 
| -    /** | 
| -     * See |updateDisabledState_| above. | 
| -     */ | 
| -    setDisabled: function(reason, disabled) { | 
| -      updateDisabledState_(this, reason, disabled); | 
| -    }, | 
| }; | 
|  | 
| -  /** | 
| -   * The preference name. | 
| -   * @type {string} | 
| -   */ | 
| -  cr.defineProperty(PrefTextField, 'pref', cr.PropertyKind.ATTR); | 
| - | 
| -  /** | 
| -   * A special preference type specific to dialogs. Changes take effect in the | 
| -   * settings UI immediately but are only actually committed when the user | 
| -   * confirms the dialog. If the user cancels the dialog instead, the changes | 
| -   * are rolled back in the settings UI and never committed. | 
| -   * @type {boolean} | 
| -   */ | 
| -  cr.defineProperty(PrefTextField, 'dialogPref', cr.PropertyKind.BOOL_ATTR); | 
| - | 
| -  /** | 
| -   * Whether the preference is controlled by something else than the user's | 
| -   * settings (either 'policy' or 'extension'). | 
| -   * @type {string} | 
| -   */ | 
| -  cr.defineProperty(PrefTextField, 'controlledBy', cr.PropertyKind.ATTR); | 
| - | 
| -  /** | 
| -   * The user metric string. | 
| -   * @type {string} | 
| -   */ | 
| -  cr.defineProperty(PrefTextField, 'metric', cr.PropertyKind.ATTR); | 
| - | 
| -  /** | 
| -   * The data type for the preference options. | 
| -   * @type {string} | 
| -   */ | 
| -  cr.defineProperty(PrefTextField, 'dataType', cr.PropertyKind.ATTR); | 
| - | 
| ///////////////////////////////////////////////////////////////////////////// | 
| // PrefButton class: | 
|  | 
| @@ -725,24 +472,22 @@ cr.define('options', function() { | 
| __proto__: HTMLButtonElement.prototype, | 
|  | 
| /** | 
| -    * Initialization function for the cr.ui framework. | 
| -    */ | 
| +     * Initialization function for the cr.ui framework. | 
| +     */ | 
| decorate: function() { | 
| var self = this; | 
|  | 
| -      // Listen to pref changes. This element behaves like a normal button and | 
| -      // doesn't affect the underlying preference; it just becomes disabled | 
| -      // when the preference is managed, and its value is false. | 
| -      // This is useful for buttons that should be disabled when the underlying | 
| -      // boolean preference is set to false by a policy or extension. | 
| +      // Listen for pref changes. | 
| +      // This element behaves like a normal button and does not affect the | 
| +      // underlying preference; it just becomes disabled when the preference is | 
| +      // managed, and its value is false. This is useful for buttons that should | 
| +      // be disabled when the underlying Boolean preference is set to false by a | 
| +      // policy or extension. | 
| Preferences.getInstance().addEventListener(this.pref, function(event) { | 
| -        var e = { | 
| -          value: { | 
| -            'disabled': event.value.disabled && !event.value.value, | 
| -            'controlledBy': event.value.controlledBy | 
| -          } | 
| -        }; | 
| -        updateElementState_(self, e); | 
| +        updateDisabledState_(self, 'notUserModifiable', | 
| +                             event.value.disabled && !event.value.value); | 
| +        self.controlledBy = event.value.controlledBy; | 
| +        OptionsPage.updateManagedBannerVisibility(); | 
| }); | 
| }, | 
|  | 
| @@ -755,14 +500,14 @@ cr.define('options', function() { | 
| }; | 
|  | 
| /** | 
| -   * The preference name. | 
| +   * The name of the associated preference. | 
| * @type {string} | 
| */ | 
| cr.defineProperty(PrefButton, 'pref', cr.PropertyKind.ATTR); | 
|  | 
| /** | 
| -   * Whether the preference is controlled by something else than the user's | 
| -   * settings (either 'policy' or 'extension'). | 
| +   * Whether the associated preference is controlled by a source other than the | 
| +   * user's setting (can be 'policy', 'extension' or 'recommended'). | 
| * @type {string} | 
| */ | 
| cr.defineProperty(PrefButton, 'controlledBy', cr.PropertyKind.ATTR); | 
| @@ -771,7 +516,6 @@ cr.define('options', function() { | 
| return { | 
| PrefCheckbox: PrefCheckbox, | 
| PrefNumber: PrefNumber, | 
| -    PrefNumeric: PrefNumeric, | 
| PrefRadio: PrefRadio, | 
| PrefRange: PrefRange, | 
| PrefSelect: PrefSelect, | 
|  |