Revision 7ab96ca42be6ce12b7bf98088d5d154f8f15be59 authored by Morten Stenshorne on 05 April 2018, 06:52:57 UTC, committed by Chromium WPT Sync on 05 April 2018, 06:52:57 UTC
When changing a layout object from in-flow to out-of-flow positioned, we
used to just remove it from the flow thread, risking that there'd no
longer be a column set to associate it with. However, an out-of-flow
positioned descendant may be contained by something that's inside the
flow thread, e.g. if the containing block of an absolutely positioned
object is a relatively positioned object, and that relatively positioned
object is contained by the flow thread.

Since it's hard to detect what the new containing block of an object is
going to be before it has actually gone out of flow, we'll still remove
it from the flow thread, but we'll now detect that we need to re-insert
it when computed style has updated.

Bug: 827424
Change-Id: I413348b0d3ecd0c4b5051e6e9d2a4526863bef60
Reviewed-on: https://chromium-review.googlesource.com/995439
Reviewed-by: Emil A Eklund <eae@chromium.org>
Commit-Queue: Morten Stenshorne <mstensho@chromium.org>
Cr-Commit-Position: refs/heads/master@{#548354}
1 parent 54f844c
Raw File
usbDevice.https.html
<!DOCTYPE html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="resources/fake-devices.js"></script>
<script src="resources/usb-helpers.js"></script>
<script>
'use strict';

function assertRejectsWithNotFoundError(promise) {
  return assertRejectsWithError(promise, 'NotFoundError');
}

function assertRejectsWithNotOpenError(promise) {
  return assertRejectsWithError(
      promise, 'InvalidStateError', 'The device must be opened first.')
}

function assertRejectsWithNotConfiguredError(promise) {
  return assertRejectsWithError(
      promise, 'InvalidStateError',
      'The device must have a configuration selected.');
}

usb_test(() => {
  return getFakeDevice().then(({ device, fakeDevice }) => {
    return waitForDisconnect(fakeDevice)
      .then(() => assertRejectsWithNotFoundError(device.open()));
  });
}, 'open rejects when called on a disconnected device');

usb_test(() => {
  return getFakeDevice().then(({ device, fakeDevice }) => {
    return device.open()
      .then(() => waitForDisconnect(fakeDevice))
      .then(() => {
        assert_false(device.opened);
      });
  });
}, 'disconnection closes the device');

usb_test(() => {
  return getFakeDevice().then(({ device }) => {
    assert_false(device.opened);
    return device.open().then(() => {
      assert_true(device.opened);
      return device.close().then(() => {
        assert_false(device.opened);
      });
    });
  });
}, 'a device can be opened and closed');

usb_test(() => {
  return getFakeDevice().then(({ device }) => {
    return device.open()
      .then(() => device.open())
      .then(() => device.open())
      .then(() => device.open())
      .then(() => device.close())
      .then(() => device.close())
      .then(() => device.close())
      .then(() => device.close());
  });
}, 'open and close can be called multiple times');

usb_test(() => {
  return getFakeDevice().then(({ device }) => {
    const message =
        'An operation that changes the device state is in progress.';
    return Promise.all([
        device.open(),
        assertRejectsWithError(device.open(), 'InvalidStateError', message),
        assertRejectsWithError(device.close(), 'InvalidStateError', message),
    ]).then(() => Promise.all([
        device.close(),
        assertRejectsWithError(device.open(), 'InvalidStateError', message),
        assertRejectsWithError(device.close(), 'InvalidStateError', message),
    ]));
  });
}, 'open and close cannot be called again while open or close are in progress');

usb_test(() => {
  return getFakeDevice().then(({ device, fakeDevice }) => {
    return device.open()
      .then(() => waitForDisconnect(fakeDevice))
      .then(() => assertRejectsWithNotFoundError(device.close()));
  });
}, 'close rejects when called on a disconnected device');

usb_test(() => {
  return getFakeDevice().then(({ device, fakeDevice }) => {
    return device.open()
      .then(() => waitForDisconnect(fakeDevice))
      .then(() => assertRejectsWithNotFoundError(device.selectConfiguration(1)));
  });
}, 'selectConfiguration rejects when called on a disconnected device');

usb_test(() => {
  return getFakeDevice().then(({ device }) => Promise.all([
      assertRejectsWithNotOpenError(device.selectConfiguration(1)),
      assertRejectsWithNotOpenError(device.claimInterface(0)),
      assertRejectsWithNotOpenError(device.releaseInterface(0)),
      assertRejectsWithNotOpenError(device.selectAlternateInterface(0, 1)),
      assertRejectsWithNotOpenError(device.controlTransferIn({
          requestType: 'vendor',
          recipient: 'device',
          request: 0x42,
          value: 0x1234,
          index: 0x5678
      }, 7)),
      assertRejectsWithNotOpenError(device.controlTransferOut({
          requestType: 'vendor',
          recipient: 'device',
          request: 0x42,
          value: 0x1234,
          index: 0x5678
      }, new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]))),
      assertRejectsWithNotOpenError(device.clearHalt('in', 1)),
      assertRejectsWithNotOpenError(device.transferIn(1, 8)),
      assertRejectsWithNotOpenError(
          device.transferOut(1, new ArrayBuffer(8))),
      assertRejectsWithNotOpenError(device.isochronousTransferIn(1, [8])),
      assertRejectsWithNotOpenError(
          device.isochronousTransferOut(1, new ArrayBuffer(8), [8])),
      assertRejectsWithNotOpenError(device.reset())
  ]));
}, 'methods requiring it reject when the device is not open');

