Home | History | Annotate | Download | only in help
      1 // Copyright 2014 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('help', function() {
      6   var Page = cr.ui.pageManager.Page;
      7   var PageManager = cr.ui.pageManager.PageManager;
      8 
      9   /**
     10    * Encapsulated handling of the About page. Called 'help' internally to avoid
     11    * confusion with generic AboutUI (about:memory, about:sandbox, etc.).
     12    */
     13   function HelpPage() {
     14     var id = loadTimeData.valueExists('aboutOverlayTabTitle') ?
     15       'aboutOverlayTabTitle' : 'aboutTitle';
     16     Page.call(this, 'help', loadTimeData.getString(id), 'help-page');
     17   }
     18 
     19   cr.addSingletonGetter(HelpPage);
     20 
     21   HelpPage.prototype = {
     22     __proto__: Page.prototype,
     23 
     24     /**
     25      * List of the channel names. Should be ordered in increasing level of
     26      * stability.
     27      * @private
     28      */
     29     channelList_: ['dev-channel', 'beta-channel', 'stable-channel'],
     30 
     31     /**
     32      * Name of the channel the device is currently on.
     33      * @private
     34      */
     35     currentChannel_: null,
     36 
     37     /**
     38      * Name of the channel the device is supposed to be on.
     39      * @private
     40      */
     41     targetChannel_: null,
     42 
     43     /**
     44      * Last status received from the version updater.
     45      * @private
     46      */
     47     status_: null,
     48 
     49     /**
     50      * Last message received from the version updater.
     51      * @private
     52      */
     53     message_: null,
     54 
     55     /** @override */
     56     initializePage: function() {
     57       Page.prototype.initializePage.call(this);
     58 
     59       $('product-license').innerHTML = loadTimeData.getString('productLicense');
     60       if (cr.isChromeOS) {
     61         $('product-os-license').innerHTML =
     62             loadTimeData.getString('productOsLicense');
     63       }
     64 
     65       var productTOS = $('product-tos');
     66       if (productTOS)
     67         productTOS.innerHTML = loadTimeData.getString('productTOS');
     68 
     69       $('get-help').onclick = function() {
     70         chrome.send('openHelpPage');
     71       };
     72 <if expr="_google_chrome">
     73       $('report-issue').onclick = function() {
     74         chrome.send('openFeedbackDialog');
     75       };
     76 </if>
     77 
     78       this.maybeSetOnClick_($('more-info-expander'),
     79           this.toggleMoreInfo_.bind(this));
     80 
     81       this.maybeSetOnClick_($('promote'), function() {
     82         chrome.send('promoteUpdater');
     83       });
     84       this.maybeSetOnClick_($('relaunch'), function() {
     85         chrome.send('relaunchNow');
     86       });
     87       if (cr.isChromeOS) {
     88         this.maybeSetOnClick_($('relaunch-and-powerwash'), function() {
     89           chrome.send('relaunchAndPowerwash');
     90         });
     91 
     92         this.channelTable_ = {
     93           'stable-channel': {
     94             'name': loadTimeData.getString('stable'),
     95             'label': loadTimeData.getString('currentChannelStable'),
     96           },
     97           'beta-channel': {
     98             'name': loadTimeData.getString('beta'),
     99             'label': loadTimeData.getString('currentChannelBeta')
    100           },
    101           'dev-channel': {
    102             'name': loadTimeData.getString('dev'),
    103             'label': loadTimeData.getString('currentChannelDev')
    104           }
    105         };
    106       }
    107       this.maybeSetOnClick_($('about-done'), function() {
    108         // Event listener for the close button when shown as an overlay.
    109         PageManager.closeOverlay();
    110       });
    111 
    112       var self = this;
    113       var channelChanger = $('channel-changer');
    114       if (channelChanger) {
    115         channelChanger.onchange = function(event) {
    116           self.setChannel_(event.target.value, false);
    117         };
    118       }
    119 
    120       if (cr.isChromeOS) {
    121         // Add event listener for the check for and apply updates button.
    122         this.maybeSetOnClick_($('request-update'), function() {
    123           self.setUpdateStatus_('checking');
    124           $('request-update').disabled = true;
    125           chrome.send('requestUpdate');
    126         });
    127 
    128         $('change-channel').onclick = function() {
    129           PageManager.showPageByName('channel-change-page', false);
    130         };
    131 
    132         var channelChangeDisallowedError = document.createElement('div');
    133         channelChangeDisallowedError.className = 'channel-change-error-bubble';
    134 
    135         var channelChangeDisallowedIcon = document.createElement('div');
    136         channelChangeDisallowedIcon.classList.add('help-page-icon-large');
    137         channelChangeDisallowedIcon.classList.add('channel-change-error-icon');
    138         channelChangeDisallowedError.appendChild(channelChangeDisallowedIcon);
    139 
    140         var channelChangeDisallowedText = document.createElement('div');
    141         channelChangeDisallowedText.className = 'channel-change-error-text';
    142         channelChangeDisallowedText.textContent =
    143             loadTimeData.getString('channelChangeDisallowedMessage');
    144         channelChangeDisallowedError.appendChild(channelChangeDisallowedText);
    145 
    146         $('channel-change-disallowed-icon').onclick = function() {
    147           PageManager.showBubble(channelChangeDisallowedError,
    148                                  $('channel-change-disallowed-icon'),
    149                                  $('help-container'),
    150                                  cr.ui.ArrowLocation.TOP_END);
    151         };
    152       }
    153 
    154       // Attempt to update.
    155       chrome.send('onPageLoaded');
    156     },
    157 
    158     /** @override */
    159     didClosePage: function() {
    160       this.setMoreInfoVisible_(false);
    161     },
    162 
    163     /**
    164      * Sets the visible state of the 'More Info' section.
    165      * @param {boolean} visible Whether the section should be visible.
    166      * @private
    167      */
    168     setMoreInfoVisible_: function(visible) {
    169       var moreInfo = $('more-info-container');
    170       if (visible == moreInfo.classList.contains('visible'))
    171         return;
    172 
    173       moreInfo.classList.toggle('visible', visible);
    174       moreInfo.style.height = visible ? moreInfo.scrollHeight + 'px' : '';
    175       moreInfo.addEventListener('webkitTransitionEnd', function(event) {
    176         $('more-info-expander').textContent = visible ?
    177             loadTimeData.getString('hideMoreInfo') :
    178             loadTimeData.getString('showMoreInfo');
    179       });
    180     },
    181 
    182     /**
    183      * Toggles the visible state of the 'More Info' section.
    184      * @private
    185      */
    186     toggleMoreInfo_: function() {
    187       var moreInfo = $('more-info-container');
    188       this.setMoreInfoVisible_(!moreInfo.classList.contains('visible'));
    189     },
    190 
    191     /**
    192      * Assigns |method| to the onclick property of |el| if |el| exists.
    193      * @param {HTMLElement} el The element on which to set the click handler.
    194      * @param {Function} method The click handler.
    195      * @private
    196      */
    197     maybeSetOnClick_: function(el, method) {
    198       if (el)
    199         el.onclick = method;
    200     },
    201 
    202     /**
    203      * @param {string} state The state of the update.
    204      * private
    205      */
    206     setUpdateImage_: function(state) {
    207       $('update-status-icon').className = 'help-page-icon ' + state;
    208     },
    209 
    210     /**
    211      * @return {boolean} True, if new channel switcher UI is used,
    212      *    false otherwise.
    213      * @private
    214      */
    215     isNewChannelSwitcherUI_: function() {
    216       return !loadTimeData.valueExists('disableNewChannelSwitcherUI');
    217     },
    218 
    219     /**
    220      * @return {boolean} True if target and current channels are not null and
    221      *     not equal.
    222      * @private
    223      */
    224     channelsDiffer_: function() {
    225       var current = this.currentChannel_;
    226       var target = this.targetChannel_;
    227       return (current != null && target != null && current != target);
    228     },
    229 
    230     /**
    231      * @return {boolean} True if target channel is more stable than the current
    232      *     one, and false otherwise.
    233      * @private
    234      */
    235     targetChannelIsMoreStable_: function() {
    236       var current = this.currentChannel_;
    237       var target = this.targetChannel_;
    238       if (current == null || target == null)
    239         return false;
    240       var currentIndex = this.channelList_.indexOf(current);
    241       var targetIndex = this.channelList_.indexOf(target);
    242       if (currentIndex < 0 || targetIndex < 0)
    243         return false;
    244       return currentIndex < targetIndex;
    245     },
    246 
    247     /**
    248      * @param {string} status The status of the update.
    249      * @param {string} message Failure message to display.
    250      * @private
    251      */
    252     setUpdateStatus_: function(status, message) {
    253       this.status_ = status;
    254       this.message_ = message;
    255 
    256       this.updateUI_();
    257     },
    258 
    259     /**
    260       * Updates UI elements on the page according to current state.
    261       * @private
    262       */
    263     updateUI_: function() {
    264       var status = this.status_;
    265       var message = this.message_;
    266       var channel = this.targetChannel_;
    267 
    268       if (this.channelList_.indexOf(channel) >= 0) {
    269         $('current-channel').textContent = loadTimeData.getStringF(
    270             'currentChannel', this.channelTable_[channel].label);
    271         this.updateChannelChangePageContainerVisibility_();
    272       }
    273 
    274       if (status == null)
    275         return;
    276 
    277       if (cr.isMac &&
    278           $('update-status-message') &&
    279           $('update-status-message').hidden) {
    280         // Chrome has reached the end of the line on this system. The
    281         // update-obsolete-system message is displayed. No other auto-update
    282         // status should be displayed.
    283         return;
    284       }
    285 
    286       if (status == 'checking') {
    287         this.setUpdateImage_('working');
    288         $('update-status-message').innerHTML =
    289             loadTimeData.getString('updateCheckStarted');
    290       } else if (status == 'updating') {
    291         this.setUpdateImage_('working');
    292         if (this.channelsDiffer_()) {
    293           $('update-status-message').innerHTML =
    294               loadTimeData.getStringF('updatingChannelSwitch',
    295                                       this.channelTable_[channel].label);
    296         } else {
    297           $('update-status-message').innerHTML =
    298               loadTimeData.getStringF('updating');
    299         }
    300       } else if (status == 'nearly_updated') {
    301         this.setUpdateImage_('up-to-date');
    302         if (this.channelsDiffer_()) {
    303           $('update-status-message').innerHTML =
    304               loadTimeData.getString('successfulChannelSwitch');
    305         } else {
    306           $('update-status-message').innerHTML =
    307               loadTimeData.getString('updateAlmostDone');
    308         }
    309       } else if (status == 'updated') {
    310         this.setUpdateImage_('up-to-date');
    311         $('update-status-message').innerHTML =
    312             loadTimeData.getString('upToDate');
    313       } else if (status == 'failed') {
    314         this.setUpdateImage_('failed');
    315         $('update-status-message').innerHTML = message;
    316       }
    317 
    318       // Following invariant must be established at the end of this function:
    319       // { ~$('relaunch_and_powerwash').hidden -> $('relaunch').hidden }
    320       var relaunchAndPowerwashHidden = true;
    321       if ($('relaunch-and-powerwash')) {
    322         // It's allowed to do powerwash only for customer devices,
    323         // when user explicitly decides to update to a more stable
    324         // channel.
    325         relaunchAndPowerwashHidden =
    326             !this.targetChannelIsMoreStable_() || status != 'nearly_updated';
    327         $('relaunch-and-powerwash').hidden = relaunchAndPowerwashHidden;
    328       }
    329 
    330       if (cr.isChromeOS) {
    331         // Only enable the update button if it hasn't been used yet or the
    332         // status isn't 'updated'.
    333         if (!$('request-update').disabled || status != 'updated') {
    334           // Disable the button if an update is already in progress.
    335           $('request-update').disabled =
    336             ['checking', 'updating', 'nearly_updated'].indexOf(status) > -1;
    337         }
    338       }
    339 
    340       var container = $('update-status-container');
    341       if (container) {
    342         container.hidden = status == 'disabled';
    343         $('relaunch').hidden =
    344             (status != 'nearly_updated') || !relaunchAndPowerwashHidden;
    345 
    346         if (cr.isChromeOS) {
    347           // Assume the "updated" status is stale if we haven't checked yet.
    348           if (status == 'updated' && !$('request-update').disabled)
    349             container.hidden = true;
    350 
    351           // Hide the request update button if auto-updating is disabled or
    352           // a relaunch button is showing.
    353           $('request-update').hidden = status == 'disabled' ||
    354             !$('relaunch').hidden || !relaunchAndPowerwashHidden;
    355         }
    356 
    357         if (!cr.isMac)
    358           $('update-percentage').hidden = status != 'updating';
    359       }
    360     },
    361 
    362     /**
    363      * @param {number} progress The percent completion.
    364      * @private
    365      */
    366     setProgress_: function(progress) {
    367       $('update-percentage').innerHTML = progress + '%';
    368     },
    369 
    370     /**
    371      * @param {string} message The allowed connection types message.
    372      * @private
    373      */
    374     setAllowedConnectionTypesMsg_: function(message) {
    375       $('allowed-connection-types-message').innerText = message;
    376     },
    377 
    378     /**
    379      * @param {boolean} visible Whether to show the message.
    380      * @private
    381      */
    382     showAllowedConnectionTypesMsg_: function(visible) {
    383       $('allowed-connection-types-message').hidden = !visible;
    384     },
    385 
    386     /**
    387      * @param {string} state The promote state to set.
    388      * @private
    389      */
    390     setPromotionState_: function(state) {
    391       if (state == 'hidden') {
    392         $('promote').hidden = true;
    393       } else if (state == 'enabled') {
    394         $('promote').disabled = false;
    395         $('promote').hidden = false;
    396       } else if (state == 'disabled') {
    397         $('promote').disabled = true;
    398         $('promote').hidden = false;
    399       }
    400     },
    401 
    402     /**
    403      * @param {boolean} obsolete Whether the system is obsolete.
    404      * @private
    405      */
    406     setObsoleteSystem_: function(obsolete) {
    407       if (cr.isMac && $('update-obsolete-system-container')) {
    408         $('update-obsolete-system-container').hidden = !obsolete;
    409       }
    410     },
    411 
    412     /**
    413      * @param {boolean} endOfTheLine Whether the train has rolled into
    414      *     the station.
    415      * @private
    416      */
    417     setObsoleteSystemEndOfTheLine_: function(endOfTheLine) {
    418       if (cr.isMac &&
    419           $('update-obsolete-system-container') &&
    420           !$('update-obsolete-system-container').hidden &&
    421           $('update-status-message')) {
    422         $('update-status-message').hidden = endOfTheLine;
    423         if (endOfTheLine) {
    424           this.setUpdateImage_('failed');
    425         }
    426       }
    427     },
    428 
    429     /**
    430      * @param {string} version Version of Chrome OS.
    431      * @private
    432      */
    433     setOSVersion_: function(version) {
    434       if (!cr.isChromeOS)
    435         console.error('OS version unsupported on non-CrOS');
    436 
    437       $('os-version').parentNode.hidden = (version == '');
    438       $('os-version').textContent = version;
    439     },
    440 
    441     /**
    442      * @param {string} firmware Firmware on Chrome OS.
    443      * @private
    444      */
    445     setOSFirmware_: function(firmware) {
    446       if (!cr.isChromeOS)
    447         console.error('OS firmware unsupported on non-CrOS');
    448 
    449       $('firmware').parentNode.hidden = (firmware == '');
    450       $('firmware').textContent = firmware;
    451     },
    452 
    453     /**
    454      * Updates page UI according to device owhership policy.
    455      * @param {boolean} isEnterpriseManaged True if the device is
    456      *     enterprise managed.
    457      * @private
    458      */
    459     updateIsEnterpriseManaged_: function(isEnterpriseManaged) {
    460       help.ChannelChangePage.updateIsEnterpriseManaged(isEnterpriseManaged);
    461       this.updateUI_();
    462     },
    463 
    464     /**
    465      * Updates name of the current channel, i.e. the name of the
    466      * channel the device is currently on.
    467      * @param {string} channel The name of the current channel.
    468      * @private
    469      */
    470     updateCurrentChannel_: function(channel) {
    471       if (this.channelList_.indexOf(channel) < 0)
    472         return;
    473       this.currentChannel_ = channel;
    474       help.ChannelChangePage.updateCurrentChannel(channel);
    475       this.updateUI_();
    476     },
    477 
    478     /**
    479      * Updates name of the target channel, i.e. the name of the
    480      * channel the device is supposed to be.
    481      * @param {string} channel The name of the target channel.
    482      * @private
    483      */
    484     updateTargetChannel_: function(channel) {
    485       if (this.channelList_.indexOf(channel) < 0)
    486         return;
    487       this.targetChannel_ = channel;
    488       help.ChannelChangePage.updateTargetChannel(channel);
    489       this.updateUI_();
    490     },
    491 
    492     /**
    493      * @param {boolean} enabled True if the release channel can be enabled.
    494      * @private
    495      */
    496     updateEnableReleaseChannel_: function(enabled) {
    497       this.updateChannelChangerContainerVisibility_(enabled);
    498       $('change-channel').disabled = !enabled;
    499       $('channel-change-disallowed-icon').hidden = enabled;
    500     },
    501 
    502     /**
    503      * Sets the device target channel.
    504      * @param {string} channel The name of the target channel.
    505      * @param {boolean} isPowerwashAllowed True iff powerwash is allowed.
    506      * @private
    507      */
    508     setChannel_: function(channel, isPowerwashAllowed) {
    509       chrome.send('setChannel', [channel, isPowerwashAllowed]);
    510       $('channel-change-confirmation').hidden = false;
    511       $('channel-change-confirmation').textContent = loadTimeData.getStringF(
    512           'channel-changed', this.channelTable_[channel].name);
    513       this.updateTargetChannel_(channel);
    514     },
    515 
    516     /**
    517      * Sets the value of the "Build Date" field of the "More Info" section.
    518      * @param {string} buildDate The date of the build.
    519      * @private
    520      */
    521     setBuildDate_: function(buildDate) {
    522       $('build-date-container').classList.remove('empty');
    523       $('build-date').textContent = buildDate;
    524     },
    525 
    526     /**
    527      * Updates channel-change-page-container visibility according to
    528      * internal state.
    529      * @private
    530      */
    531     updateChannelChangePageContainerVisibility_: function() {
    532       if (!this.isNewChannelSwitcherUI_()) {
    533         $('channel-change-page-container').hidden = true;
    534         return;
    535       }
    536       $('channel-change-page-container').hidden =
    537           !help.ChannelChangePage.isPageReady();
    538     },
    539 
    540     /**
    541      * Updates channel-changer dropdown visibility if |visible| is
    542      * true and new channel switcher UI is disallowed.
    543      * @param {boolean} visible True if channel-changer should be
    544      *     displayed, false otherwise.
    545      * @private
    546      */
    547     updateChannelChangerContainerVisibility_: function(visible) {
    548       if (this.isNewChannelSwitcherUI_()) {
    549         $('channel-changer').hidden = true;
    550         return;
    551       }
    552       $('channel-changer').hidden = !visible;
    553     },
    554   };
    555 
    556   HelpPage.setUpdateStatus = function(status, message) {
    557     HelpPage.getInstance().setUpdateStatus_(status, message);
    558   };
    559 
    560   HelpPage.setProgress = function(progress) {
    561     HelpPage.getInstance().setProgress_(progress);
    562   };
    563 
    564   HelpPage.setAndShowAllowedConnectionTypesMsg = function(message) {
    565     HelpPage.getInstance().setAllowedConnectionTypesMsg_(message);
    566     HelpPage.getInstance().showAllowedConnectionTypesMsg_(true);
    567   };
    568 
    569   HelpPage.showAllowedConnectionTypesMsg = function(visible) {
    570     HelpPage.getInstance().showAllowedConnectionTypesMsg_(visible);
    571   };
    572 
    573   HelpPage.setPromotionState = function(state) {
    574     HelpPage.getInstance().setPromotionState_(state);
    575   };
    576 
    577   HelpPage.setObsoleteSystem = function(obsolete) {
    578     HelpPage.getInstance().setObsoleteSystem_(obsolete);
    579   };
    580 
    581   HelpPage.setObsoleteSystemEndOfTheLine = function(endOfTheLine) {
    582     HelpPage.getInstance().setObsoleteSystemEndOfTheLine_(endOfTheLine);
    583   };
    584 
    585   HelpPage.setOSVersion = function(version) {
    586     HelpPage.getInstance().setOSVersion_(version);
    587   };
    588 
    589   HelpPage.setOSFirmware = function(firmware) {
    590     HelpPage.getInstance().setOSFirmware_(firmware);
    591   };
    592 
    593   HelpPage.updateIsEnterpriseManaged = function(isEnterpriseManaged) {
    594     if (!cr.isChromeOS)
    595       return;
    596     HelpPage.getInstance().updateIsEnterpriseManaged_(isEnterpriseManaged);
    597   };
    598 
    599   HelpPage.updateCurrentChannel = function(channel) {
    600     if (!cr.isChromeOS)
    601       return;
    602     HelpPage.getInstance().updateCurrentChannel_(channel);
    603   };
    604 
    605   HelpPage.updateTargetChannel = function(channel) {
    606     if (!cr.isChromeOS)
    607       return;
    608     HelpPage.getInstance().updateTargetChannel_(channel);
    609   };
    610 
    611   HelpPage.updateEnableReleaseChannel = function(enabled) {
    612     HelpPage.getInstance().updateEnableReleaseChannel_(enabled);
    613   };
    614 
    615   HelpPage.setChannel = function(channel, isPowerwashAllowed) {
    616     HelpPage.getInstance().setChannel_(channel, isPowerwashAllowed);
    617   };
    618 
    619   HelpPage.setBuildDate = function(buildDate) {
    620     HelpPage.getInstance().setBuildDate_(buildDate);
    621   };
    622 
    623   // Export
    624   return {
    625     HelpPage: HelpPage
    626   };
    627 });
    628