Index: device/usb/webusb_descriptors.cc |
diff --git a/device/usb/webusb_descriptors.cc b/device/usb/webusb_descriptors.cc |
index 43ad170f44da0ae2e052199e7b9221b946c9af6b..27a3d38ea8f8ae6531284945373424e4475a2162 100644 |
--- a/device/usb/webusb_descriptors.cc |
+++ b/device/usb/webusb_descriptors.cc |
@@ -5,9 +5,19 @@ |
#include <stddef.h> |
#include <iterator> |
+#include <map> |
+#include <set> |
+#include "base/barrier_closure.h" |
+#include "base/bind.h" |
+#include "base/callback.h" |
#include "base/logging.h" |
+#include "components/device_event_log/device_event_log.h" |
+#include "device/usb/usb_device_handle.h" |
#include "device/usb/webusb_descriptors.h" |
+#include "net/base/io_buffer.h" |
+ |
+using net::IOBufferWithSize; |
namespace device { |
@@ -15,6 +25,8 @@ namespace { |
// These constants are defined by the Universal Serial Device 3.0 Specification |
// Revision 1.0. |
+const uint8_t kGetDescriptorRequest = 0x06; |
+ |
const uint8_t kBosDescriptorType = 0x0F; |
const uint8_t kDeviceCapabilityDescriptorType = 0x10; |
@@ -22,136 +34,325 @@ const uint8_t kPlatformDevCapabilityType = 0x05; |
// These constants are defined by the WebUSB specification: |
// http://wicg.github.io/webusb/ |
+const uint8_t kGetAllowedOriginsRequest = 0x01; |
+const uint8_t kGetUrlRequest = 0x02; |
+ |
const uint8_t kWebUsbCapabilityUUID[16] = { |
// Little-endian encoding of {3408b638-09a9-47a0-8bfd-a0768815b665}. |
0x38, 0xB6, 0x08, 0x34, 0xA9, 0x09, 0xA0, 0x47, |
0x8B, 0xFD, 0xA0, 0x76, 0x88, 0x15, 0xB6, 0x65}; |
-const uint8_t kDescriptorSetDescriptorType = 0x00; |
-const uint8_t kConfigurationSubsetDescriptorType = 0x01; |
-const uint8_t kFunctionSubsetDescriptorType = 0x02; |
-const uint8_t kUrlDescriptorType = 0x03; |
+const int kControlTransferTimeout = 60000; // 1 minute |
+ |
+using ReadWebUsbDescriptorsCallback = |
+ base::Callback<void(scoped_ptr<WebUsbAllowedOrigins> allowed_origins, |
+ const GURL& landing_page)>; |
+ |
+using ReadWebUsbAllowedOriginsCallback = |
+ base::Callback<void(scoped_ptr<WebUsbAllowedOrigins> allowed_origins)>; |
+ |
+// Parses a WebUSB Function Subset Header: |
+// http://wicg.github.io/webusb/#dfn-function-subset-header |
+// |
+// 0 1 2 3 |
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+// | length | type | 1st interface | origin[0] | |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+// | origin[1] | ... |
+// +-+-+-+-+-+-+-+-+-+-+-+------ |
+bool ParseFunction(WebUsbFunctionSubset* function, |
+ std::vector<uint8_t>::const_iterator* it, |
+ std::vector<uint8_t>::const_iterator end) { |
+ const uint8_t kDescriptorType = 0x02; |
+ const uint8_t kDescriptorMinLength = 3; |
-bool ParseUrl(GURL* url, |
- std::vector<uint8_t>::const_iterator* it, |
- std::vector<uint8_t>::const_iterator end) { |
- // These conditions must be guaranteed by the caller. |
- DCHECK(*it != end); |
+ // If this isn't the end of the buffer then there must be at least one byte |
+ // available, the length of the descriptor. |
+ if (*it == end) |
+ return false; |
uint8_t length = (*it)[0]; |
- DCHECK_LE(length, std::distance(*it, end)); |
- DCHECK_GE(length, 2); |
- DCHECK_EQ((*it)[1], kUrlDescriptorType); |
- if (length == 2) { |
+ // Is this a valid Function Subset Header? It must be long enough, fit within |
+ // the buffer and have the right descriptor type. |
+ if (length < kDescriptorMinLength || std::distance(*it, end) < length || |
+ (*it)[1] != kDescriptorType) { |
return false; |
} |
- const char* str = reinterpret_cast<const char*>(&(*it)[2]); |
- *url = GURL(std::string(str, length - 2)); |
- if (!url->is_valid()) { |
- return false; |
+ function->first_interface = (*it)[2]; |
+ |
+ // Everything after the mandatory fields are origin indicies. |
+ std::advance(*it, kDescriptorMinLength); |
+ uint8_t num_origins = length - kDescriptorMinLength; |
+ function->origin_ids.reserve(num_origins); |
+ for (size_t i = 0; i < num_origins; ++i) { |
+ uint8_t index = *(*it)++; |
+ if (index == 0) |
+ return false; |
+ function->origin_ids.push_back(index); |
} |
- std::advance(*it, length); |
return true; |
} |
-bool ParseFunction(WebUsbFunctionSubset* function, |
- std::vector<uint8_t>::const_iterator* it, |
- std::vector<uint8_t>::const_iterator end) { |
- // These conditions must be guaranteed by the caller. |
- DCHECK(*it != end); |
+// Parses a WebUSB Configuration Subset Header: |
+// http://wicg.github.io/webusb/#dfn-configuration-subset-header |
+// |
+// 0 1 2 3 |
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+// | length | type | config value | num functions | |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+// | origin[0] | origin[1] | ... |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+------ |
+bool ParseConfiguration(WebUsbConfigurationSubset* configuration, |
+ std::vector<uint8_t>::const_iterator* it, |
+ std::vector<uint8_t>::const_iterator end) { |
+ const uint8_t kDescriptorType = 0x01; |
+ const uint8_t kDescriptorMinLength = 4; |
+ |
+ // If this isn't the end of the buffer then there must be at least one byte |
+ // available, the length of the descriptor. |
+ if (*it == end) |
+ return false; |
uint8_t length = (*it)[0]; |
- DCHECK_LE(length, std::distance(*it, end)); |
- DCHECK_GE(length, 2); |
- DCHECK_EQ((*it)[1], kFunctionSubsetDescriptorType); |
- if (length != 5) { |
+ // Is this a valid Configuration Subset Header? It must be long enough, fit |
+ // within the buffer and have the right descriptor type. |
+ if (length < kDescriptorMinLength || std::distance(*it, end) < length || |
+ (*it)[1] != kDescriptorType) { |
return false; |
} |
- function->first_interface = (*it)[2]; |
+ configuration->configuration_value = (*it)[2]; |
+ uint8_t num_functions = (*it)[3]; |
+ |
+ // The next |length - 4| bytes after the mandatory fields are origin indicies. |
+ std::advance(*it, kDescriptorMinLength); |
+ uint8_t num_origins = length - kDescriptorMinLength; |
+ configuration->origin_ids.reserve(num_origins); |
+ for (size_t i = 0; i < num_origins; ++i) { |
+ uint8_t index = *(*it)++; |
+ if (index == 0) |
+ return false; |
+ configuration->origin_ids.push_back(index); |
+ } |
- // Validate the Function Subset header. |
- uint16_t total_length = (*it)[3] + ((*it)[4] << 8); |
- if (length > total_length || total_length > std::distance(*it, end)) { |
- return false; |
+ // |num_functions| function descriptors then follow the descriptor. |
+ for (size_t i = 0; i < num_functions; ++i) { |
+ WebUsbFunctionSubset function; |
+ if (!ParseFunction(&function, it, end)) |
+ return false; |
+ configuration->functions.push_back(function); |
} |
- end = *it + total_length; |
- std::advance(*it, length); |
+ return true; |
+} |
- while (*it != end) { |
- uint8_t length = (*it)[0]; |
- if (length < 2 || std::distance(*it, end) < length) { |
- return false; |
+void OnDoneReadingUrls(scoped_ptr<WebUsbAllowedOrigins> allowed_origins, |
+ uint8_t landing_page_id, |
+ scoped_ptr<std::map<uint8_t, GURL>> url_map, |
+ const ReadWebUsbDescriptorsCallback& callback) { |
+ for (uint8_t origin_id : allowed_origins->origin_ids) { |
+ const auto& it = url_map->find(origin_id); |
+ if (it != url_map->end()) |
+ allowed_origins->origins.push_back(it->second.GetOrigin()); |
+ } |
+ |
+ for (auto& configuration : allowed_origins->configurations) { |
+ for (uint8_t origin_id : configuration.origin_ids) { |
+ const auto& it = url_map->find(origin_id); |
+ if (it != url_map->end()) |
+ configuration.origins.push_back(it->second.GetOrigin()); |
} |
- uint8_t type = (*it)[1]; |
- if (type == kUrlDescriptorType) { |
- GURL url; |
- if (!ParseUrl(&url, it, end)) { |
- return false; |
+ for (auto& function : configuration.functions) { |
+ for (uint8_t origin_id : function.origin_ids) { |
+ const auto& it = url_map->find(origin_id); |
+ if (it != url_map->end()) |
+ function.origins.push_back(it->second.GetOrigin()); |
} |
- function->origins.push_back(url.GetOrigin()); |
- } else { |
- return false; |
} |
} |
- return true; |
+ GURL landing_page; |
+ if (landing_page_id != 0) |
+ landing_page = (*url_map)[landing_page_id]; |
+ |
+ callback.Run(std::move(allowed_origins), landing_page); |
} |
-bool ParseConfiguration(WebUsbConfigurationSubset* configuration, |
- std::vector<uint8_t>::const_iterator* it, |
- std::vector<uint8_t>::const_iterator end) { |
- // These conditions must be guaranteed by the caller. |
- DCHECK(*it != end); |
- uint8_t length = (*it)[0]; |
- DCHECK_LE(length, std::distance(*it, end)); |
- DCHECK_GE(length, 2); |
- DCHECK_EQ((*it)[1], kConfigurationSubsetDescriptorType); |
+void OnReadUrlDescriptor(std::map<uint8_t, GURL>* url_map, |
+ uint8_t index, |
+ const base::Closure& callback, |
+ UsbTransferStatus status, |
+ scoped_refptr<net::IOBuffer> buffer, |
+ size_t length) { |
+ if (status != USB_TRANSFER_COMPLETED) { |
+ USB_LOG(EVENT) << "Failed to read WebUSB URL descriptor: " << index; |
+ callback.Run(); |
+ return; |
+ } |
- if (length != 5) { |
- return false; |
+ GURL url; |
+ if (ParseWebUsbUrlDescriptor( |
+ std::vector<uint8_t>(buffer->data(), buffer->data() + length), |
+ &url)) { |
+ (*url_map)[index] = url; |
} |
+ callback.Run(); |
+} |
- configuration->configuration_value = (*it)[2]; |
+// Reads the descriptor with |index| from the device, adds the value to |
+// |url_map| and then runs |callback|. |
+void ReadUrlDescriptor(scoped_refptr<UsbDeviceHandle> device_handle, |
+ uint8_t vendor_code, |
+ std::map<uint8_t, GURL>* url_map, |
+ uint8_t index, |
+ const base::Closure& callback) { |
+ scoped_refptr<IOBufferWithSize> buffer = new IOBufferWithSize(255); |
+ device_handle->ControlTransfer( |
+ USB_DIRECTION_INBOUND, UsbDeviceHandle::VENDOR, UsbDeviceHandle::DEVICE, |
+ vendor_code, index, kGetUrlRequest, buffer, buffer->size(), |
+ kControlTransferTimeout, |
+ base::Bind(&OnReadUrlDescriptor, url_map, index, callback)); |
+} |
- // Validate the Configuration Subset header. |
- uint16_t total_length = (*it)[3] + ((*it)[4] << 8); |
- if (length > total_length || total_length > std::distance(*it, end)) { |
- return false; |
+// Reads URL descriptors from the device so that it can fill |allowed_origins| |
+// with the GURLs matching the indicies already collected. |
+void ReadUrlDescriptors(scoped_refptr<UsbDeviceHandle> device_handle, |
+ uint8_t vendor_code, |
+ uint8_t landing_page_id, |
+ const ReadWebUsbDescriptorsCallback& callback, |
+ scoped_ptr<WebUsbAllowedOrigins> allowed_origins) { |
+ if (!allowed_origins) { |
+ callback.Run(nullptr, GURL()); |
+ return; |
} |
- end = *it + total_length; |
- std::advance(*it, length); |
+ std::set<uint8_t> to_request; |
+ if (landing_page_id != 0) |
+ to_request.insert(landing_page_id); |
- while (*it != end) { |
- uint8_t length = (*it)[0]; |
- if (length < 2 || std::distance(*it, end) < length) { |
- return false; |
+ to_request.insert(allowed_origins->origin_ids.begin(), |
+ allowed_origins->origin_ids.end()); |
+ for (auto& config : allowed_origins->configurations) { |
+ to_request.insert(config.origin_ids.begin(), config.origin_ids.end()); |
+ for (auto& function : config.functions) { |
+ to_request.insert(function.origin_ids.begin(), function.origin_ids.end()); |
} |
+ } |
- uint8_t type = (*it)[1]; |
- if (type == kFunctionSubsetDescriptorType) { |
- WebUsbFunctionSubset function; |
- if (!ParseFunction(&function, it, end)) { |
- return false; |
- } |
- configuration->functions.push_back(function); |
- } else if (type == kUrlDescriptorType) { |
- GURL url; |
- if (!ParseUrl(&url, it, end)) { |
- return false; |
- } |
- configuration->origins.push_back(url.GetOrigin()); |
- } else { |
- return false; |
- } |
+ scoped_ptr<std::map<uint8_t, GURL>> url_map(new std::map<uint8_t, GURL>()); |
+ std::map<uint8_t, GURL>* url_map_ptr = url_map.get(); |
+ base::Closure barrier = base::BarrierClosure( |
+ static_cast<int>(to_request.size()), |
+ base::Bind(&OnDoneReadingUrls, base::Passed(&allowed_origins), |
+ landing_page_id, base::Passed(&url_map), callback)); |
+ |
+ for (uint8_t index : to_request) { |
+ ReadUrlDescriptor(device_handle, vendor_code, url_map_ptr, index, barrier); |
} |
+} |
- return true; |
+void OnReadWebUsbAllowedOrigins( |
+ const ReadWebUsbAllowedOriginsCallback& callback, |
+ UsbTransferStatus status, |
+ scoped_refptr<net::IOBuffer> buffer, |
+ size_t length) { |
+ if (status != USB_TRANSFER_COMPLETED) { |
+ USB_LOG(EVENT) << "Failed to read WebUSB allowed origins."; |
+ callback.Run(nullptr); |
+ return; |
+ } |
+ |
+ scoped_ptr<WebUsbAllowedOrigins> allowed_origins(new WebUsbAllowedOrigins()); |
+ if (allowed_origins->Parse( |
+ std::vector<uint8_t>(buffer->data(), buffer->data() + length))) { |
+ callback.Run(std::move(allowed_origins)); |
+ } else { |
+ callback.Run(nullptr); |
+ } |
+} |
+ |
+void OnReadWebUsbAllowedOriginsHeader( |
+ scoped_refptr<UsbDeviceHandle> device_handle, |
+ const ReadWebUsbAllowedOriginsCallback& callback, |
+ uint8_t vendor_code, |
+ UsbTransferStatus status, |
+ scoped_refptr<net::IOBuffer> buffer, |
+ size_t length) { |
+ if (status != USB_TRANSFER_COMPLETED || length != 4) { |
+ USB_LOG(EVENT) << "Failed to read WebUSB allowed origins header."; |
+ callback.Run(nullptr); |
+ return; |
+ } |
+ |
+ uint16_t new_length = buffer->data()[2] | (buffer->data()[3] << 8); |
+ scoped_refptr<IOBufferWithSize> new_buffer = new IOBufferWithSize(new_length); |
+ device_handle->ControlTransfer( |
+ USB_DIRECTION_INBOUND, UsbDeviceHandle::VENDOR, UsbDeviceHandle::DEVICE, |
+ vendor_code, 0, kGetAllowedOriginsRequest, new_buffer, new_buffer->size(), |
+ kControlTransferTimeout, |
+ base::Bind(&OnReadWebUsbAllowedOrigins, callback)); |
+} |
+ |
+void ReadWebUsbAllowedOrigins( |
+ scoped_refptr<UsbDeviceHandle> device_handle, |
+ uint8_t vendor_code, |
+ const ReadWebUsbAllowedOriginsCallback& callback) { |
+ scoped_refptr<IOBufferWithSize> buffer = new IOBufferWithSize(4); |
+ device_handle->ControlTransfer( |
+ USB_DIRECTION_INBOUND, UsbDeviceHandle::VENDOR, UsbDeviceHandle::DEVICE, |
+ vendor_code, 0, kGetAllowedOriginsRequest, buffer, buffer->size(), |
+ kControlTransferTimeout, |
+ base::Bind(&OnReadWebUsbAllowedOriginsHeader, device_handle, callback, |
+ vendor_code)); |
+} |
+ |
+void OnReadBosDescriptor(scoped_refptr<UsbDeviceHandle> device_handle, |
+ const ReadWebUsbDescriptorsCallback& callback, |
+ UsbTransferStatus status, |
+ scoped_refptr<net::IOBuffer> buffer, |
+ size_t length) { |
+ if (status != USB_TRANSFER_COMPLETED) { |
+ USB_LOG(EVENT) << "Failed to read BOS descriptor."; |
+ callback.Run(nullptr, GURL()); |
+ return; |
+ } |
+ |
+ WebUsbPlatformCapabilityDescriptor descriptor; |
+ if (!descriptor.ParseFromBosDescriptor( |
+ std::vector<uint8_t>(buffer->data(), buffer->data() + length))) { |
+ callback.Run(nullptr, GURL()); |
+ return; |
+ } |
+ |
+ ReadWebUsbAllowedOrigins( |
+ device_handle, descriptor.vendor_code, |
+ base::Bind(&ReadUrlDescriptors, device_handle, descriptor.vendor_code, |
+ descriptor.landing_page_id, callback)); |
+} |
+ |
+void OnReadBosDescriptorHeader(scoped_refptr<UsbDeviceHandle> device_handle, |
+ const ReadWebUsbDescriptorsCallback& callback, |
+ UsbTransferStatus status, |
+ scoped_refptr<net::IOBuffer> buffer, |
+ size_t length) { |
+ if (status != USB_TRANSFER_COMPLETED || length != 5) { |
+ USB_LOG(EVENT) << "Failed to read BOS descriptor header."; |
+ callback.Run(nullptr, GURL()); |
+ return; |
+ } |
+ |
+ uint16_t new_length = buffer->data()[2] | (buffer->data()[3] << 8); |
+ scoped_refptr<IOBufferWithSize> new_buffer = new IOBufferWithSize(new_length); |
+ device_handle->ControlTransfer( |
+ USB_DIRECTION_INBOUND, UsbDeviceHandle::STANDARD, UsbDeviceHandle::DEVICE, |
+ kGetDescriptorRequest, kBosDescriptorType << 8, 0, new_buffer, |
+ new_buffer->size(), kControlTransferTimeout, |
+ base::Bind(&OnReadBosDescriptor, device_handle, callback)); |
} |
} // namespace |
@@ -165,49 +366,59 @@ WebUsbConfigurationSubset::WebUsbConfigurationSubset() |
WebUsbConfigurationSubset::~WebUsbConfigurationSubset() {} |
-WebUsbDescriptorSet::WebUsbDescriptorSet() {} |
- |
-WebUsbDescriptorSet::~WebUsbDescriptorSet() {} |
- |
-bool WebUsbDescriptorSet::Parse(const std::vector<uint8_t>& bytes) { |
- if (bytes.size() < 4) { |
+WebUsbAllowedOrigins::WebUsbAllowedOrigins() {} |
+ |
+WebUsbAllowedOrigins::~WebUsbAllowedOrigins() {} |
+ |
+// Parses a WebUSB Allowed Origins Header: |
+// http://wicg.github.io/webusb/#dfn-allowed-origins-header |
+// |
+// 0 1 2 3 |
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+// | length | type | total length | |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+// | num configs | origin[0] | origin[1] | ... |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+------ |
+bool WebUsbAllowedOrigins::Parse(const std::vector<uint8_t>& bytes) { |
+ const uint8_t kDescriptorType = 0x00; |
+ const uint8_t kDescriptorMinLength = 5; |
+ |
+ // The buffer must be at least the length of this descriptor's mandatory |
+ // fields. |
+ if (bytes.size() < kDescriptorMinLength) |
return false; |
- } |
- // Validate the descriptor set header. |
+ // Validate that the length of this descriptor and the total length of the |
+ // entire block of descriptors is consistent with the length of the buffer. |
+ uint8_t length = bytes[0]; |
uint16_t total_length = bytes[2] + (bytes[3] << 8); |
- if (bytes[0] != 4 || // bLength |
- bytes[1] != kDescriptorSetDescriptorType || // bDescriptorType |
- 4 > total_length || total_length > bytes.size()) { // wTotalLength |
+ if (length < 5 || length > bytes.size() || // bLength |
+ bytes[1] != kDescriptorType || // bDescriptorType |
+ total_length < length || total_length > bytes.size()) { // wTotalLength |
return false; |
} |
std::vector<uint8_t>::const_iterator it = bytes.begin(); |
- std::vector<uint8_t>::const_iterator end = it + total_length; |
- std::advance(it, 4); |
- |
- while (it != bytes.end()) { |
- uint8_t length = it[0]; |
- if (length < 2 || std::distance(it, end) < length) { |
+ uint8_t num_configurations = bytes[4]; |
+ |
+ // The next |length - 5| bytes after the mandatory fields are origin indicies. |
+ std::advance(it, kDescriptorMinLength); |
+ uint8_t num_origins = length - kDescriptorMinLength; |
+ origin_ids.reserve(num_origins); |
+ for (size_t i = 0; i < num_origins; ++i) { |
+ uint8_t index = *it++; |
+ if (index == 0) |
return false; |
- } |
+ origin_ids.push_back(index); |
+ } |
- uint8_t type = it[1]; |
- if (type == kConfigurationSubsetDescriptorType) { |
- WebUsbConfigurationSubset configuration; |
- if (!ParseConfiguration(&configuration, &it, end)) { |
- return false; |
- } |
- configurations.push_back(configuration); |
- } else if (type == kUrlDescriptorType) { |
- GURL url; |
- if (!ParseUrl(&url, &it, end)) { |
- return false; |
- } |
- origins.push_back(url.GetOrigin()); |
- } else { |
+ // |num_configurations| configuration descriptors then follow the descriptor. |
+ for (size_t i = 0; i < num_configurations; ++i) { |
+ WebUsbConfigurationSubset configuration; |
+ if (!ParseConfiguration(&configuration, &it, bytes.end())) |
return false; |
- } |
+ configurations.push_back(configuration); |
} |
return true; |
@@ -240,7 +451,6 @@ bool WebUsbPlatformCapabilityDescriptor::ParseFromBosDescriptor( |
std::advance(it, 5); |
uint8_t length = 0; |
- bool found_vendor_code = false; |
for (size_t i = 0; i < num_device_caps; ++i, std::advance(it, length)) { |
if (it == end) { |
return false; |
@@ -270,8 +480,8 @@ bool WebUsbPlatformCapabilityDescriptor::ParseFromBosDescriptor( |
continue; |
} |
- if (length < 23) { |
- // The WebUSB capability descriptor must be at least 23 bytes (to allow |
+ if (length < 22) { |
+ // The WebUSB capability descriptor must be at least 22 bytes (to allow |
// for future versions). |
return false; |
} |
@@ -281,24 +491,74 @@ bool WebUsbPlatformCapabilityDescriptor::ParseFromBosDescriptor( |
continue; |
} |
- // Version 1.0 only defines a single field, bVendorCode. |
+ // Version 1.0 defines two fields for a total length of 24 bytes. |
+ if (length != 24) { |
+ return false; |
+ } |
+ |
vendor_code = it[22]; |
- found_vendor_code = true; |
+ landing_page_id = it[23]; |
+ return true; |
} |
- return found_vendor_code; |
+ return false; |
} |
+// Parses a WebUSB URL Descriptor: |
+// http://wicg.github.io/webusb/#dfn-url-descriptor |
+// |
+// 0 1 2 3 |
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+// | length | type | prefix | data[0] | |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
+// | data[1] | ... |
+// +-+-+-+-+-+-+-+-+-+-+-+------ |
bool ParseWebUsbUrlDescriptor(const std::vector<uint8_t>& bytes, GURL* output) { |
- if (bytes.size() < 2) { |
+ const uint8_t kDescriptorType = 0x03; |
+ const uint8_t kDescriptorMinLength = 3; |
+ |
+ if (bytes.size() < kDescriptorMinLength) { |
return false; |
} |
+ |
+ // Validate that the length is consistent and fits within the buffer. |
uint8_t length = bytes[0]; |
- if (length != bytes.size() || bytes[1] != kUrlDescriptorType) { |
+ if (length < kDescriptorMinLength || length < bytes.size() || |
+ bytes[1] != kDescriptorType) { |
return false; |
} |
- std::vector<uint8_t>::const_iterator it = bytes.begin(); |
- return ParseUrl(output, &it, bytes.end()); |
+ |
+ // Look up the URL prefix and append the rest of the data in the descriptor. |
+ std::string url; |
+ switch (bytes[2]) { |
+ case 0: |
+ url.append("http://"); |
+ break; |
+ case 1: |
+ url.append("https://"); |
+ break; |
+ default: |
+ return false; |
+ } |
+ url.append(reinterpret_cast<const char*>(bytes.data() + 3), length - 3); |
+ |
+ *output = GURL(url); |
+ if (!output->is_valid()) { |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+void ReadWebUsbDescriptors(scoped_refptr<UsbDeviceHandle> device_handle, |
+ const ReadWebUsbDescriptorsCallback& callback) { |
+ scoped_refptr<IOBufferWithSize> buffer = new IOBufferWithSize(5); |
+ device_handle->ControlTransfer( |
+ USB_DIRECTION_INBOUND, UsbDeviceHandle::STANDARD, UsbDeviceHandle::DEVICE, |
+ kGetDescriptorRequest, kBosDescriptorType << 8, 0, buffer, buffer->size(), |
+ kControlTransferTimeout, |
+ base::Bind(&OnReadBosDescriptorHeader, device_handle, callback)); |
} |
} // namespace device |