usb_test(() => {
  return getFakeDevice().then(({ device }) => {
    assert_equals(device.configuration, null);
    return device.open()
      .then(() => {
        assert_equals(device.configuration, null);
        return device.selectConfiguration(1);
      })
      .then(() => {
        assertDeviceInfoEquals(
            device.configuration, fakeDeviceInit.configurations[0]);
      })
      .then(() => device.close());
  });
}, 'device configuration can be set and queried');

usb_test(() => {
  return getFakeDevice().then(({ device }) => {
    assert_equals(device.configuration, null);
    return device.open()
      .then(() => assertRejectsWithError(
            device.selectConfiguration(3), 'NotFoundError',
            'The configuration value provided is not supported by the device.'))
      .then(() => device.close());
  });
}, 'selectConfiguration rejects on invalid configurations');

usb_test(() => {
  return getFakeDevice().then(({ device }) => {
    assert_equals(device.configuration, null);
    return device.open().then(() => Promise.all([
        assertRejectsWithNotConfiguredError(device.claimInterface(0)),
        assertRejectsWithNotConfiguredError(device.releaseInterface(0)),
        assertRejectsWithNotConfiguredError(device.selectAlternateInterface(0, 1)),
        assertRejectsWithNotConfiguredError(device.controlTransferIn({
            requestType: 'vendor',
            recipient: 'device',
            request: 0x42,
            value: 0x1234,
            index: 0x5678
        }, 7)),
        assertRejectsWithNotConfiguredError(device.controlTransferOut({
            requestType: 'vendor',
            recipient: 'device',
            request: 0x42,
            value: 0x1234,
            index: 0x5678
        }, new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]))),
        assertRejectsWithNotConfiguredError(device.clearHalt('in', 1)),
        assertRejectsWithNotConfiguredError(device.transferIn(1, 8)),
        assertRejectsWithNotConfiguredError(
            device.transferOut(1, new ArrayBuffer(8))),
        assertRejectsWithNotConfiguredError(
            device.isochronousTransferIn(1, [8])),
        assertRejectsWithNotConfiguredError(
            device.isochronousTransferOut(1, new ArrayBuffer(8), [8])),
    ])).then(() => device.close());
  });
}, 'methods requiring it reject when the device is unconfigured');

usb_test(() => {
  return getFakeDevice().then(({ device }) => {
    return device.open()
      .then(() => device.selectConfiguration(1))
      .then(() => device.claimInterface(0))
      .then(() => {
        assert_true(device.configuration.interfaces[0].claimed);
        return device.releaseInterface(0);
      })
      .then(() => {
        assert_false(device.configuration.interfaces[0].claimed);
        return device.close();
      });
  });
}, 'an interface can be claimed and released');

