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   var Preferences = options.Preferences;
      7 
      8   /**
      9    * A controlled setting indicator that can be placed on a setting as an
     10    * indicator that the value is controlled by some external entity such as
     11    * policy or an extension.
     12    * @constructor
     13    * @extends {HTMLSpanElement}
     14    */
     15   var ControlledSettingIndicator = cr.ui.define('span');
     16 
     17   ControlledSettingIndicator.prototype = {
     18     __proto__: cr.ui.BubbleButton.prototype,
     19 
     20     /**
     21      * Decorates the base element to show the proper icon.
     22      */
     23     decorate: function() {
     24       cr.ui.BubbleButton.prototype.decorate.call(this);
     25       this.classList.add('controlled-setting-indicator');
     26 
     27       // If there is a pref, track its controlledBy and recommendedValue
     28       // properties in order to be able to bring up the correct bubble.
     29       if (this.pref) {
     30         Preferences.getInstance().addEventListener(
     31             this.pref, this.handlePrefChange.bind(this));
     32         this.resetHandler = this.clearAssociatedPref_;
     33       }
     34     },
     35 
     36     /**
     37      * The given handler will be called when the user clicks on the 'reset to
     38      * recommended value' link shown in the indicator bubble. The |this| object
     39      * will be the indicator itself.
     40      * @param {function()} handler The handler to be called.
     41      */
     42     set resetHandler(handler) {
     43       this.resetHandler_ = handler;
     44     },
     45 
     46     /**
     47      * Clears the preference associated with this indicator.
     48      * @private
     49      */
     50     clearAssociatedPref_: function() {
     51       Preferences.clearPref(this.pref, !this.dialogPref);
     52     },
     53 
     54     /* Handle changes to the associated pref by hiding any currently visible
     55      * bubble and updating the controlledBy property.
     56      * @param {Event} event Pref change event.
     57      */
     58     handlePrefChange: function(event) {
     59       OptionsPage.hideBubble();
     60       if (event.value.controlledBy) {
     61         if (!this.value || String(event.value.value) == this.value) {
     62           this.controlledBy = event.value.controlledBy;
     63           if (event.value.extension) {
     64             if (this.pref == 'session.restore_on_startup' ||
     65                 this.pref == 'homepage_is_newtabpage') {
     66               // Special case for the restore on startup, which is implied
     67               // by the startup pages settings being controlled by an
     68               // extension, and same for the home page as NTP, so we don't want
     69               // to show two buttons in these cases.
     70               // TODO(mad): Find a better way to handle this.
     71               this.controlledBy = null;
     72             } else {
     73               this.extensionId = event.value.extension.id;
     74               this.extensionIcon = event.value.extension.icon;
     75               this.extensionName = event.value.extension.name;
     76             }
     77           }
     78         } else {
     79           this.controlledBy = null;
     80         }
     81       } else if (event.value.recommendedValue != undefined) {
     82         this.controlledBy =
     83             !this.value || String(event.value.recommendedValue) == this.value ?
     84             'hasRecommendation' : null;
     85       } else {
     86         this.controlledBy = null;
     87       }
     88     },
     89 
     90     /**
     91      * Open or close a bubble with further information about the pref.
     92      * @private
     93      */
     94     toggleBubble_: function() {
     95       if (this.showingBubble) {
     96         OptionsPage.hideBubble();
     97       } else {
     98         var self = this;
     99 
    100         // Construct the bubble text.
    101         if (this.hasAttribute('plural')) {
    102           var defaultStrings = {
    103             'policy': loadTimeData.getString('controlledSettingsPolicy'),
    104             'extension': loadTimeData.getString('controlledSettingsExtension'),
    105             'extensionWithName': loadTimeData.getString(
    106                 'controlledSettingsExtensionWithName'),
    107           };
    108         } else {
    109           var defaultStrings = {
    110             'policy': loadTimeData.getString('controlledSettingPolicy'),
    111             'extension': loadTimeData.getString('controlledSettingExtension'),
    112             'extensionWithName': loadTimeData.getString(
    113                 'controlledSettingExtensionWithName'),
    114             'recommended':
    115                 loadTimeData.getString('controlledSettingRecommended'),
    116             'hasRecommendation':
    117                 loadTimeData.getString('controlledSettingHasRecommendation'),
    118           };
    119           if (cr.isChromeOS) {
    120             defaultStrings.owner =
    121                 loadTimeData.getString('controlledSettingOwner');
    122           }
    123         }
    124 
    125         // No controller, no bubble.
    126         if (!this.controlledBy || !(this.controlledBy in defaultStrings))
    127           return;
    128 
    129         var text = defaultStrings[this.controlledBy];
    130         if (this.controlledBy == 'extension' && this.extensionName)
    131           text = defaultStrings.extensionWithName;
    132 
    133         // Apply text overrides.
    134         if (this.hasAttribute('text' + this.controlledBy))
    135           text = this.getAttribute('text' + this.controlledBy);
    136 
    137         // Create the DOM tree.
    138         var content = document.createElement('div');
    139         content.className = 'controlled-setting-bubble-content';
    140         content.setAttribute('controlled-by', this.controlledBy);
    141         content.textContent = text;
    142 
    143         if (this.controlledBy == 'hasRecommendation' && this.resetHandler_ &&
    144             !this.readOnly) {
    145           var container = document.createElement('div');
    146           var action = document.createElement('button');
    147           action.classList.add('link-button');
    148           action.classList.add('controlled-setting-bubble-action');
    149           action.textContent =
    150               loadTimeData.getString('controlledSettingFollowRecommendation');
    151           action.addEventListener('click', function(event) {
    152             self.resetHandler_();
    153           });
    154           container.appendChild(action);
    155           content.appendChild(container);
    156         } else if (this.controlledBy == 'extension' && this.extensionName) {
    157           var extensionContainer =
    158               $('extension-controlled-settings-bubble-template').
    159                   cloneNode(true);
    160           // No need for an id anymore, and thus remove to avoid id collision.
    161           extensionContainer.removeAttribute('id');
    162           extensionContainer.hidden = false;
    163 
    164           var extensionName = extensionContainer.querySelector(
    165               '.controlled-setting-bubble-extension-name');
    166           extensionName.textContent = this.extensionName;
    167           extensionName.style.backgroundImage =
    168               'url("' + this.extensionIcon + '")';
    169 
    170           var manageLink = extensionContainer.querySelector(
    171               '.controlled-setting-bubble-extension-manage-link');
    172           manageLink.onclick = function() {
    173             uber.invokeMethodOnWindow(
    174                 window.top, 'showPage', {pageId: 'extensions'});
    175           };
    176 
    177           var disableButton = extensionContainer.querySelector('button');
    178           var extensionId = this.extensionId;
    179           disableButton.onclick = function() {
    180             chrome.send('disableExtension', [extensionId]);
    181           };
    182           content.appendChild(extensionContainer);
    183         }
    184 
    185         OptionsPage.showBubble(content, this.image, this, this.location);
    186       }
    187     },
    188   };
    189 
    190   /**
    191    * The name of the associated preference.
    192    * @type {string}
    193    */
    194   cr.defineProperty(ControlledSettingIndicator, 'pref', cr.PropertyKind.ATTR);
    195 
    196   /**
    197    * Whether this indicator is part of a dialog. If so, changes made to the
    198    * associated preference take effect in the settings UI immediately but are
    199    * only actually committed when the user confirms the dialog. If the user
    200    * cancels the dialog instead, the changes are rolled back in the settings UI
    201    * and never committed.
    202    * @type {boolean}
    203    */
    204   cr.defineProperty(ControlledSettingIndicator, 'dialogPref',
    205                     cr.PropertyKind.BOOL_ATTR);
    206 
    207   /**
    208    * The value of the associated preference that the indicator represents. If
    209    * this is not set, the indicator will be visible whenever any value is
    210    * enforced or recommended. If it is set, the indicator will be visible only
    211    * when the enforced or recommended value matches the value it represents.
    212    * This allows multiple indicators to be created for a set of radio buttons,
    213    * ensuring that only one of them is visible at a time.
    214    */
    215   cr.defineProperty(ControlledSettingIndicator, 'value',
    216                     cr.PropertyKind.ATTR);
    217 
    218   /**
    219    * The status of the associated preference:
    220    * - 'policy':            A specific value is enfoced by policy.
    221    * - 'extension':         A specific value is enforced by an extension.
    222    * - 'recommended':       A value is recommended by policy. The user could
    223    *                        override this recommendation but has not done so.
    224    * - 'hasRecommendation': A value is recommended by policy. The user has
    225    *                        overridden this recommendation.
    226    * - unset:               The value is controlled by the user alone.
    227    * @type {string}
    228    */
    229   cr.defineProperty(ControlledSettingIndicator, 'controlledBy',
    230                     cr.PropertyKind.ATTR);
    231 
    232   // Export.
    233   return {
    234     ControlledSettingIndicator: ControlledSettingIndicator
    235   };
    236 });
    237