Home | History | Annotate | Download | only in diagnostics
      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 var localStrings = new LocalStrings();
      6 
      7 cr.define('diag', function() {
      8   /**
      9    * Encapsulated handling of the diagnostics page.
     10    */
     11   function DiagPage() {}
     12 
     13   cr.addSingletonGetter(DiagPage);
     14 
     15   /*
     16    * Remove all children nodes for an element.
     17    * @param {element} parent of the elements to be removed.
     18    */
     19   function removeChildren(element) {
     20     element.textContent = '';
     21   }
     22 
     23   /**
     24    * List of network adapter types.
     25    */
     26   DiagPage.AdapterType = [
     27       {adapter: 'wlan0', name: localStrings.getString('wlan0'), kind: 'wifi'},
     28       {adapter: 'eth0', name: localStrings.getString('eth0'), kind: 'ethernet'},
     29       {adapter: 'eth1', name: localStrings.getString('eth1'), kind: 'ethernet'},
     30       {adapter: 'wwan0', name: localStrings.getString('wwan0'), kind: '3g'},
     31   ];
     32 
     33   /**
     34    * List of network adapter status.
     35    * The numeric value assigned to each status reflects how healthy the network
     36    * adapter is.
     37    *
     38    * @enum {int}
     39    */
     40   DiagPage.AdapterStatus = {
     41       NOT_FOUND: 0,
     42       DISABLED: 1,
     43       NO_IP: 2,
     44       VALID_IP: 3
     45   };
     46 
     47   /**
     48    * List of ping test status.
     49    * The numeric value assigned to each status reflects how much progress has
     50    * been made for the ping test.
     51    *
     52    * @enum {int}
     53    */
     54   DiagPage.PingTestStatus = {
     55       NOT_STARTED: 0,
     56       IN_PROGRESS: 1,
     57       FAILED: 2,
     58       SUCCEEDED: 3
     59   };
     60 
     61   /**
     62    * Image elements for icons.
     63    */
     64   DiagPage.FailIconElement = document.createElement('img');
     65   DiagPage.TickIconElement = document.createElement('img');
     66   DiagPage.FailIconElement.setAttribute('src', 'chrome://diagnostics/fail.png');
     67   DiagPage.TickIconElement.setAttribute('src', 'chrome://diagnostics/tick.png');
     68 
     69   DiagPage.prototype = {
     70     /**
     71      * Perform initial setup.
     72      */
     73     initialize: function() {
     74       // Reset the diag page state.
     75       this.reset_();
     76 
     77       // Register event handlers.
     78       $('connectivity-refresh').addEventListener('click', function() {
     79         if (!this.getNetifStatusInProgress_)
     80           this.reset_();
     81       }.bind(this));
     82     },
     83 
     84     /**
     85      * Resets the diag page state.
     86      */
     87     reset_: function() {
     88       // Initialize member variables.
     89       this.activeAdapter_ = -1;
     90       this.adapterStatus_ = new Array();
     91       if (!this.pingTestStatus_ ||
     92           this.pingTestStatus_ != DiagPage.PingTestStatus.IN_PROGRESS) {
     93         this.pingTestStatus_ = DiagPage.PingTestStatus.NOT_STARTED;
     94       }
     95 
     96       // Initialize the UI with "loading" message.
     97       $('loading').hidden = false;
     98       $('choose-adapter').hidden = true;
     99       removeChildren($('adapter-selection'));
    100       removeChildren($('connectivity-status'));
    101 
    102       // Call into Chrome to get network interfaces status.
    103       chrome.send('getNetworkInterfaces');
    104       this.getNetifStatusInProgress_ = true;
    105     },
    106 
    107     /**
    108      * Updates the connectivity status with netif information.
    109      * @param {Object} netifStatus Dictionary of network adapter status.
    110      */
    111     setNetifStatus_: function(netifStatus) {
    112       // Hide the "loading" message and show the "choose-adapter" message.
    113       $('loading').hidden = true;
    114       $('choose-adapter').hidden = false;
    115 
    116       // Update netif state.
    117       var foundValidIp = false;
    118       for (var i = 0; i < DiagPage.AdapterType.length; i++) {
    119         var adapterType = DiagPage.AdapterType[i];
    120         var status = netifStatus[adapterType.adapter];
    121         if (!status)
    122           this.adapterStatus_[i] = DiagPage.AdapterStatus.NOT_FOUND;
    123         else if (!status.flags || status.flags.indexOf('up') == -1)
    124           this.adapterStatus_[i] = DiagPage.AdapterStatus.DISABLED;
    125         else if (!status.ipv4)
    126           this.adapterStatus_[i] = DiagPage.AdapterStatus.NO_IP;
    127         else
    128           this.adapterStatus_[i] = DiagPage.AdapterStatus.VALID_IP;
    129 
    130         if (this.adapterStatus_[i] == DiagPage.AdapterStatus.VALID_IP)
    131           foundValidIp = true;
    132       }
    133 
    134       // If we have valid IP, start ping test.
    135       if (foundValidIp &&
    136           this.pingTestStatus_ == DiagPage.PingTestStatus.NOT_STARTED) {
    137         this.pingTestStatus_ == DiagPage.PingTestStatus.IN_PROGRESS;
    138         chrome.send('testICMP', [String('8.8.8.8')]);
    139       }
    140 
    141       // Update UI
    142       this.updateAdapterSelection_();
    143       this.updateConnectivityStatus_();
    144 
    145       // Clear the getNetifStatusInProgress flag.
    146       this.getNetifStatusInProgress_ = false;
    147     },
    148 
    149     /**
    150      * Updates the ICMP connectivity status.
    151      * @param {Object} testICMPStatus Dictionary of ICMP connectivity status.
    152      */
    153     setTestICMPStatus_: function(testICMPStatus) {
    154       // Update the ping test state.
    155       for (var prop in testICMPStatus) {
    156         var status = testICMPStatus[prop];
    157         if (status.sent && status.recvd && status.sent == status.recvd)
    158           this.pingTestStatus_ = DiagPage.PingTestStatus.SUCCEEDED;
    159         else
    160           this.pingTestStatus_ = DiagPage.PingTestStatus.FAILED;
    161         break;
    162       }
    163 
    164       // Update UI
    165       this.updateConnectivityStatus_();
    166     },
    167 
    168     /**
    169      * Gets the HTML radio input element id for a network adapter.
    170      * @private
    171      */
    172     getAdapterElementId_: function(adapter) {
    173       return 'adapter-' + DiagPage.AdapterType[adapter].adapter;
    174     },
    175 
    176     /**
    177      * Gets the most active adapter based on their status.
    178      * @private
    179      */
    180     getActiveAdapter_: function() {
    181       var activeAdapter = -1;
    182       var activeAdapterStatus = DiagPage.AdapterStatus.NOT_FOUND;
    183       for (var i = 0; i < DiagPage.AdapterType.length; i++) {
    184         var status = this.adapterStatus_[i];
    185         if (status == DiagPage.AdapterStatus.NOT_FOUND)
    186           continue;
    187         if (activeAdapter == -1 || status > activeAdapterStatus) {
    188           activeAdapter = i;
    189           activeAdapterStatus = status;
    190         }
    191       }
    192       return activeAdapter;
    193     },
    194 
    195     /**
    196      * Update the adapter selection section.
    197      * @private
    198      */
    199     updateAdapterSelection_: function() {
    200       // Determine active adapter.
    201       if (this.activeAdapter_ == -1)
    202         this.activeAdapter_ = this.getActiveAdapter_();
    203       // Clear adapter selection section.
    204       var adapterSelectionElement = $('adapter-selection');
    205       removeChildren(adapterSelectionElement);
    206       // Create HTML radio input elements.
    207       for (var i = 0; i < DiagPage.AdapterType.length; i++) {
    208         if (this.adapterStatus_[i] == DiagPage.AdapterStatus.NOT_FOUND)
    209           continue;
    210         var radioElement = document.createElement('input');
    211         var elementId = this.getAdapterElementId_(i);
    212         radioElement.setAttribute('type', 'radio');
    213         radioElement.setAttribute('name', 'adapter');
    214         radioElement.setAttribute('id', elementId);
    215         if (i == this.activeAdapter_)
    216           radioElement.setAttribute('checked', 'true');
    217         radioElement.onclick = function(adapter) {
    218           this.activeAdapter_ = adapter;
    219           this.updateConnectivityStatus_();
    220         }.bind(this, i);
    221         var labelElement = document.createElement('label');
    222         labelElement.setAttribute('for', elementId);
    223         labelElement.appendChild(radioElement);
    224         labelElement.appendChild(
    225             document.createTextNode(DiagPage.AdapterType[i].name));
    226         adapterSelectionElement.appendChild(labelElement);
    227         adapterSelectionElement.appendChild(document.createElement('br'));
    228       }
    229     },
    230 
    231     /**
    232      * Update the connectivity status for the specified network interface.
    233      * @private
    234      */
    235     updateConnectivityStatus_: function() {
    236       var adapter = this.activeAdapter_;
    237       var status = this.adapterStatus_[adapter];
    238       var name = DiagPage.AdapterType[adapter].name;
    239       var kind = DiagPage.AdapterType[adapter].kind;
    240 
    241       // Status messages for individual tests.
    242       var connectivityStatusElement = $('connectivity-status');
    243       var testStatusElements = new Array();
    244       removeChildren(connectivityStatusElement);
    245       for (var i = 0; i < 3; i++) {
    246         testStatusElements[i] = document.createElement('div');
    247         connectivityStatusElement.appendChild(testStatusElements[i]);
    248       }
    249       testStatusElements[0].innerHTML =
    250         localStrings.getStringF('testing-hardware', name);
    251       testStatusElements[1].innerHTML =
    252         localStrings.getString('testing-connection-to-router');
    253       testStatusElements[2].innerHTML =
    254         localStrings.getString('testing-connection-to-internet');
    255 
    256       // Error and recommendation messages may be inserted in test status
    257       // elements.
    258       var errorElement = document.createElement('div');
    259       var recommendationElement = document.createElement('div');
    260       errorElement.className = 'test-error';
    261       recommendationElement.className = 'recommendation';
    262       testStatusElements[0].className = 'test-performed';
    263       if (status == DiagPage.AdapterStatus.DISABLED) {
    264         errorElement.appendChild(DiagPage.FailIconElement.cloneNode());
    265         errorElement.appendChild(document.createTextNode(
    266             localStrings.getStringF('adapter-disabled', name)));
    267         recommendationElement.innerHTML =
    268             localStrings.getStringF('enable-adapter', name);
    269         connectivityStatusElement.insertBefore(errorElement,
    270             testStatusElements[1]);
    271         connectivityStatusElement.insertBefore(recommendationElement,
    272             testStatusElements[1]);
    273         testStatusElements[1].className = 'test-pending';
    274         testStatusElements[2].className = 'test-pending';
    275       } else {
    276         testStatusElements[0].appendChild(DiagPage.TickIconElement.cloneNode());
    277         testStatusElements[1].className = 'test-performed';
    278         if (status == DiagPage.AdapterStatus.NO_IP) {
    279           errorElement.appendChild(DiagPage.FailIconElement.cloneNode());
    280           errorElement.appendChild(document.createTextNode(
    281               localStrings.getStringF('adapter-no-ip', name)));
    282           recommendationElement.innerHTML =
    283               localStrings.getStringF('fix-no-ip-' + kind);
    284           connectivityStatusElement.insertBefore(errorElement,
    285               testStatusElements[2]);
    286           connectivityStatusElement.insertBefore(recommendationElement,
    287               testStatusElements[2]);
    288           testStatusElements[2].className = 'test-pending';
    289         } else {
    290           testStatusElements[1].appendChild(
    291               DiagPage.TickIconElement.cloneNode());
    292           testStatusElements[2].className = 'test-performed';
    293           if (this.pingTestStatus_ == DiagPage.PingTestStatus.NOT_STARTED ||
    294               this.pingTestStatus_ == DiagPage.PingTestStatus.IN_PROGRESS) {
    295             // TODO(hshi): make the ellipsis below i18n-friendly.
    296             testStatusElements[2].innerHTML += '...';
    297           } else {
    298             if (this.pingTestStatus_ == DiagPage.PingTestStatus.FAILED) {
    299               errorElement.appendChild(DiagPage.FailIconElement.cloneNode());
    300               errorElement.appendChild(document.createTextNode(
    301                 localStrings.getString('gateway-not-connected-to-internet')));
    302               recommendationElement.innerHTML =
    303                 localStrings.getStringF('fix-gateway-connection');
    304               connectivityStatusElement.appendChild(errorElement);
    305               connectivityStatusElement.appendChild(recommendationElement);
    306             } else {
    307               testStatusElements[2].appendChild(
    308                 DiagPage.TickIconElement.cloneNode());
    309             }
    310           }
    311         }
    312       }
    313     }
    314   };
    315 
    316   DiagPage.setNetifStatus = function(netifStatus) {
    317     DiagPage.getInstance().setNetifStatus_(netifStatus);
    318   }
    319 
    320   DiagPage.setTestICMPStatus = function(testICMPStatus) {
    321     DiagPage.getInstance().setTestICMPStatus_(testICMPStatus);
    322   }
    323 
    324   // Export
    325   return {
    326     DiagPage: DiagPage
    327   };
    328 });
    329 
    330 /**
    331  * Initialize the DiagPage upon DOM content loaded.
    332  */
    333 document.addEventListener('DOMContentLoaded', function() {
    334   diag.DiagPage.getInstance().initialize();
    335 });
    336