usb_test(() => {
  return getFakeDevice().then(({ device }) => {
    return device.open()
      .then(() => device.selectConfiguration(1))
      .then(() => device.claimInterface(0))
      .then(() => {
        assert_true(device.configuration.interfaces[0].claimed);
        return device.close(0);
      })
      .then(() => {
        assert_false(device.configuration.interfaces[0].claimed);
      });
  });
}, 'interfaces are released on close');

usb_test(() => {
  return getFakeDevice().then(({ device }) => {
    const message = 'The interface number provided is not supported by the ' +
                    'device in its current configuration.';
    return device.open()
      .then(() => device.selectConfiguration(1))
      .then(() => Promise.all([
          assertRejectsWithError(
              device.claimInterface(2), 'NotFoundError', message),
          assertRejectsWithError(
              device.releaseInterface(2), 'NotFoundError', message),
      ]))
      .then(() => device.close());
  });
}, 'a non-existent interface cannot be claimed or released');

usb_test(() => {
  return getFakeDevice().then(({ device, fakeDevice }) => {
    return device.open()
      .then(() => device.selectConfiguration(1))
      .then(() => waitForDisconnect(fakeDevice))
      .then(() => assertRejectsWithNotFoundError(device.claimInterface(0)));
  });
}, 'claimInterface rejects when called on a disconnected device');

usb_test(() => {
  return getFakeDevice().then(({ device, fakeDevice }) => {
    return device.open()
      .then(() => device.selectConfiguration(1))
      .then(() => device.claimInterface(0))
      .then(() => waitForDisconnect(fakeDevice))
      .then(() => assertRejectsWithNotFoundError(device.releaseInterface(0)));
  });
}, 'releaseInterface rejects when called on a disconnected device');

usb_test(() => {
  return getFakeDevice().then(({ device }) => {
    return device.open()
      .then(() => device.selectConfiguration(2))
      .then(() => device.claimInterface(0))
      .then(() => device.selectAlternateInterface(0, 1))
      .then(() => device.close());
  });
}, 'can select an alternate interface');

usb_test(() => {
  return getFakeDevice().then(({ device }) => {
    return device.open()
      .then(() => device.selectConfiguration(2))
      .then(() => device.claimInterface(0))
      .then(() => assertRejectsWithError(
          device.selectAlternateInterface(0, 2), 'NotFoundError',
          'The alternate setting provided is not supported by the device in ' +
          'its current configuration.'))
      .then(() => device.close());
  });
}, 'cannot select a non-existent alternate interface');

usb_test(() => {
  return getFakeDevice().then(({ device, fakeDevice }) => {
    return device.open()
      .then(() => device.selectConfiguration(2))
      .then(() => device.claimInterface(0))
      .then(() => waitForDisconnect(fakeDevice))
      .then(() => assertRejectsWithNotFoundError(device.selectAlternateInterface(0, 1)));
  });
}, 'selectAlternateInterface rejects when called on a disconnected device');

usb_test(() => {
  return getFakeDevice().then(({ device }) => {
    return device.open()
      .then(() => device.selectConfiguration(1))
      .then(() => device.controlTransferIn({
        requestType: 'vendor',
        recipient: 'device',
        request: 0x42,
        value: 0x1234,
        index: 0x5678
      }, 7))
      .then(result => {
        assert_true(result instanceof USBInTransferResult);
        assert_equals(result.status, 'ok');
        assert_equals(result.data.byteLength, 7);
        assert_equals(result.data.getUint16(0), 0x07);
        assert_equals(result.data.getUint8(2), 0x42);
        assert_equals(result.data.getUint16(3), 0x1234);
        assert_equals(result.data.getUint16(5), 0x5678);
        return device.close();
      });
  });
}, 'can issue IN control transfer');

usb_test(() => {
  return getFakeDevice().then(({ device, fakeDevice }) => {
    return device.open()
      .then(() => device.selectConfiguration(1))
      .then(() => waitForDisconnect(fakeDevice))
      .then(() => assertRejectsWithNotFoundError(device.controlTransferIn({
          requestType: 'vendor',
          recipient: 'device',
          request: 0x42,
          value: 0x1234,
          index: 0x5678
        }, 7)));
  });
}, 'controlTransferIn rejects when called on a disconnected device');

