Home | History | Annotate | Download | only in help
      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 <include src="../uber/uber_utils.js">
      6 
      7 cr.define('help', function() {
      8   /**
      9    * Encapsulated handling of the help page.
     10    */
     11   function HelpPage() {}
     12 
     13   cr.addSingletonGetter(HelpPage);
     14 
     15   HelpPage.prototype = {
     16     __proto__: HTMLDivElement.prototype,
     17 
     18     /**
     19      * True if after update powerwash button should be displayed.
     20      * @private
     21      */
     22     powerwashAfterUpdate_: false,
     23 
     24     /**
     25      * List of the channels names.
     26      * @private
     27      */
     28     channelList_: ['dev-channel', 'beta-channel', 'stable-channel'],
     29 
     30     /**
     31      * Bubble for error messages and notifications.
     32      * @private
     33      */
     34     bubble_: null,
     35 
     36     /**
     37      * Name of the channel the device is currently on.
     38      * @private
     39      */
     40     currentChannel_: null,
     41 
     42     /**
     43      * Name of the channel the device is supposed to be on.
     44      * @private
     45      */
     46     targetChannel_: null,
     47 
     48     /**
     49      * Perform initial setup.
     50      */
     51     initialize: function() {
     52       var self = this;
     53 
     54       uber.onContentFrameLoaded();
     55 
     56       // Set the title.
     57       var title = loadTimeData.getString('helpTitle');
     58       uber.invokeMethodOnParent('setTitle', {title: title});
     59 
     60       $('product-license').innerHTML = loadTimeData.getString('productLicense');
     61       if (cr.isChromeOS) {
     62         $('product-os-license').innerHTML =
     63             loadTimeData.getString('productOsLicense');
     64       }
     65 
     66       var productTOS = $('product-tos');
     67       if (productTOS)
     68         productTOS.innerHTML = loadTimeData.getString('productTOS');
     69 
     70       $('get-help').onclick = function() {
     71         chrome.send('openHelpPage');
     72       };
     73       $('report-issue').onclick = function() {
     74         chrome.send('openFeedbackDialog');
     75       };
     76 
     77       this.maybeSetOnClick_($('more-info-expander'),
     78           this.toggleMoreInfo_.bind(this));
     79 
     80       this.maybeSetOnClick_($('promote'), function() {
     81         chrome.send('promoteUpdater');
     82       });
     83       this.maybeSetOnClick_($('relaunch'), function() {
     84         chrome.send('relaunchNow');
     85       });
     86       if (cr.isChromeOS) {
     87         this.maybeSetOnClick_($('relaunch-and-powerwash'), function() {
     88           chrome.send('relaunchAndPowerwash');
     89         });
     90 
     91         this.channelTable_ = {
     92           'stable-channel': {
     93               'name': loadTimeData.getString('stable'),
     94               'label': loadTimeData.getString('currentChannelStable'),
     95           },
     96           'beta-channel': {
     97               'name': loadTimeData.getString('beta'),
     98               'label': loadTimeData.getString('currentChannelBeta')
     99           },
    100           'dev-channel': {
    101               'name': loadTimeData.getString('dev'),
    102               'label': loadTimeData.getString('currentChannelDev')
    103           }
    104         };
    105       }
    106 
    107       var channelChanger = $('channel-changer');
    108       if (channelChanger) {
    109         channelChanger.onchange = function(event) {
    110           self.setChannel_(event.target.value, false);
    111         };
    112       }
    113 
    114       if (cr.isChromeOS) {
    115         cr.ui.overlay.setupOverlay($('overlay-container'));
    116         cr.ui.overlay.globalInitialization();
    117         $('overlay-container').addEventListener('cancelOverlay', function() {
    118           self.showOverlay_(null);
    119         });
    120         $('change-channel').onclick = function() {
    121           self.showOverlay_($('channel-change-page'));
    122         };
    123 
    124         var channelChangeDisallowedError = document.createElement('div');
    125         channelChangeDisallowedError.className = 'channel-change-error-bubble';
    126 
    127         var channelChangeDisallowedIcon = document.createElement('div');
    128         channelChangeDisallowedIcon.classList.add('help-page-icon-large');
    129         channelChangeDisallowedIcon.classList.add('channel-change-error-icon');
    130         channelChangeDisallowedError.appendChild(channelChangeDisallowedIcon);
    131 
    132         var channelChangeDisallowedText = document.createElement('div');
    133         channelChangeDisallowedText.className = 'channel-change-error-text';
    134         channelChangeDisallowedText.textContent =
    135             loadTimeData.getString('channelChangeDisallowedMessage');
    136         channelChangeDisallowedError.appendChild(channelChangeDisallowedText);
    137 
    138         $('channel-change-disallowed-icon').onclick = function() {
    139             self.showBubble_(channelChangeDisallowedError,
    140                              $('help-container'),
    141                              $('channel-change-disallowed-icon'),
    142                              cr.ui.ArrowLocation.TOP_END);
    143         };
    144       }
    145 
    146       cr.ui.FocusManager.disableMouseFocusOnButtons();
    147 
    148       // Attempt to update.
    149       chrome.send('onPageLoaded');
    150     },
    151 
    152     /**
    153      * Shows the bubble.
    154      * @param {HTMLDivElement} content The content of the bubble.
    155      * @param {HTMLElement} target The element at which the bubble points.
    156      * @param {HTMLElement} domSibling The element after which the bubble is
    157      *     added to the DOM.
    158      * @param {cr.ui.ArrowLocation} location The arrow location.
    159      * @private
    160      */
    161     showBubble_: function(content, domSibling, target, location) {
    162       if (!cr.isChromeOS)
    163         return;
    164       this.hideBubble_();
    165       var bubble = new cr.ui.AutoCloseBubble;
    166       bubble.anchorNode = target;
    167       bubble.domSibling = domSibling;
    168       bubble.arrowLocation = location;
    169       bubble.content = content;
    170       bubble.show();
    171       this.bubble_ = bubble;
    172     },
    173 
    174     /**
    175      * Hides the bubble.
    176      * @private
    177      */
    178     hideBubble_: function() {
    179       if (!cr.isChromeOS)
    180         return;
    181       if (this.bubble_)
    182         this.bubble_.hide();
    183     },
    184 
    185     /**
    186      * Toggles the visible state of the 'More Info' section.
    187      * @private
    188      */
    189     toggleMoreInfo_: function() {
    190       var moreInfo = $('more-info-container');
    191       var visible = moreInfo.className == 'visible';
    192       moreInfo.className = visible ? '' : 'visible';
    193       moreInfo.style.height = visible ? '' : moreInfo.scrollHeight + 'px';
    194       moreInfo.addEventListener('webkitTransitionEnd', function(event) {
    195         $('more-info-expander').textContent = visible ?
    196             loadTimeData.getString('showMoreInfo') :
    197             loadTimeData.getString('hideMoreInfo');
    198       });
    199     },
    200 
    201     /**
    202      * Assigns |method| to the onclick property of |el| if |el| exists.
    203      * @private
    204      */
    205     maybeSetOnClick_: function(el, method) {
    206       if (el)
    207         el.onclick = method;
    208     },
    209 
    210     /**
    211      * @private
    212      */
    213     setUpdateImage_: function(state) {
    214       $('update-status-icon').className = 'help-page-icon ' + state;
    215     },
    216 
    217     /**
    218      * Returns current overlay.
    219      * @return {HTMLElement} Current overlay
    220      * @private
    221      */
    222     getCurrentOverlay_: function() {
    223       return document.querySelector('#overlay .page.showing');
    224     },
    225 
    226     /**
    227      * @return {boolean} True, if new channel switcher UI is used,
    228      *    false otherwise.
    229      * @private
    230      */
    231     isNewChannelSwitcherUI_: function() {
    232       return !loadTimeData.valueExists('disableNewChannelSwitcherUI');
    233     },
    234 
    235     /**
    236      * @return {boolean} True if target and current channels are not
    237      *     null and not equals
    238      * @private
    239      */
    240     channelsDiffer_: function() {
    241       var current = this.currentChannel_;
    242       var target = this.targetChannel_;
    243       return (current != null && target != null && current != target);
    244     },
    245 
    246     /**
    247      * @private
    248      */
    249     setUpdateStatus_: function(status, message) {
    250       var channel = this.targetChannel_;
    251       if (status == 'checking') {
    252         this.setUpdateImage_('working');
    253         $('update-status-message').innerHTML =
    254             loadTimeData.getString('updateCheckStarted');
    255       } else if (status == 'updating') {
    256         this.setUpdateImage_('working');
    257         if (this.channelsDiffer_()) {
    258           $('update-status-message').innerHTML =
    259               loadTimeData.getStringF('updatingChannelSwitch',
    260                                       this.channelTable_[channel].label);
    261         } else {
    262           $('update-status-message').innerHTML =
    263               loadTimeData.getStringF('updating');
    264         }
    265       } else if (status == 'nearly_updated') {
    266         this.setUpdateImage_('up-to-date');
    267         if (this.channelsDiffer_()) {
    268           $('update-status-message').innerHTML =
    269               loadTimeData.getString('successfulChannelSwitch');
    270         } else {
    271           $('update-status-message').innerHTML =
    272               loadTimeData.getString('updateAlmostDone');
    273         }
    274       } else if (status == 'updated') {
    275         this.setUpdateImage_('up-to-date');
    276         $('update-status-message').innerHTML =
    277             loadTimeData.getString('upToDate');
    278       } else if (status == 'failed') {
    279         this.setUpdateImage_('failed');
    280         $('update-status-message').innerHTML = message;
    281       }
    282 
    283       var container = $('update-status-container');
    284       if (container) {
    285         container.hidden = status == 'disabled';
    286         $('relaunch').hidden = status != 'nearly_updated';
    287 
    288         if (!cr.isMac)
    289           $('update-percentage').hidden = status != 'updating';
    290       }
    291 
    292       if ($('relaunch-and-powerwash')) {
    293         // It's allowed to do powerwash only for customer devices,
    294         // when user explicitly decides to update to a more stable
    295         // channel.
    296         $('relaunch-and-powerwash').hidden =
    297             !this.powerwashAfterUpdate_ || status != 'nearly_updated';
    298       }
    299     },
    300 
    301     /**
    302      * @private
    303      */
    304     setProgress_: function(progress) {
    305       $('update-percentage').innerHTML = progress + '%';
    306     },
    307 
    308     /**
    309      * @private
    310      */
    311     setAllowedConnectionTypesMsg_: function(message) {
    312       $('allowed-connection-types-message').innerText = message;
    313     },
    314 
    315     /**
    316      * @private
    317      */
    318     showAllowedConnectionTypesMsg_: function(visible) {
    319       $('allowed-connection-types-message').hidden = !visible;
    320     },
    321 
    322     /**
    323      * @private
    324      */
    325     setPromotionState_: function(state) {
    326       if (state == 'hidden') {
    327         $('promote').hidden = true;
    328       } else if (state == 'enabled') {
    329         $('promote').disabled = false;
    330         $('promote').hidden = false;
    331       } else if (state == 'disabled') {
    332         $('promote').disabled = true;
    333         $('promote').hidden = false;
    334       }
    335     },
    336 
    337     /**
    338      * @private
    339      */
    340     setOSVersion_: function(version) {
    341       if (!cr.isChromeOS)
    342         console.error('OS version unsupported on non-CrOS');
    343 
    344       $('os-version').parentNode.hidden = (version == '');
    345       $('os-version').textContent = version;
    346     },
    347 
    348     /**
    349      * @private
    350      */
    351     setOSFirmware_: function(firmware) {
    352       if (!cr.isChromeOS)
    353         console.error('OS firmware unsupported on non-CrOS');
    354 
    355       $('firmware').parentNode.hidden = (firmware == '');
    356       $('firmware').textContent = firmware;
    357     },
    358 
    359     /**
    360      * Sets the given overlay to show. This hides whatever overlay is currently
    361      * showing, if any.
    362      * @param {HTMLElement} node The overlay page to show. If null, all
    363      *     overlays are hidden.
    364      */
    365     showOverlay_: function(node) {
    366       var currentlyShowingOverlay = this.getCurrentOverlay_();
    367       if (currentlyShowingOverlay)
    368         currentlyShowingOverlay.classList.remove('showing');
    369       if (node)
    370         node.classList.add('showing');
    371       $('overlay-container').hidden = !node;
    372     },
    373 
    374     /**
    375      * Updates name of the current channel, i.e. the name of the
    376      * channel the device is currently on.
    377      * @param {string} channel The name of the current channel
    378      * @private
    379      */
    380     updateCurrentChannel_: function(channel) {
    381       if (this.channelList_.indexOf(channel) < 0)
    382         return;
    383       $('current-channel').textContent = loadTimeData.getStringF(
    384           'currentChannel', this.channelTable_[channel].label);
    385       this.currentChannel_ = channel;
    386       help.ChannelChangePage.updateCurrentChannel(channel);
    387     },
    388 
    389     /**
    390      * |enabled| is true if the release channel can be enabled.
    391      * @private
    392      */
    393     updateEnableReleaseChannel_: function(enabled) {
    394       this.updateChannelChangerContainerVisibility_(enabled);
    395       $('change-channel').disabled = !enabled;
    396       $('channel-change-disallowed-icon').hidden = enabled;
    397     },
    398 
    399     /**
    400      * Sets the device target channel.
    401      * @param {string} channel The name of the target channel
    402      * @param {boolean} isPowerwashAllowed True iff powerwash is allowed
    403      * @private
    404      */
    405     setChannel_: function(channel, isPowerwashAllowed) {
    406       this.powerwashAfterUpdate_ = isPowerwashAllowed;
    407       this.targetChannel_ = channel;
    408       chrome.send('setChannel', [channel, isPowerwashAllowed]);
    409       $('channel-change-confirmation').hidden = false;
    410       $('channel-change-confirmation').textContent = loadTimeData.getStringF(
    411           'channel-changed', this.channelTable_[channel].name);
    412     },
    413 
    414     /**
    415      * Sets the value of the "Build Date" field of the "More Info" section.
    416      * @param {string} buildDate The date of the build.
    417      * @private
    418      */
    419     setBuildDate_: function(buildDate) {
    420       $('build-date-container').classList.remove('empty');
    421       $('build-date').textContent = buildDate;
    422     },
    423 
    424     /**
    425      * Updates channel-change-page-container visibility according to
    426      * internal state.
    427      * @private
    428      */
    429     updateChannelChangePageContainerVisibility_: function() {
    430       if (!this.isNewChannelSwitcherUI_()) {
    431         $('channel-change-page-container').hidden = true;
    432         return;
    433       }
    434       $('channel-change-page-container').hidden =
    435           !help.ChannelChangePage.isPageReady();
    436     },
    437 
    438     /**
    439      * Updates channel-changer dropdown visibility if |visible| is
    440      * true and new channel switcher UI is disallowed.
    441      * @param {boolean} visible True if channel-changer should be
    442      *     displayed, false otherwise.
    443      * @private
    444      */
    445     updateChannelChangerContainerVisibility_: function(visible) {
    446       if (this.isNewChannelSwitcherUI_()) {
    447         $('channel-changer').hidden = true;
    448         return;
    449       }
    450       $('channel-changer').hidden = !visible;
    451     },
    452   };
    453 
    454   HelpPage.setUpdateStatus = function(status, message) {
    455     HelpPage.getInstance().setUpdateStatus_(status, message);
    456   };
    457 
    458   HelpPage.setProgress = function(progress) {
    459     HelpPage.getInstance().setProgress_(progress);
    460   };
    461 
    462   HelpPage.setAndShowAllowedConnectionTypesMsg = function(message) {
    463     HelpPage.getInstance().setAllowedConnectionTypesMsg_(message);
    464     HelpPage.getInstance().showAllowedConnectionTypesMsg_(true);
    465   };
    466 
    467   HelpPage.showAllowedConnectionTypesMsg = function(visible) {
    468     HelpPage.getInstance().showAllowedConnectionTypesMsg_(visible);
    469   };
    470 
    471   HelpPage.setPromotionState = function(state) {
    472     HelpPage.getInstance().setPromotionState_(state);
    473   };
    474 
    475   HelpPage.setObsoleteOS = function(obsolete) {
    476     HelpPage.getInstance().setObsoleteOS_(obsolete);
    477   };
    478 
    479   HelpPage.setOSVersion = function(version) {
    480     HelpPage.getInstance().setOSVersion_(version);
    481   };
    482 
    483   HelpPage.setOSFirmware = function(firmware) {
    484     HelpPage.getInstance().setOSFirmware_(firmware);
    485   };
    486 
    487   HelpPage.showOverlay = function(node) {
    488     HelpPage.getInstance().showOverlay_(node);
    489   };
    490 
    491   HelpPage.cancelOverlay = function() {
    492     HelpPage.getInstance().showOverlay_(null);
    493   };
    494 
    495   HelpPage.updateIsEnterpriseManaged = function(isEnterpriseManaged) {
    496     if (!cr.isChromeOS)
    497       return;
    498     help.ChannelChangePage.updateIsEnterpriseManaged(isEnterpriseManaged);
    499   };
    500 
    501   HelpPage.updateCurrentChannel = function(channel) {
    502     if (!cr.isChromeOS)
    503       return;
    504     HelpPage.getInstance().updateCurrentChannel_(channel);
    505   };
    506 
    507   HelpPage.updateTargetChannel = function(channel) {
    508     if (!cr.isChromeOS)
    509       return;
    510     help.ChannelChangePage.updateTargetChannel(channel);
    511   };
    512 
    513   HelpPage.updateEnableReleaseChannel = function(enabled) {
    514     HelpPage.getInstance().updateEnableReleaseChannel_(enabled);
    515   };
    516 
    517   HelpPage.setChannel = function(channel, isPowerwashAllowed) {
    518     HelpPage.getInstance().setChannel_(channel, isPowerwashAllowed);
    519   };
    520 
    521   HelpPage.setBuildDate = function(buildDate) {
    522     HelpPage.getInstance().setBuildDate_(buildDate);
    523   };
    524 
    525   HelpPage.updateChannelChangePageContainerVisibility = function() {
    526     HelpPage.getInstance().updateChannelChangePageContainerVisibility_();
    527   };
    528 
    529   // Export
    530   return {
    531     HelpPage: HelpPage
    532   };
    533 });
    534 
    535 /**
    536  * onload listener to initialize the HelpPage.
    537  */
    538 window.onload = function() {
    539   help.HelpPage.getInstance().initialize();
    540   if (cr.isChromeOS)
    541     help.ChannelChangePage.getInstance().initialize();
    542 };
    543