From 3ab65a542de50c9c5269b72f0d62f74c01f21983 Mon Sep 17 00:00:00 2001 From: wuggy Date: Sat, 19 Sep 2026 14:45:05 -0700 Subject: [PATCH] USBPermissionRequest (WebUSB pt.5) --- browser/modules/PermissionUI.jsm | 45 +++++++++++++++++++++++--------- dom/usb/USB.cpp | 40 +++++++++++++++++++++++++--- 2 files changed, 69 insertions(+), 16 deletions(-) diff --git a/browser/modules/PermissionUI.jsm b/browser/modules/PermissionUI.jsm index 9d99d19227..bd94dfd24d 100644 --- a/browser/modules/PermissionUI.jsm +++ b/browser/modules/PermissionUI.jsm @@ -488,13 +488,28 @@ function USBPermissionPrompt(request) { this.request = request; } +function getUSBDeviceChoices(request) { + let choices = []; + let types = request.types.QueryInterface(Ci.nsIArray); + if (!types.length) { + return choices; + } + + let type = types.queryElementAt(0, Ci.nsIContentPermissionType); + let options = type.options; + if (!options) { + return choices; + } + + for (let i = 0; i < options.length; ++i) { + choices.push(options.queryElementAt(i, Ci.nsISupportsString).data); + } + return choices; +} + USBPermissionPrompt.prototype = { __proto__: PermissionPromptForRequestPrototype, - get permissionKey() { - return "usb"; - }, - get notificationID() { return "web-usb"; }, @@ -509,17 +524,21 @@ USBPermissionPrompt.prototype = { }, get promptActions() { - return [{ - label: getUSBString("webUSB.allow", "Allow USB Devices"), - accessKey: getUSBString("webUSB.allow.accesskey", "A"), - action: Ci.nsIPermissionManager.ALLOW_ACTION, - expireType: Ci.nsIPermissionManager.EXPIRE_SESSION, - }, { + let actions = []; + for (let choice of getUSBDeviceChoices(this.request)) { + actions.push({ + label: choice, + accessKey: "", + callback: () => this.request.allow({usb: choice}), + }); + } + + actions.push({ label: getUSBString("webUSB.block", "Block USB Devices"), accessKey: getUSBString("webUSB.block.accesskey", "B"), - action: Ci.nsIPermissionManager.DENY_ACTION, - expireType: Ci.nsIPermissionManager.EXPIRE_SESSION, - }]; + callback: () => this.cancel(), + }); + return actions; }, }; diff --git a/dom/usb/USB.cpp b/dom/usb/USB.cpp index 658b85cc81..842e8d5acf 100644 --- a/dom/usb/USB.cpp +++ b/dom/usb/USB.cpp @@ -5,7 +5,10 @@ #include "mozilla/dom/USBBinding.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/USBDevice.h" +#include "mozilla/dom/ScriptSettings.h" +#include "jsapi.h" #include "nsContentPermissionHelper.h" +#include "nsContentUtils.h" #include "nsIDocument.h" #include "nsError.h" #include "nsPIDOMWindow.h" @@ -177,15 +180,46 @@ USBPermissionRequest::Cancel() NS_IMETHODIMP USBPermissionRequest::Allow(JS::HandleValue aChoices) { - (void)aChoices; if (mCandidates.IsEmpty()) { mPromise->MaybeReject(NS_ERROR_DOM_NOT_FOUND_ERR); return NS_OK; } - RefPtr device = mCandidates[0]; + uint32_t selected = 0; + if (aChoices.isObject()) { + JSContext* cx = nsContentUtils::GetCurrentJSContext(); + if (cx) { + JS::RootedObject object(cx, &aChoices.toObject()); + JSAutoCompartment ac(cx, object); + JS::RootedValue value(cx); + if (JS_GetProperty(cx, object, "usb", &value) && value.isString()) { + nsAutoJSString choice; + if (choice.init(cx, value)) { + for (uint32_t i = 0; i < mCandidates.Length(); ++i) { + DOMString label; + mCandidates[i]->GetProductName(label); + nsAutoString labelString; + label.ToString(labelString); + if ((label.IsNull() || labelString.IsEmpty()) && + choice.EqualsLiteral("USB device")) { + selected = i; + break; + } + if (!label.IsNull() && labelString.Equals(choice)) { + selected = i; + break; + } + } + } + } else { + JS_ClearPendingException(cx); + } + } + } + + RefPtr device = mCandidates[selected]; mUSB->AddAuthorizedDevice(device); - mPromise->MaybeResolve(device); + mPromise->MaybeResolve(device.get()); return NS_OK; }