usb_test(() => {
  return getFakeDevice().then(({ device }) => {
    return device.open()
      .then(() => device.selectConfiguration(1))
      .then(() => device.controlTransferOut({
        requestType: 'vendor',
        recipient: 'device',
        request: 0x42,
        value: 0x1234,
        index: 0x5678
      }, new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8])))
    .then(result => {
      assert_true(result instanceof USBOutTransferResult);
      assert_equals(result.status, 'ok');
      assert_equals(result.bytesWritten, 8);
      return device.close();
    })
  });
}, 'can issue OUT control transfer');

usb_test(() => {
  return getFakeDevice().then(({ device, fakeDevice }) => {
    return device.open()
      .then(() => device.selectConfiguration(1))
      .then(() => waitForDisconnect(fakeDevice))
      .then(() => assertRejectsWithNotFoundError(device.controlTransferOut({
          requestType: 'vendor',
          recipient: 'device',
          request: 0x42,
          value: 0x1234,
          index: 0x5678
        }, new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]))));
  });
}, 'controlTransferOut rejects when called on a disconnected device');

usb_test(() => {
  return getFakeDevice().then(({ device }) => {
    let interfaceRequest = {
        requestType: 'vendor',
        recipient: 'interface',
        request: 0x42,
        value: 0x1234,
        index: 0x5600  // Last byte of index is interface number.
    };
    let endpointRequest = {
        requestType: 'vendor',
        recipient: 'endpoint',
        request: 0x42,
        value: 0x1234,
        index: 0x5681  // Last byte of index is endpoint address.
    };
    let data = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);
    return device.open()
      .then(() => device.selectConfiguration(1))
      .then(() => Promise.all([
          assertRejectsWithError(
              device.controlTransferIn(interfaceRequest, 7),
              'InvalidStateError'),
          assertRejectsWithError(
              device.controlTransferIn(endpointRequest, 7),
              'NotFoundError'),
          assertRejectsWithError(
              device.controlTransferOut(interfaceRequest, data),
              'InvalidStateError'),
          assertRejectsWithError(
              device.controlTransferOut(endpointRequest, data),
              'NotFoundError'),
      ]))
      .then(() => device.claimInterface(0))
      .then(() => Promise.all([
          device.controlTransferIn(interfaceRequest, 7).then(result => {
            assert_true(result instanceof USBInTransferResult);
            assert_equals(result.status, 'ok');
            assert_equals(result.data.byteLength, 7);
            assert_equals(result.data.getUint16(0), 0x07);
            assert_equals(result.data.getUint8(2), 0x42);
            assert_equals(result.data.getUint16(3), 0x1234);
            assert_equals(result.data.getUint16(5), 0x5600);
          }),
          device.controlTransferIn(endpointRequest, 7).then(result => {
            assert_true(result instanceof USBInTransferResult);
            assert_equals(result.status, 'ok');
            assert_equals(result.data.byteLength, 7);
            assert_equals(result.data.getUint16(0), 0x07);
            assert_equals(result.data.getUint8(2), 0x42);
            assert_equals(result.data.getUint16(3), 0x1234);
            assert_equals(result.data.getUint16(5), 0x5681);
          }),
          device.controlTransferOut(interfaceRequest, data),
          device.controlTransferOut(endpointRequest, data),
      ]))
      .then(() => device.close());
  });
}, 'requests to interfaces and endpoint require an interface claim');

usb_test(() => {
  return getFakeDevice().then(({ device }) => {
    return device.open()
      .then(() => device.selectConfiguration(1))
      .then(() => device.claimInterface(0))
      .then(() => device.clearHalt('in', 1))
      .then(() => device.close());
  });
}, 'can clear a halt condition');

usb_test(() => {
  return getFakeDevice().then(({ device, fakeDevice }) => {
    return device.open()
      .then(() => device.selectConfiguration(1))
      .then(() => device.claimInterface(0))
      .then(() => waitForDisconnect(fakeDevice))
      .then(() => assertRejectsWithNotFoundError(device.clearHalt('in', 1)));
  });
}, 'clearHalt rejects when called on a disconnected device');

