diff options
author | reillyg <reillyg@chromium.org> | 2016-03-24 16:55:39 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-03-24 23:58:26 +0000 |
commit | 6cfa6305dc22be1489cec97f8ddcf595d75f2a75 (patch) | |
tree | d8eaf9338d4fd022ea18bc81cce920a4665c7217 | |
parent | 6781323221029e2f5c0ca1b0f055b81bb2d69153 (diff) | |
download | chromium_src-6cfa6305dc22be1489cec97f8ddcf595d75f2a75.zip chromium_src-6cfa6305dc22be1489cec97f8ddcf595d75f2a75.tar.gz chromium_src-6cfa6305dc22be1489cec97f8ddcf595d75f2a75.tar.bz2 |
Track USB endpoint state in Blink.lkgr
The last in a series of patches putting input validation for the WebUSB
directly into Blink. This one adds bit vectors that track which
endpoints are available given the current set of claimed interfaces and
selected alternate interfaces.
BUG=593164
Review URL: https://codereview.chromium.org/1830833002
Cr-Commit-Position: refs/heads/master@{#383194}
-rw-r--r-- | device/usb/mojo/type_converters.cc | 2 | ||||
-rw-r--r-- | third_party/WebKit/LayoutTests/usb/usbDevice.html | 94 | ||||
-rw-r--r-- | third_party/WebKit/Source/modules/webusb/USBDevice.cpp | 142 | ||||
-rw-r--r-- | third_party/WebKit/Source/modules/webusb/USBDevice.h | 4 |
4 files changed, 191 insertions, 51 deletions
diff --git a/device/usb/mojo/type_converters.cc b/device/usb/mojo/type_converters.cc index a2daeaa..b7bfd7f 100644 --- a/device/usb/mojo/type_converters.cc +++ b/device/usb/mojo/type_converters.cc @@ -138,7 +138,7 @@ device::usb::EndpointInfoPtr TypeConverter<device::usb::EndpointInfoPtr, device::UsbEndpointDescriptor>:: Convert(const device::UsbEndpointDescriptor& endpoint) { device::usb::EndpointInfoPtr info = device::usb::EndpointInfo::New(); - info->endpoint_number = endpoint.address; + info->endpoint_number = endpoint.address & 0xf; info->direction = ConvertTo<device::usb::TransferDirection>(endpoint.direction); info->type = ConvertTo<device::usb::EndpointType>(endpoint.transfer_type); diff --git a/third_party/WebKit/LayoutTests/usb/usbDevice.html b/third_party/WebKit/LayoutTests/usb/usbDevice.html index e611148..763228a 100644 --- a/third_party/WebKit/LayoutTests/usb/usbDevice.html +++ b/third_party/WebKit/LayoutTests/usb/usbDevice.html @@ -411,6 +411,69 @@ usb_test(usb => { usb_test(usb => { usb.mockDeviceManager.addMockDevice(usb.fakeDevices[0]); return navigator.usb.getDevices().then(devices => { + assert_equals(1, devices.length); + let device = devices[0]; + 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(usb => { + usb.mockDeviceManager.addMockDevice(usb.fakeDevices[0]); + return navigator.usb.getDevices().then(devices => { assert_equals(devices.length, 1); let device = devices[0]; return device.open() @@ -441,6 +504,37 @@ usb_test(usb => { return navigator.usb.getDevices().then(devices => { assert_equals(devices.length, 1); let device = devices[0]; + 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(usb => { + usb.mockDeviceManager.addMockDevice(usb.fakeDevices[0]); + return navigator.usb.getDevices().then(devices => { + assert_equals(devices.length, 1); + let device = devices[0]; return device.open() .then(() => device.selectConfiguration(1)) .then(() => device.claimInterface(0)) diff --git a/third_party/WebKit/Source/modules/webusb/USBDevice.cpp b/third_party/WebKit/Source/modules/webusb/USBDevice.cpp index 54ec6d9..bbaf4ed6 100644 --- a/third_party/WebKit/Source/modules/webusb/USBDevice.cpp +++ b/third_party/WebKit/Source/modules/webusb/USBDevice.cpp @@ -32,40 +32,7 @@ const char kInterfaceStateChangeInProgress[] = "An operation that changes interf const char kOpenRequired[] = "The device must be opened first."; const char kVisibiltyError[] = "Connection is only allowed while the page is visible. This is a temporary measure until we are able to effectively communicate to the user that the page is connected to a device."; -DOMException* convertControlTransferParameters( - WebUSBDevice::TransferDirection direction, - const USBControlTransferParameters& parameters, - WebUSBDevice::ControlTransferParameters* webParameters) -{ - webParameters->direction = direction; - - if (parameters.requestType() == "standard") - webParameters->type = WebUSBDevice::RequestType::Standard; - else if (parameters.requestType() == "class") - webParameters->type = WebUSBDevice::RequestType::Class; - else if (parameters.requestType() == "vendor") - webParameters->type = WebUSBDevice::RequestType::Vendor; - else - return DOMException::create(TypeMismatchError, "The control transfer requestType parameter is invalid."); - // TODO(reillyg): Check for interface and endpoint availability if that is - // the control transfer target. - if (parameters.recipient() == "device") - webParameters->recipient = WebUSBDevice::RequestRecipient::Device; - else if (parameters.recipient() == "interface") - webParameters->recipient = WebUSBDevice::RequestRecipient::Interface; - else if (parameters.recipient() == "endpoint") - webParameters->recipient = WebUSBDevice::RequestRecipient::Endpoint; - else if (parameters.recipient() == "other") - webParameters->recipient = WebUSBDevice::RequestRecipient::Other; - else - return DOMException::create(TypeMismatchError, "The control transfer recipient parameter is invalid."); - - webParameters->request = parameters.request(); - webParameters->value = parameters.value(); - webParameters->index = parameters.index(); - return nullptr; -} String convertTransferStatus(const WebUSBTransferInfo::Status& status) { @@ -325,6 +292,8 @@ USBDevice::USBDevice(PassOwnPtr<WebUSBDevice> device, ExecutionContext* context) , m_opened(false) , m_deviceStateChangeInProgress(false) , m_configurationIndex(-1) + , m_inEndpoints(15) + , m_outEndpoints(15) { int configurationIndex = findConfigurationIndex(info().activeConfiguration); if (configurationIndex != -1) @@ -348,16 +317,21 @@ void USBDevice::onConfigurationSelected(bool success, size_t configurationIndex) m_interfaceStateChangeInProgress.resize(numInterfaces); m_selectedAlternates.resize(numInterfaces); m_selectedAlternates.fill(0); + m_inEndpoints.clearAll(); + m_outEndpoints.clearAll(); } m_deviceStateChangeInProgress = false; } void USBDevice::onInterfaceClaimedOrUnclaimed(bool claimed, size_t interfaceIndex) { - if (claimed) + if (claimed) { m_claimedInterfaces.set(interfaceIndex); - else + } else { m_claimedInterfaces.clear(interfaceIndex); + m_selectedAlternates[interfaceIndex] = 0; + } + setEndpointsForInterface(interfaceIndex, claimed); m_interfaceStateChangeInProgress.clear(interfaceIndex); } @@ -365,6 +339,7 @@ void USBDevice::onAlternateInterfaceSelected(bool success, size_t interfaceIndex { if (success) m_selectedAlternates[interfaceIndex] = alternateIndex; + setEndpointsForInterface(interfaceIndex, success); m_interfaceStateChangeInProgress.clear(interfaceIndex); } @@ -479,6 +454,9 @@ ScriptPromise USBDevice::releaseInterface(ScriptState* scriptState, uint8_t inte } else if (!m_claimedInterfaces.get(interfaceIndex)) { resolver->resolve(); } else { + // Mark this interface's endpoints unavailable while its state is + // changing. + setEndpointsForInterface(interfaceIndex, false); m_interfaceStateChangeInProgress.set(interfaceIndex); m_device->releaseInterface(interfaceNumber, new ClaimInterfacePromiseAdapter(this, resolver, interfaceIndex, false /* release */)); } @@ -498,6 +476,9 @@ ScriptPromise USBDevice::selectAlternateInterface(ScriptState* scriptState, uint if (alternateIndex == -1) { resolver->reject(DOMException::create(NotFoundError, "The alternate setting provided is not supported by the device in its current configuration.")); } else { + // Mark this old alternate interface's endpoints unavailable while + // the change is in progress. + setEndpointsForInterface(interfaceIndex, false); m_interfaceStateChangeInProgress.set(interfaceIndex); m_device->setInterface(interfaceNumber, alternateSetting, new SelectAlternateInterfacePromiseAdapter(this, resolver, interfaceIndex, alternateIndex)); } @@ -511,10 +492,7 @@ ScriptPromise USBDevice::controlTransferIn(ScriptState* scriptState, const USBCo ScriptPromise promise = resolver->promise(); if (ensureDeviceConfigured(resolver)) { WebUSBDevice::ControlTransferParameters parameters; - DOMException* error = convertControlTransferParameters(WebUSBDevice::TransferDirection::In, setup, ¶meters); - if (error) - resolver->reject(error); - else + if (convertControlTransferParameters(WebUSBDevice::TransferDirection::In, setup, ¶meters, resolver)) m_device->controlTransfer(parameters, nullptr, length, 0, new CallbackPromiseAdapter<InputTransferResult, USBError>(resolver)); } return promise; @@ -526,10 +504,7 @@ ScriptPromise USBDevice::controlTransferOut(ScriptState* scriptState, const USBC ScriptPromise promise = resolver->promise(); if (ensureDeviceConfigured(resolver)) { WebUSBDevice::ControlTransferParameters parameters; - DOMException* error = convertControlTransferParameters(WebUSBDevice::TransferDirection::Out, setup, ¶meters); - if (error) - resolver->reject(error); - else + if (convertControlTransferParameters(WebUSBDevice::TransferDirection::Out, setup, ¶meters, resolver)) m_device->controlTransfer(parameters, nullptr, 0, 0, new CallbackPromiseAdapter<OutputTransferResult, USBError>(resolver)); } return promise; @@ -541,10 +516,7 @@ ScriptPromise USBDevice::controlTransferOut(ScriptState* scriptState, const USBC ScriptPromise promise = resolver->promise(); if (ensureDeviceConfigured(resolver)) { WebUSBDevice::ControlTransferParameters parameters; - DOMException* error = convertControlTransferParameters(WebUSBDevice::TransferDirection::Out, setup, ¶meters); - if (error) { - resolver->reject(error); - } else { + if (convertControlTransferParameters(WebUSBDevice::TransferDirection::Out, setup, ¶meters, resolver)) { BufferSource buffer(data); m_device->controlTransfer(parameters, buffer.data(), buffer.size(), 0, new CallbackPromiseAdapter<OutputTransferResult, USBError>(resolver)); } @@ -723,9 +695,18 @@ bool USBDevice::ensureInterfaceClaimed(uint8_t interfaceNumber, ScriptPromiseRes bool USBDevice::ensureEndpointAvailable(bool inTransfer, uint8_t endpointNumber, ScriptPromiseResolver* resolver) const { - // TODO(reillyg): Check endpoint availability once Blink is tracking which - // alternate interfaces are selected. - return ensureDeviceConfigured(resolver); + if (!ensureDeviceConfigured(resolver)) + return false; + if (endpointNumber == 0 || endpointNumber >= 16) { + resolver->reject(DOMException::create(IndexSizeError, "The specified endpoint number is out of range.")); + return false; + } + auto& bitVector = inTransfer ? m_inEndpoints : m_outEndpoints; + if (!bitVector.get(endpointNumber - 1)) { + resolver->reject(DOMException::create(NotFoundError, "The specified endpoint is not part of a claimed and selected alternate interface.")); + return false; + } + return true; } bool USBDevice::anyInterfaceChangeInProgress() const @@ -737,4 +718,65 @@ bool USBDevice::anyInterfaceChangeInProgress() const return false; } +bool USBDevice::convertControlTransferParameters( + WebUSBDevice::TransferDirection direction, + const USBControlTransferParameters& parameters, + WebUSBDevice::ControlTransferParameters* webParameters, + ScriptPromiseResolver* resolver) const +{ + webParameters->direction = direction; + + if (parameters.requestType() == "standard") { + webParameters->type = WebUSBDevice::RequestType::Standard; + } else if (parameters.requestType() == "class") { + webParameters->type = WebUSBDevice::RequestType::Class; + } else if (parameters.requestType() == "vendor") { + webParameters->type = WebUSBDevice::RequestType::Vendor; + } else { + resolver->reject(DOMException::create(TypeMismatchError, "The control transfer requestType parameter is invalid.")); + return false; + } + + if (parameters.recipient() == "device") { + webParameters->recipient = WebUSBDevice::RequestRecipient::Device; + } else if (parameters.recipient() == "interface") { + size_t interfaceNumber = parameters.index() & 0xff; + if (!ensureInterfaceClaimed(interfaceNumber, resolver)) + return false; + webParameters->recipient = WebUSBDevice::RequestRecipient::Interface; + } else if (parameters.recipient() == "endpoint") { + bool inTransfer = parameters.index() & 0x80; + size_t endpointNumber = parameters.index() & 0x0f; + if (!ensureEndpointAvailable(inTransfer, endpointNumber, resolver)) + return false; + webParameters->recipient = WebUSBDevice::RequestRecipient::Endpoint; + } else if (parameters.recipient() == "other") { + webParameters->recipient = WebUSBDevice::RequestRecipient::Other; + } else { + resolver->reject(DOMException::create(TypeMismatchError, "The control transfer recipient parameter is invalid.")); + return false; + } + + webParameters->request = parameters.request(); + webParameters->value = parameters.value(); + webParameters->index = parameters.index(); + return true; +} + +void USBDevice::setEndpointsForInterface(size_t interfaceIndex, bool set) +{ + const auto& configuration = info().configurations[m_configurationIndex]; + const auto& interface = configuration.interfaces[interfaceIndex]; + const auto& alternate = interface.alternates[m_selectedAlternates[interfaceIndex]]; + for (const auto& endpoint : alternate.endpoints) { + if (endpoint.endpointNumber == 0 || endpoint.endpointNumber >= 16) + continue; // Ignore endpoints with invalid indices. + auto& bitVector = endpoint.direction == WebUSBDevice::TransferDirection::In ? m_inEndpoints : m_outEndpoints; + if (set) + bitVector.set(endpoint.endpointNumber - 1); + else + bitVector.clear(endpoint.endpointNumber - 1); + } +} + } // namespace blink diff --git a/third_party/WebKit/Source/modules/webusb/USBDevice.h b/third_party/WebKit/Source/modules/webusb/USBDevice.h index 7f30b53..d28121f 100644 --- a/third_party/WebKit/Source/modules/webusb/USBDevice.h +++ b/third_party/WebKit/Source/modules/webusb/USBDevice.h @@ -106,6 +106,8 @@ private: bool ensureInterfaceClaimed(uint8_t interfaceNumber, ScriptPromiseResolver*) const; bool ensureEndpointAvailable(bool inTransfer, uint8_t endpointNumber, ScriptPromiseResolver*) const; bool anyInterfaceChangeInProgress() const; + bool convertControlTransferParameters(WebUSBDevice::TransferDirection, const USBControlTransferParameters&, WebUSBDevice::ControlTransferParameters*, ScriptPromiseResolver*) const; + void setEndpointsForInterface(size_t interfaceIndex, bool set); OwnPtr<WebUSBDevice> m_device; bool m_opened; @@ -114,6 +116,8 @@ private: WTF::BitVector m_claimedInterfaces; WTF::BitVector m_interfaceStateChangeInProgress; WTF::Vector<size_t> m_selectedAlternates; + WTF::BitVector m_inEndpoints; + WTF::BitVector m_outEndpoints; }; } // namespace blink |