1 // Copyright (c) 2013 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 GEN('#if defined(OS_CHROMEOS)'); 6 7 function BluetoothWebUITest() {} 8 9 BluetoothWebUITest.prototype = { 10 __proto__: testing.Test.prototype, 11 12 /** 13 * Start tests from the main-settings page. 14 */ 15 browsePreload: 'chrome://settings-frame/settings', 16 17 /** 18 * @inheritDoc 19 */ 20 preLoad: function() { 21 this.makeAndRegisterMockHandler([ 22 'bluetoothEnableChange', 23 'updateBluetoothDevice', 24 'findBluetoothDevices', 25 'stopBluetoothDeviceDiscovery', 26 ]); 27 }, 28 29 /** 30 * Retrieves the list item associated with a Bluetooth device. 31 * @param {!Element} listElement Element containing a list of devices. 32 * @param {string} deviceName The name of the device. 33 * @return {Element|undefined} List item matching the device name. 34 */ 35 getElementForDevice: function(listElement, deviceName) { 36 var items = listElement.querySelectorAll('.bluetooth-device'); 37 for (var i = 0; i < items.length; i++) { 38 var candidate = items[i]; 39 var name = candidate.data.name; 40 if (name == deviceName) 41 return candidate; 42 } 43 }, 44 45 /** 46 * Selects a bluetooth device from the list with the matching address. 47 * @param {!Element} listElement A list of Bluetooth devices. 48 * @param {{address: string, 49 * connectable: boolean, 50 * connected: boolean, 51 * name: string, 52 * paired: boolean}} device Description of the device. 53 */ 54 selectDevice: function(listElement, device) { 55 listElement.selectedItem = device; 56 cr.dispatchSimpleEvent(listElement, 'change'); 57 }, 58 59 /** 60 * Fake input of a pincode or passkey. 61 * @param {!Element} element Text input field. 62 * @param {string} text New value for the input field. 63 */ 64 fakeInput: function(element, text) { 65 element.value = text; 66 cr.dispatchSimpleEvent(element, 'input'); 67 }, 68 69 }; 70 71 TEST_F('BluetoothWebUITest','testEnableBluetooth', function() { 72 assertEquals(this.browsePreload, document.location.href); 73 expectFalse($('enable-bluetooth').checked); 74 expectTrue($('bluetooth-paired-devices-list').parentNode.hidden); 75 76 this.mockHandler.expects(once()).bluetoothEnableChange([true]).will( 77 callFunction(function() { 78 options.BrowserOptions.setBluetoothState(true); 79 })); 80 $('enable-bluetooth').click(); 81 82 expectFalse($('bluetooth-paired-devices-list').parentNode.hidden); 83 }); 84 85 TEST_F('BluetoothWebUITest','testAddDevices', function() { 86 assertEquals(this.browsePreload, document.location.href); 87 88 var pairedDeviceList = $('bluetooth-paired-devices-list'); 89 var unpairedDeviceList = $('bluetooth-unpaired-devices-list'); 90 91 var fakePairedDevice = { 92 address: '00:11:22:33:44:55', 93 connectable: true, 94 connected: false, 95 name: 'Fake device', 96 paired: true 97 }; 98 99 var fakeUnpairedDevice = { 100 address: '28:CF:DA:00:00:00', 101 connectable: true, 102 connected: false, 103 name: 'Apple Magic Mouse', 104 paired: false 105 }; 106 107 var fakeUnpairedDevice2 = { 108 address: '28:37:37:00:00:00', 109 connectable: true, 110 connected: false, 111 name: 'Apple Wireless Keyboard', 112 paired: false 113 }; 114 115 // Ensure data models for the paired and unpaired device lists are properly 116 // updated. 117 var index = pairedDeviceList.find(fakePairedDevice.address); 118 expectEquals(undefined, index); 119 options.BrowserOptions.addBluetoothDevice(fakePairedDevice); 120 index = pairedDeviceList.find(fakePairedDevice.address); 121 expectEquals(0, index); 122 123 // Ensure the DOM contents of the list are properly updated. The default 124 // layout of a list creates DOM elements only for visible elements in the 125 // list, which is problematic if the list is hidden at the time of layout. 126 // The Bluetooth device lists overcomes this problem by using fixed sized 127 // elements in an auto-expanding list. This test ensures the problem stays 128 // fixed. 129 expectTrue(!!this.getElementForDevice(pairedDeviceList, 130 fakePairedDevice.name)); 131 expectFalse(!!this.getElementForDevice(unpairedDeviceList, 132 fakePairedDevice.name)); 133 134 options.BrowserOptions.addBluetoothDevice(fakeUnpairedDevice); 135 index = unpairedDeviceList.find(fakeUnpairedDevice.address); 136 expectEquals(0, index); 137 expectFalse(!!this.getElementForDevice(pairedDeviceList, 138 fakeUnpairedDevice.name)); 139 expectTrue(!!this.getElementForDevice(unpairedDeviceList, 140 fakeUnpairedDevice.name)); 141 142 // Test adding a second device to a list. 143 options.BrowserOptions.addBluetoothDevice(fakeUnpairedDevice2); 144 index = unpairedDeviceList.find(fakeUnpairedDevice2.address); 145 expectEquals(1, index); 146 expectTrue(!!this.getElementForDevice(unpairedDeviceList, 147 fakeUnpairedDevice2.name)); 148 149 // Test clicking on the 'Add a device' button. 150 this.mockHandler.expects(once()).findBluetoothDevices(); 151 $('bluetooth-add-device').click(); 152 expectFalse($('bluetooth-options').hidden); 153 expectTrue($('bluetooth-add-device-apply-button').disabled); 154 expectFalse($('bluetooth-add-device-cancel-button').disabled); 155 156 Mock4JS.verifyAllMocks(); 157 Mock4JS.clearMocksToVerify(); 158 159 // Test selecting an element and clicking on the connect button. 160 this.mockHandler.expects(once()).stopBluetoothDeviceDiscovery(); 161 this.mockHandler.expects(once()).updateBluetoothDevice( 162 [fakeUnpairedDevice2.address, 'connect']); 163 this.selectDevice(unpairedDeviceList, fakeUnpairedDevice2); 164 var connectButton = $('bluetooth-add-device-apply-button'); 165 expectFalse(connectButton.disabled); 166 connectButton.click(); 167 }); 168 169 TEST_F('BluetoothWebUITest','testDevicePairing', function() { 170 assertEquals(this.browsePreload, document.location.href); 171 172 var pairedDeviceList = $('bluetooth-paired-devices-list'); 173 var unpairedDeviceList = $('bluetooth-unpaired-devices-list'); 174 175 var fakeDevice = { 176 address: '00:24:BE:00:00:00', 177 connectable: true, 178 connected: false, 179 name: 'Sony BT-00', 180 paired: false 181 }; 182 183 options.BrowserOptions.addBluetoothDevice(fakeDevice); 184 var index = unpairedDeviceList.find(fakeDevice.address); 185 expectEquals(0, index); 186 expectTrue(!!this.getElementForDevice(unpairedDeviceList, 187 fakeDevice.name)); 188 189 // Simulate process of pairing a device. 190 fakeDevice.pairing = 'bluetoothEnterPinCode'; 191 options.BrowserOptions.addBluetoothDevice(fakeDevice); 192 193 // Verify that the pairing dialog is displayed with the proper options. 194 expectFalse($('bluetooth-pairing').hidden); 195 expectTrue($('bluetooth-pairing-passkey-display').hidden); 196 expectTrue($('bluetooth-pairing-passkey-entry').hidden); 197 expectFalse($('bluetooth-pairing-pincode-entry').hidden); 198 199 // Connect button should be visible but disabled until a pincode is entered. 200 expectFalse($('bluetooth-pair-device-connect-button').hidden); 201 expectFalse($('bluetooth-pair-device-cancel-button').hidden); 202 expectTrue($('bluetooth-pair-device-connect-button').disabled); 203 expectFalse($('bluetooth-pair-device-cancel-button').disabled); 204 205 // Simulate process of entering a pincode. 206 var pincode = '123456'; 207 208 this.mockHandler.expects(once()).updateBluetoothDevice( 209 [fakeDevice.address, 'connect', pincode]).will( 210 callFunction(function() { 211 delete fakeDevice.pairing; 212 fakeDevice.paired = true; 213 options.BrowserOptions.addBluetoothDevice(fakeDevice); 214 })); 215 216 this.fakeInput($('bluetooth-pincode'), pincode); 217 $('bluetooth-pair-device-connect-button').click(); 218 219 // Verify that the device is removed from the unparied list and added to the 220 // paired device list. 221 expectTrue(!!this.getElementForDevice(pairedDeviceList, 222 fakeDevice.name)); 223 expectFalse(!!this.getElementForDevice(unpairedDeviceList, 224 fakeDevice.name)); 225 }); 226 227 TEST_F('BluetoothWebUITest','testConnectionState', function() { 228 assertEquals(this.browsePreload, document.location.href); 229 230 var pairedDeviceList = $('bluetooth-paired-devices-list'); 231 var connectButton = $('bluetooth-reconnect-device'); 232 233 var fakeDevice = { 234 address: '00:24:BE:00:00:00', 235 connectable: true, 236 connected: false, 237 name: 'Sony BT-00', 238 paired: true 239 }; 240 241 options.BrowserOptions.addBluetoothDevice(fakeDevice); 242 var element = this.getElementForDevice(pairedDeviceList, 243 fakeDevice.name); 244 assertTrue(!!element); 245 expectFalse(!!element.getAttribute('connected')); 246 expectTrue(connectButton.disabled); 247 248 // Simulate connecting to a previously paired device. 249 this.selectDevice(pairedDeviceList, fakeDevice); 250 expectFalse(connectButton.disabled); 251 this.mockHandler.expects(once()).updateBluetoothDevice( 252 [fakeDevice.address, 'connect']).will( 253 callFunction(function() { 254 fakeDevice.connected = true; 255 options.BrowserOptions.addBluetoothDevice(fakeDevice); 256 })); 257 connectButton.click(); 258 element = this.getElementForDevice(pairedDeviceList, 259 fakeDevice.name); 260 assertTrue(!!element); 261 expectTrue(!!element.getAttribute('connected')); 262 var button = element.querySelector('.row-delete-button'); 263 expectTrue(!!button); 264 265 Mock4JS.verifyAllMocks(); 266 Mock4JS.clearMocksToVerify(); 267 268 // Test disconnecting. 269 this.mockHandler.expects(once()).updateBluetoothDevice( 270 [fakeDevice.address, 'disconnect']).will( 271 callFunction(function() { 272 fakeDevice.connected = false; 273 options.BrowserOptions.addBluetoothDevice(fakeDevice); 274 })); 275 button.click(); 276 element = this.getElementForDevice(pairedDeviceList, 277 fakeDevice.name); 278 assertTrue(!!element); 279 expectFalse(!!element.getAttribute('connected')); 280 button = element.querySelector('.row-delete-button'); 281 expectTrue(!!button); 282 283 Mock4JS.verifyAllMocks(); 284 Mock4JS.clearMocksToVerify(); 285 286 // Test forgetting a disconnected device. 287 this.mockHandler.expects(once()).updateBluetoothDevice( 288 [fakeDevice.address, 'forget']).will( 289 callFunction(function() { 290 options.BrowserOptions.removeBluetoothDevice(fakeDevice.address); 291 })); 292 button.click(); 293 expectFalse(!!this.getElementForDevice(pairedDeviceList, 294 fakeDevice.name)); 295 }); 296 297 298 TEST_F('BluetoothWebUITest','testMaliciousInput', function() { 299 assertEquals(this.browsePreload, document.location.href); 300 301 var unpairedDeviceList = $('bluetooth-unpaired-devices-list'); 302 var pairDeviceDialog = $('bluetooth-pairing'); 303 304 var maliciousStrings = [ 305 '<SCRIPT>alert(1)</SCRIPT>', 306 '>\'>\\"><SCRIPT>alert(1)</SCRIPT>', 307 '<IMG SRC=\\"javascript:alert(1)\\">', 308 '<A HREF=\\"data:text/html;base64,' + 309 'PHNjcmlwdD5hbGVydCgxKTwvc2NyaXB0Pgo=\\">...</A>', 310 '<div>', 311 '<textarea>', 312 '<style>', 313 '[0xC0][0xBC]SCRIPT[0xC0][0xBE]alert(1)[0xC0][0xBC]/SCRIPT[0xC0][0xBE]', 314 '+ADw-SCRIPT+AD4-alert(1)+ADw-/SCRIPT+AD4-', 315 '&#<script>alert(1)</script>;', 316 '<!-- Hello -- world > <SCRIPT>alert(1)</SCRIPT> -->', 317 '<!<!-- Hello world > <SCRIPT>alert(1)</SCRIPT> -->', 318 '\x3CSCRIPT\x3Ealert(1)\x3C/SCRIPT\x3E', 319 '<IMG SRC=\\"j[0x00]avascript:alert(1)\\">', 320 '<BASE HREF=\\"javascript:1;/**/\\"><IMG SRC=\\"alert(1)\\">', 321 'javascript:alert(1);', 322 ' xss_injection=\\"\\" ', 323 '\\" xss_injection=\\"', 324 '\' xss_injection=\'', 325 '<!--', 326 '\'', 327 '\\"' 328 ]; 329 330 var fakeDevice = { 331 address: '11:22:33:44:55:66', 332 connectable: true, 333 connected: false, 334 name: 'fake', 335 paired: false, 336 pairing: 'bluetoothStartConnecting' 337 }; 338 339 options.BrowserOptions.addBluetoothDevice(fakeDevice); 340 341 var nodeCount = function(node) { 342 if (node.getAttribute) 343 assertFalse(!!node.getAttribute('xss_injection')); 344 var length = node.childNodes.length; 345 var tally = length; 346 for (var i = 0; i < length; i++) { 347 tally += nodeCount(node.childNodes[i]); 348 } 349 return tally; 350 }; 351 352 // Determine the expected sizes. 353 var unpairedDeviceListSize = nodeCount(unpairedDeviceList); 354 var pairDeviceDialogSize = nodeCount(pairDeviceDialog); 355 356 // Ensure that updating the device with a malicious name does not corrupt the 357 // structure of the document. Tests the unpaired device list and bluetooth 358 // pairing dialog. 359 for (var i = 0; i < maliciousStrings.length; i++) { 360 fakeDevice.name = maliciousStrings[i]; 361 options.BrowserOptions.addBluetoothDevice(fakeDevice); 362 assertEquals(unpairedDeviceListSize, nodeCount(unpairedDeviceList)); 363 var element = this.getElementForDevice(unpairedDeviceList, 364 fakeDevice.name); 365 assertTrue(!!element); 366 var label = element.querySelector('.bluetooth-device-label'); 367 assertTrue(!!label); 368 assertEquals(maliciousStrings[i], label.textContent); 369 assertEquals(pairDeviceDialogSize, nodeCount(pairDeviceDialog)); 370 } 371 372 }); 373 374 GEN('#endif'); 375