usb_test(() => {
  return getFakeDevice().then(({ device }) => {
    let data = new DataView(new ArrayBuffer(1024));
    for (let i = 0; i < 1024; ++i)
      data.setUint8(i, i & 0xff);
    const notFoundMessage = 'The specified endpoint is not part of a claimed ' +
                            'and selected alternate interface.';
    const rangeError = 'The specified endpoint number is out of range.';
    return device.open()
      .then(() => device.selectConfiguration(1))
      .then(() => device.claimInterface(0))
      .then(() => Promise.all([
          assertRejectsWithError(device.transferIn(2, 8),
                                 'NotFoundError', notFoundMessage), // Unclaimed
          assertRejectsWithError(device.transferIn(3, 8), 'NotFoundError',
                                 notFoundMessage), // Non-existent
          assertRejectsWithError(
              device.transferIn(16, 8), 'IndexSizeError', rangeError),
          assertRejectsWithError(device.transferOut(2, data),
                                 'NotFoundError', notFoundMessage), // Unclaimed
          assertRejectsWithError(device.transferOut(3, data), 'NotFoundError',
                                 notFoundMessage), // Non-existent
          assertRejectsWithError(
              device.transferOut(16, data), 'IndexSizeError', rangeError),
      ]));
  });
}, 'transfers to unavailable endpoints are rejected');

usb_test(() => {
  return getFakeDevice().then(({ device }) => {
    return device.open()
      .then(() => device.selectConfiguration(1))
      .then(() => device.claimInterface(0))
      .then(() => device.transferIn(1, 8))
      .then(result => {
        assert_true(result instanceof USBInTransferResult);
        assert_equals(result.status, 'ok');
        assert_equals(result.data.byteLength, 8);
        for (let i = 0; i < 8; ++i)
          assert_equals(result.data.getUint8(i), i, 'mismatch at byte ' + i);
        return device.close();
      });
  });
}, 'can issue IN interrupt transfer');

usb_test(() => {
  return getFakeDevice().then(({ device }) => {
    return device.open()
      .then(() => device.selectConfiguration(1))
      .then(() => device.claimInterface(1))
      .then(() => device.transferIn(2, 1024))
      .then(result => {
        assert_true(result instanceof USBInTransferResult);
        assert_equals(result.status, 'ok');
        assert_equals(result.data.byteLength, 1024);
        for (let i = 0; i < 1024; ++i)
          assert_equals(result.data.getUint8(i), i & 0xff,
                        'mismatch at byte ' + i);
        return device.close();
      });
  });
}, 'can issue IN bulk transfer');

usb_test(() => {
  return getFakeDevice().then(({ device, fakeDevice }) => {
    return device.open()
      .then(() => device.selectConfiguration(1))
      .then(() => device.claimInterface(1))
      .then(() => waitForDisconnect(fakeDevice))
      .then(() => assertRejectsWithNotFoundError(device.transferIn(2, 1024)));
  });
}, 'transferIn rejects if called on a disconnected device');

usb_test(() => {
  return getFakeDevice().then(({ device }) => {
    return device.open()
      .then(() => device.selectConfiguration(1))
      .then(() => device.claimInterface(1))
      .then(() => {
        let data = new DataView(new ArrayBuffer(1024));
        for (let i = 0; i < 1024; ++i)
          data.setUint8(i, i & 0xff);
        return device.transferOut(2, data);
      })
      .then(result => {
        assert_true(result instanceof USBOutTransferResult);
        assert_equals(result.status, 'ok');
        assert_equals(result.bytesWritten, 1024);
        return device.close();
      });
  });
}, 'can issue OUT bulk transfer');

usb_test(() => {
  return getFakeDevice().then(({ device, fakeDevice }) => {
    return device.open()
      .then(() => device.selectConfiguration(1))
      .then(() => device.claimInterface(1))
      .then(() => {
        let data = new DataView(new ArrayBuffer(1024));
        for (let i = 0; i < 1024; ++i)
          data.setUint8(i, i & 0xff);
        return waitForDisconnect(fakeDevice)
          .then(() => assertRejectsWithNotFoundError(device.transferOut(2, data)));
      });
  });
}, 'transferOut rejects if called on a disconnected device');

