tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

usbDevice_controlTransferIn-manual.https.html (13783B)


      1 <!DOCTYPE html>
      2 <html>
      3  <head>
      4    <meta charset="utf-8">
      5    <title></title>
      6    <script src="/resources/testharness.js"></script>
      7    <script src="/resources/testharnessreport.js"></script>
      8    <script src="resources/manual.js"></script>
      9  </head>
     10  <body>
     11    <p>
     12      These tests require a USB device to be connected.
     13    </p>
     14    <script>
     15      const kGetDescriptorRequest = 0x06;
     16 
     17      const kDeviceDescriptorType = 0x01;
     18      const kDeviceDescriptorLength = 18;
     19 
     20      const kConfigurationDescriptorType = 0x02;
     21      const kConfigurationDescriptorLength = 9;
     22 
     23      const kStringDescriptorType = 0x03;
     24      const kStringDescriptorMaxLength = 0xFF;
     25 
     26      const kInterfaceDescriptorType = 0x04;
     27      const kInterfaceDescriptorLength = 9;
     28 
     29      const kEndpointDescriptorType = 0x05;
     30      const kEndpointDescriptorLength = 7;
     31 
     32      let device = null;
     33      let pending_subtests = 0;
     34      const string_tests = new Set();
     35      const string_languages = [];
     36 
     37      async function subtest_complete() {
     38        if (--pending_subtests == 0) {
     39          await device.close();
     40        }
     41      }
     42 
     43      function manual_usb_subtest(func, name, properties) {
     44        pending_subtests++;
     45        promise_test(async (test) => {
     46          test.add_cleanup(subtest_complete);
     47          await func(test);
     48        }, name, properties);
     49      }
     50 
     51      function read_string(index, expected) {
     52        // A device may use the same string in multiple places. Don't bother
     53        // repeating the test.
     54        if (string_tests.has(index)) {
     55          return;
     56        }
     57        string_tests.add(index);
     58 
     59        const string_values = new Set();
     60        const decoder = new TextDecoder('utf-16le');
     61 
     62        for (const language of string_languages) {
     63          const language_string = language.toString(16).padStart(4, '0');
     64          manual_usb_subtest(async (t) => {
     65            if (expected != undefined) {
     66              t.add_cleanup(() => {
     67                if (string_values.size == string_languages.length) {
     68                  assert_true(string_values.has(expected));
     69                }
     70              });
     71            }
     72 
     73            const result = await device.controlTransferIn({
     74              requestType: 'standard',
     75              recipient: 'device',
     76              request: kGetDescriptorRequest,
     77              value: kStringDescriptorType << 8 | index,
     78              index: language
     79            }, kStringDescriptorMaxLength);
     80 
     81            assert_equals(result.status, 'ok', 'transfer status');
     82            const length = result.data.getUint8(0);
     83            assert_greater_than_equal(length, 2, 'descriptor length');
     84            assert_equals(result.data.byteLength, length, 'transfer length');
     85            assert_equals(result.data.getUint8(1), kStringDescriptorType,
     86                          'descriptor type');
     87            const string_buffer = new Uint8Array(
     88                result.data.buffer, result.data.byteOffset + 2, length - 2);
     89            string_values.add(decoder.decode(string_buffer));
     90          },
     91          `Read string descriptor ${index} in language 0x${language_string}`);
     92        }
     93      }
     94 
     95      function check_interface_descriptor(configuration, data) {
     96        assert_greater_than_equal(
     97            data.getUint8(0), kInterfaceDescriptorLength, 'descriptor length');
     98 
     99        const interface_number = data.getUint8(2);
    100        const iface = configuration.interfaces.find((iface) => {
    101          return iface.interfaceNumber == interface_number;
    102        });
    103        assert_not_equals(
    104            iface, undefined, `unknown interface ${interface_number}`);
    105 
    106        const alternate_setting = data.getUint8(3);
    107        const alternate = iface.alternates.find((alternate) => {
    108          return alternate.alternateSetting == alternate_setting;
    109        });
    110        assert_not_equals(
    111            alternate, undefined, `unknown alternate ${alternate_setting}`);
    112 
    113        assert_equals(data.getUint8(4), alternate.endpoints.length,
    114                      'number of endpoints');
    115        assert_equals(
    116            data.getUint8(5), alternate.interfaceClass, 'interface class');
    117        assert_equals(data.getUint8(6), alternate.interfaceSubclass,
    118                      'interface subclass');
    119        assert_equals(data.getUint8(7), alternate.interfaceProtocol,
    120                      'interface protocol');
    121 
    122        const interface_string = data.getUint8(8);
    123        if (interface_string != 0) {
    124          // TODO(crbug.com/727819): Check that the string descriptor matches
    125          // iface.interfaceName.
    126          read_string(interface_string);
    127        }
    128 
    129        return alternate;
    130      }
    131 
    132      function check_endpoint_descriptor(alternate, data) {
    133        assert_greater_than_equal(
    134            data.getUint8(0), kEndpointDescriptorLength, 'descriptor length');
    135 
    136        const endpoint_address = data.getUint8(2);
    137        const direction = endpoint_address & 0x80 ? 'in' : 'out';
    138        const endpoint_number = endpoint_address & 0x0f;
    139        const endpoint = alternate.endpoints.find((endpoint) => {
    140          return endpoint.direction == direction &&
    141                 endpoint.endpointNumber == endpoint_number;
    142        });
    143        assert_not_equals(
    144            endpoint, undefined, `unknown endpoint ${endpoint_number}`);
    145 
    146        const attributes = data.getUint8(3);
    147        switch (attributes & 0x03) {
    148          case 0:
    149            assert_equals(endpoint.type, 'control', 'endpoint type');
    150            break;
    151          case 1:
    152            assert_equals(endpoint.type, 'isochronous', 'endpoint type');
    153            break;
    154          case 2:
    155            assert_equals(endpoint.type, 'bulk', 'endpoint type');
    156            break;
    157          case 3:
    158            assert_equals(endpoint.type, 'interrupt', 'endpoint type');
    159            break;
    160        }
    161 
    162        assert_equals(data.getUint16(4, /*littleEndian=*/true),
    163                      endpoint.packetSize, 'packet size');
    164      }
    165 
    166      function read_config_descriptor(config_value) {
    167        manual_usb_subtest(async (t) => {
    168          const configuration = device.configurations.find((config) => {
    169            return config.configurationValue == config_value;
    170          });
    171          assert_not_equals(configuration, undefined);
    172 
    173          let result = await device.controlTransferIn({
    174            requestType: 'standard',
    175            recipient: 'device',
    176            request: kGetDescriptorRequest,
    177            value: kConfigurationDescriptorType << 8 | (config_value - 1),
    178            index: 0
    179          }, kConfigurationDescriptorLength);
    180 
    181          assert_equals(result.status, 'ok', 'transfer status');
    182          let length = result.data.getUint8(0);
    183          assert_greater_than_equal(
    184              length, kConfigurationDescriptorLength, 'descriptor length');
    185          assert_equals(result.data.byteLength, length, 'transfer length');
    186          const total_length = result.data.getUint16(2, /*littleEndian=*/true);
    187 
    188          result = await device.controlTransferIn({
    189            requestType: 'standard',
    190            recipient: 'device',
    191            request: kGetDescriptorRequest,
    192            value: kConfigurationDescriptorType << 8 | (config_value - 1),
    193            index: 0
    194          }, total_length);
    195 
    196          assert_equals(result.status, 'ok', 'transfer status');
    197          assert_equals(
    198              result.data.byteLength, total_length, 'transfer length');
    199          assert_equals(result.data.getUint8(0), length, 'descriptor length');
    200          assert_equals(result.data.getUint8(1), kConfigurationDescriptorType,
    201                        'descriptor type');
    202          assert_equals(result.data.getUint16(2, /*littleEndian=*/true),
    203                        total_length, 'total length');
    204          assert_equals(
    205              result.data.getUint8(4), configuration.interfaces.length,
    206              'number of interfaces');
    207          assert_equals(
    208              result.data.getUint8(5), config_value, 'configuration value');
    209 
    210          const configuration_string = result.data.getUint8(6);
    211          if (configuration_string != 0) {
    212            // TODO(crbug.com/727819): Check that the string descriptor matches
    213            // configuration.configurationName.
    214            read_string(configuration_string);
    215          }
    216 
    217          let offset = length;
    218          let alternate = undefined;
    219          while (offset < total_length) {
    220            length = result.data.getUint8(offset);
    221            assert_less_than_equal(offset + length, total_length);
    222 
    223            const view = new DataView(
    224                result.data.buffer, result.data.byteOffset + offset, length);
    225            switch (view.getUint8(1)) {
    226              case kConfigurationDescriptorType:
    227                assert_unreached('cannot contain multiple config descriptors');
    228                break;
    229              case kInterfaceDescriptorType:
    230                alternate = check_interface_descriptor(configuration, view);
    231                break;
    232              case kEndpointDescriptorType:
    233                assert_not_equals(alternate, undefined,
    234                                  'endpoint not defined after interface');
    235                check_endpoint_descriptor(alternate, view);
    236                break;
    237            }
    238 
    239            offset += length;
    240          }
    241        }, `Read config descriptor ${config_value}`);
    242      }
    243 
    244      function read_string_descriptor_languages(device_descriptor) {
    245        manual_usb_subtest(async (t) => {
    246          const result = await device.controlTransferIn({
    247            requestType: 'standard',
    248            recipient: 'device',
    249            request: kGetDescriptorRequest,
    250            value: kStringDescriptorType << 8,
    251            index: 0
    252          }, kStringDescriptorMaxLength);
    253 
    254          assert_equals(result.status, 'ok', 'transfer status');
    255          assert_equals(result.data.getUint8(1), kStringDescriptorType,
    256                        'descriptor type');
    257          const length = result.data.getUint8(0);
    258          assert_greater_than_equal(length, 2, 'descriptor length')
    259          assert_greater_than_equal(
    260              result.data.byteLength, length, 'transfer length');
    261 
    262          for (let index = 2; index < length; index += 2) {
    263            string_languages.push(
    264                result.data.getUint16(index, /*littleEndian=*/true));
    265          }
    266 
    267          const manufacturer_string = device_descriptor.getUint8(14);
    268          if (manufacturer_string != 0) {
    269            assert_not_equals(device.manufacturerName, undefined);
    270            read_string(manufacturer_string, device.manufacturerName);
    271          }
    272 
    273          const product_string = device_descriptor.getUint8(15);
    274          if (product_string != 0) {
    275            assert_not_equals(device.productName, undefined);
    276            read_string(product_string, device.productName);
    277          }
    278 
    279          const serial_number_string = device_descriptor.getUint8(16);
    280          if (serial_number_string != 0) {
    281            assert_not_equals(device.serialNumber, undefined);
    282            read_string(serial_number_string, device.serialNumber);
    283          }
    284 
    285          const num_configurations = device_descriptor.getUint8(17);
    286          for (let config_value = 1; config_value <= num_configurations;
    287               ++config_value) {
    288            read_config_descriptor(config_value);
    289          }
    290        }, `Read supported languages`);
    291      }
    292 
    293      promise_test(async (t) => {
    294        device = await getDeviceForManualTest();
    295        await device.open();
    296 
    297        const result = await device.controlTransferIn({
    298          requestType: 'standard',
    299          recipient: 'device',
    300          request: kGetDescriptorRequest,
    301          value: kDeviceDescriptorType << 8,
    302          index: 0,
    303        }, kDeviceDescriptorLength);
    304 
    305        assert_equals(result.status, 'ok', 'transfer status');
    306        assert_equals(
    307            result.data.byteLength, kDeviceDescriptorLength, 'transfer length');
    308        assert_greater_than_equal(
    309            result.data.getUint8(0),
    310            kDeviceDescriptorLength, 'descriptor length');
    311        assert_equals(result.data.getUint8(1), kDeviceDescriptorType,
    312                      'descriptor type');
    313        const bcd_usb = result.data.getUint16(2, /*littleEndian=*/true);
    314        assert_equals(
    315            bcd_usb >> 8, device.usbVersionMajor, 'USB version major');
    316        assert_equals(
    317            (bcd_usb & 0xf0) >> 4, device.usbVersionMinor, 'USB version minor');
    318        assert_equals(
    319            bcd_usb & 0xf, device.usbVersionSubminor, 'USV version subminor');
    320        assert_equals(
    321            result.data.getUint8(4), device.deviceClass, 'device class');
    322        assert_equals(
    323            result.data.getUint8(5), device.deviceSubclass, 'device subclass');
    324        assert_equals(
    325            result.data.getUint8(6), device.deviceProtocol, 'device protocol');
    326        assert_equals(result.data.getUint16(8, /*littleEndian=*/true),
    327                      device.vendorId, 'vendor id');
    328        assert_equals(result.data.getUint16(10, /*littleEndian=*/true),
    329                      device.productId, 'product id');
    330        const bcd_device = result.data.getUint16(12, /*littleEndian=*/true);
    331        assert_equals(
    332            bcd_device >> 8, device.deviceVersionMajor, 'device version major');
    333        assert_equals((bcd_device & 0xf0) >> 4, device.deviceVersionMinor,
    334                      'device version minor');
    335        assert_equals(bcd_device & 0xf, device.deviceVersionSubminor,
    336                      'device version subminor');
    337        assert_equals(result.data.getUint8(17), device.configurations.length,
    338                      'number of configurations');
    339 
    340        read_string_descriptor_languages(result.data);
    341 
    342        if (pending_subtests == 0) {
    343          await device.close();
    344        }
    345      }, 'Read device descriptor');
    346    </script>
    347  </body>
    348 </html>