summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorreillyg <reillyg@chromium.org>2016-03-24 16:55:39 -0700
committerCommit bot <commit-bot@chromium.org>2016-03-24 23:58:26 +0000
commit6cfa6305dc22be1489cec97f8ddcf595d75f2a75 (patch)
treed8eaf9338d4fd022ea18bc81cce920a4665c7217
parent6781323221029e2f5c0ca1b0f055b81bb2d69153 (diff)
downloadchromium_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.cc2
-rw-r--r--third_party/WebKit/LayoutTests/usb/usbDevice.html94
-rw-r--r--third_party/WebKit/Source/modules/webusb/USBDevice.cpp142
-rw-r--r--third_party/WebKit/Source/modules/webusb/USBDevice.h4
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, &parameters);
- if (error)
- resolver->reject(error);
- else
+ if (convertControlTransferParameters(WebUSBDevice::TransferDirection::In, setup, &parameters, 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, &parameters);
- if (error)
- resolver->reject(error);
- else
+ if (convertControlTransferParameters(WebUSBDevice::TransferDirection::Out, setup, &parameters, 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, &parameters);
- if (error) {
- resolver->reject(error);
- } else {
+ if (convertControlTransferParameters(WebUSBDevice::TransferDirection::Out, setup, &parameters, 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