usb_test(() => {
  return getFakeDevice().then(({ device }) => {
    return device.open()
      .then(() => device.selectConfiguration(2))
      .then(() => device.claimInterface(0))
      .then(() => device.selectAlternateInterface(0, 1))
      .then(() => device.isochronousTransferIn(
          1, [64, 64, 64, 64, 64, 64, 64, 64]))
      .then(result => {
        assert_true(result instanceof USBIsochronousInTransferResult);
        assert_equals(result.data.byteLength, 64 * 8, 'buffer size');
        assert_equals(result.packets.length, 8, 'number of packets');
        let byteOffset = 0;
        for (let i = 0; i < result.packets.length; ++i) {
          assert_true(
              result.packets[i] instanceof USBIsochronousInTransferPacket);
          assert_equals(result.packets[i].status, 'ok');
          assert_equals(result.packets[i].data.byteLength, 64);
          assert_equals(result.packets[i].data.buffer, result.data.buffer);
          assert_equals(result.packets[i].data.byteOffset, byteOffset);
          for (let j = 0; j < 64; ++j)
            assert_equals(result.packets[i].data.getUint8(j), j & 0xff,
                          'mismatch at byte ' + j + ' of packet ' + i);
          byteOffset += result.packets[i].data.byteLength;
        }
        return device.close();
      });
  });
}, 'can issue IN isochronous transfer');

usb_test(() => {
  return getFakeDevice().then(({ device, fakeDevice }) => {
    return device.open()
      .then(() => device.selectConfiguration(2))
      .then(() => device.claimInterface(0))
      .then(() => device.selectAlternateInterface(0, 1))
      .then(() => waitForDisconnect(fakeDevice))
      .then(() => assertRejectsWithNotFoundError(device.isochronousTransferIn(
          1, [64, 64, 64, 64, 64, 64, 64, 64])));
  });
}, 'isochronousTransferIn rejects when called on a disconnected device');

usb_test(() => {
  return getFakeDevice().then(({ device }) => {
    return device.open()
      .then(() => device.selectConfiguration(2))
      .then(() => device.claimInterface(0))
      .then(() => device.selectAlternateInterface(0, 1))
      .then(() => {
        let data = new DataView(new ArrayBuffer(64 * 8));
        for (let i = 0; i < 8; ++i) {
          for (let j = 0; j < 64; ++j)
            data.setUint8(i * j, j & 0xff);
        }
        return device.isochronousTransferOut(
            1, data, [64, 64, 64, 64, 64, 64, 64, 64]);
      })
      .then(result => {
        assert_true(result instanceof USBIsochronousOutTransferResult);
        assert_equals(result.packets.length, 8, 'number of packets');
        let byteOffset = 0;
        for (let i = 0; i < result.packets.length; ++i) {
          assert_true(
              result.packets[i] instanceof USBIsochronousOutTransferPacket);
          assert_equals(result.packets[i].status, 'ok');
          assert_equals(result.packets[i].bytesWritten, 64);
        }
        return device.close();
      });
  });
}, 'can issue OUT isochronous transfer');

usb_test(() => {
  return getFakeDevice().then(({ device, fakeDevice }) => {
    return device.open()
      .then(() => device.selectConfiguration(2))
      .then(() => device.claimInterface(0))
      .then(() => device.selectAlternateInterface(0, 1))
      .then(() => {
        let data = new DataView(new ArrayBuffer(64 * 8));
        for (let i = 0; i < 8; ++i) {
          for (let j = 0; j < 64; ++j)
            data.setUint8(i * j, j & 0xff);
        }
        return waitForDisconnect(fakeDevice)
          .then(() => assertRejectsWithNotFoundError(device.isochronousTransferOut(
              1, data, [64, 64, 64, 64, 64, 64, 64, 64])));
      });
  });
}, 'isochronousTransferOut rejects when called on a disconnected device');

usb_test(() => {
  return getFakeDevice().then(({ device }) => {
    return device.open().then(() => device.reset()).then(() => device.close());
  });
}, 'can reset the device');

usb_test(() => {
  return getFakeDevice().then(({ device, fakeDevice }) => {
    return device.open()
      .then(() => waitForDisconnect(fakeDevice))
      .then(() => assertRejectsWithNotFoundError(device.reset()));
  });
}, 'resetDevice rejects when called on a disconnected device');
</script>
back to top