Repository: electron/electron
Pull request: feat: support complete WebUSB device properties

#### Description of Change

Similar vein as https://github.com/electron/electron/pull/47391 - we should expose the full property set of [`USBDevice`] per spec.

#### Checklist
<!-- Remove items that do not apply. For completed items, change [ ] to [x]. -->

- [x] PR description included and stakeholders cc'd
- [x] `npm test` passes
- [x] relevant API documentation, tutorials, and examples are updated and follow the [documentation style guide](https://github.com/electron/electron/blob/main/docs/development/style-guide.md)
- [x] [PR release notes](https://github.com/electron/clerk/blob/main/README.md) describe the change in a way relevant to app developers, and are [capitalized, punctuated, and past tense](https://github.com/electron/clerk/blob/main/README.md#examples).

#### Release Notes

Notes: Added support for `USBDevice.configurations`.


Changed files:
filename: docs/api/structures/usb-device.md
patch: @@ -1,17 +1,35 @@
 # USBDevice Object
 
+* `configuration` Object (optional) - A [USBConfiguration](https://developer.mozilla.org/en-US/docs/Web/API/USBConfiguration) object containing information about the currently selected configuration of a USB device.
+  * `configurationValue` Integer - the configuration value of this configuration.
+  * `configurationName` string - the name provided by the device to describe this configuration.
+  * `interfaces` Object[] - An array of [USBInterface](https://developer.mozilla.org/en-US/docs/Web/API/USBInterface) objects containing information about an interface provided by the USB device.
+    * `interfaceNumber` Integer - the interface number of this interface.
+    * `alternate` Object - the currently selected alternative configuration of this interface.
+      * `alternateSetting` Integer - the alternate setting number of this interface.
+      * `interfaceClass` Integer - the class of this interface. See [USB.org](https://www.usb.org/defined-class-codes) for class code descriptions.
+      * `interfaceSubclass` Integer - the subclass of this interface.
+      * `interfaceProtocol` Integer - the protocol supported by this interface.
+      * `interfaceName` string (optional) - the name of the interface, if one is provided by the device.
+      * `endpoints` Object[] - an array containing instances of the [USBEndpoint interface](https://developer.mozilla.org/en-US/docs/Web/API/USBEndpoint) describing each of the endpoints that are part of this interface.
+        * `endpointNumber` Integer - this endpoint's "endpoint number" which is a value from 1 to 15.
+        * `direction` string - the direction in which this endpoint transfers data - can be either 'in' or 'out'.
+        * `type` string - the type of this endpoint - can be either 'bulk', 'interrupt', or 'isochronous'.
+        * `packetSize` Integer - the size of the packets that data sent through this endpoint will be divided into.
+    * `alternates` Object[] - an array containing instances of the [USBAlternateInterface](https://developer.mozilla.org/en-US/docs/Web/API/USBAlternateInterface) interface describing each of the alternative configurations possible for this interface.
+* `configurations` Object[] - An array of [USBConfiguration](https://developer.mozilla.org/en-US/docs/Web/API/USBConfiguration) interfaces for controlling a paired USB device.
+* `deviceClass` Integer - The device class for the communication interface supported by the device.
 * `deviceId` string - Unique identifier for the device.
-* `vendorId` Integer - The USB vendor ID.
-* `productId` Integer - The USB product ID.
-* `productName` string (optional) - Name of the device.
-* `serialNumber` string (optional) - The USB device serial number.
-* `manufacturerName` string (optional) - The manufacturer name of the device.
-* `usbVersionMajor` Integer - The USB protocol major version supported by the device
-* `usbVersionMinor` Integer - The USB protocol minor version supported by the device
-* `usbVersionSubminor` Integer - The USB protocol subminor version supported by the device
-* `deviceClass` Integer - The device class for the communication interface supported by the device
-* `deviceSubclass` Integer - The device subclass for the communication interface supported by the device
-* `deviceProtocol` Integer - The device protocol for the communication interface supported by the device
+* `deviceProtocol` Integer - The device protocol for the communication interface supported by the device.
+* `deviceSubclass` Integer - The device subclass for the communication interface supported by the device.
 * `deviceVersionMajor` Integer - The major version number of the device as defined by the device manufacturer.
 * `deviceVersionMinor` Integer - The minor version number of the device as defined by the device manufacturer.
 * `deviceVersionSubminor` Integer - The subminor version number of the device as defined by the device manufacturer.
+* `manufacturerName` string (optional) - The manufacturer name of the device.
+* `productId` Integer - The USB product ID.
+* `productName` string (optional) - Name of the device.
+* `serialNumber` string (optional) - The USB device serial number.
+* `usbVersionMajor` Integer - The USB protocol major version supported by the device.
+* `usbVersionMinor` Integer - The USB protocol minor version supported by the device.
+* `usbVersionSubminor` Integer - The USB protocol subminor version supported by the device.
+* `vendorId` Integer - The USB vendor ID.

filename: shell/browser/usb/usb_chooser_context.cc
patch: @@ -98,6 +98,98 @@ base::Value UsbChooserContext::DeviceInfoToValue(
   device_value.Set("deviceVersionMinor", device_info.device_version_minor);
   device_value.Set("deviceVersionSubminor",
                    device_info.device_version_subminor);
+
+  bool has_active_configuration = false;
+  base::Value::List configuration_list;
+  for (const auto& configuration : device_info.configurations) {
+    base::Value::Dict configuration_value;
+    configuration_value.Set("configurationValue",
+                            configuration->configuration_value);
+    configuration_value.Set("configurationName",
+                            configuration->configuration_name
+                                ? *configuration->configuration_name
+                                : std::u16string_view());
+
+    for (const auto& interface : configuration->interfaces) {
+      base::Value::Dict interface_value;
+      interface_value.Set("interfaceNumber", interface->interface_number);
+
+      base::Value::List alternate_list;
+      for (const auto& alternate : interface->alternates) {
+        base::Value::Dict alternate_value;
+        alternate_value.Set("alternateSetting", alternate->alternate_setting);
+        alternate_value.Set("interfaceClass", alternate->class_code);
+        alternate_value.Set("interfaceSubclass", alternate->subclass_code);
+        alternate_value.Set("interfaceProtocol", alternate->protocol_code);
+        alternate_value.Set("interfaceName", alternate->interface_name
+                                                 ? *alternate->interface_name
+                                                 : std::u16string_view());
+
+        base::Value::List endpoint_list;
+        for (const auto& endpoint : alternate->endpoints) {
+          base::Value::Dict endpoint_value;
+          endpoint_value.Set("endpointNumber", endpoint->endpoint_number);
+
+          bool inbound = endpoint->direction ==
+                         device::mojom::UsbTransferDirection::INBOUND;
+          endpoint_value.Set("direction", inbound ? "in" : "out");
+
+          std::string type;
+          switch (endpoint->type) {
+            case device::mojom::UsbTransferType::ISOCHRONOUS:
+              type = "isochronous";
+              break;
+            case device::mojom::UsbTransferType::BULK:
+              type = "bulk";
+              break;
+            case device::mojom::UsbTransferType::INTERRUPT:
+              type = "interrupt";
+              break;
+            default:
+              NOTREACHED() << "Unknown USB transfer type: "
+                           << static_cast<int>(endpoint->type);
+          }
+          endpoint_value.Set("type", type);
+          endpoint_value.Set("packetSize",
+                             static_cast<int>(endpoint->packet_size));
+          endpoint_list.Append(std::move(endpoint_value));
+        }
+
+        alternate_value.Set("endpoints", base::Value(std::move(endpoint_list)));
+
+        if (alternate->alternate_setting == 0) {
+          auto active_alternate_value = alternate_value.Clone();
+          interface_value.Set("alternate", std::move(active_alternate_value));
+        }
+
+        alternate_list.Append(std::move(alternate_value));
+      }
+
+      interface_value.Set("alternates", std::move(alternate_list));
+
+      configuration_value.Set("interfaces",
+                              base::Value(std::move(interface_value)));
+    }
+
+    if (device_info.active_configuration &&
+        device_info.active_configuration ==
+            configuration->configuration_value) {
+      auto active_configuration_value = configuration_value.Clone();
+      has_active_configuration = true;
+      configuration_value.Set("configuration",
+                              std::move(active_configuration_value));
+    }
+
+    configuration_list.Append(std::move(configuration_value));
+  }
+
+  device_value.Set("configurations", std::move(configuration_list));
+
+  // Set value for "configuration" to null if no active configuration.
+  if (!has_active_configuration) {
+    device_value.Set("configuration", base::Value());
+  }
+
   return base::Value(std::move(device_value));
 }