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