USB chooser thing aaa

This commit is contained in:
wuggy 2026-09-24 10:52:09 -07:00
commit 99049da066
6 changed files with 172 additions and 15 deletions

View file

@ -0,0 +1,29 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#usb-device-picker {
min-width: 360px;
min-height: 220px;
}
#picker {
padding: 16px;
}
#message {
margin: 0 0 10px;
}
#devices {
min-height: 100px;
margin: 0 0 12px;
}
#buttons {
margin-inline-start: 8px;
}
#buttons > button + button {
margin-inline-start: 8px;
}

View file

@ -0,0 +1,50 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
var USBDevicePicker = {
args: null,
init() {
this.args = window.arguments[0];
document.title = this.args.title;
document.getElementById("message").value = this.args.message;
document.getElementById("select").label = this.args.selectLabel;
document.getElementById("cancel").label = this.args.cancelLabel;
let list = document.getElementById("devices");
for (let choice of this.args.choices) {
list.appendItem(choice, choice);
}
list.selectedIndex = 0;
list.focus();
},
select() {
let list = document.getElementById("devices");
this.args.prompt.select(list.selectedIndex);
},
cancel() {
this.args.prompt.cancel();
},
destroy() {
if (this.args && !this.args.prompt.completed) {
// The window manager can close this window directly. Detach it first
// so cancel() does not try to close an already-unloading window.
this.args.prompt.window = null;
this.args.prompt.cancel();
}
this.args = null;
},
};
window.addEventListener("keydown", event => {
if (event.key == "Escape") {
USBDevicePicker.cancel();
} else if (event.key == "Enter") {
USBDevicePicker.select();
}
});

View file

@ -0,0 +1,22 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/usbDevicePicker.css" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
id="usb-device-picker" windowtype="Browser:USBDevicePicker"
width="420" height="260" onload="USBDevicePicker.init();"
onunload="USBDevicePicker.destroy();">
<script type="application/javascript" src="chrome://browser/content/usbDevicePicker.js"/>
<vbox id="picker" flex="1">
<label id="message"/>
<listbox id="devices" flex="1" rows="5"
ondblclick="USBDevicePicker.select();"/>
<hbox id="buttons" pack="end">
<button id="cancel" label="Cancel" oncommand="USBDevicePicker.cancel();"/>
<button id="select" label="Select" default="true"
oncommand="USBDevicePicker.select();"/>
</hbox>
</vbox>
</window>

View file

@ -6,6 +6,9 @@ browser.jar:
content/browser/pictureInPicture.js (content/pictureInPicture.js) content/browser/pictureInPicture.js (content/pictureInPicture.js)
content/browser/pictureInPicture.css (content/pictureInPicture.css) content/browser/pictureInPicture.css (content/pictureInPicture.css)
content/browser/pictureInPictureContent.js (content/pictureInPictureContent.js) content/browser/pictureInPictureContent.js (content/pictureInPictureContent.js)
content/browser/usbDevicePicker.xul (content/usbDevicePicker.xul)
content/browser/usbDevicePicker.js (content/usbDevicePicker.js)
content/browser/usbDevicePicker.css (content/usbDevicePicker.css)
% content browser %content/browser/ contentaccessible=yes % content browser %content/browser/ contentaccessible=yes
#ifdef XP_MACOSX #ifdef XP_MACOSX
% overlay chrome://mozapps/content/downloads/downloads.xul chrome://browser/content/downloadManagerOverlay.xul % overlay chrome://mozapps/content/downloads/downloads.xul chrome://browser/content/downloadManagerOverlay.xul

View file

@ -370,6 +370,8 @@ geolocation.shareWithFile2=Would you like to share your location with this file?
webUSB.connectToSite=Would you like to let this site access USB devices? webUSB.connectToSite=Would you like to let this site access USB devices?
webUSB.selectDeviceTitle=Select USB device webUSB.selectDeviceTitle=Select USB device
webUSB.selectDevice=Choose a USB device to access: webUSB.selectDevice=Choose a USB device to access:
webUSB.selectDeviceButton=Select
webUSB.cancel=Cancel
webUSB.allow=Allow USB Devices webUSB.allow=Allow USB Devices
webUSB.allow.accesskey=A webUSB.allow.accesskey=A
webUSB.block=Block USB Devices webUSB.block=Block USB Devices

View file

@ -486,6 +486,8 @@ function getUSBString(name, fallback) {
function USBPermissionPrompt(request) { function USBPermissionPrompt(request) {
this.request = request; this.request = request;
this.window = null;
this.completed = false;
} }
function getUSBDeviceChoices(request) { function getUSBDeviceChoices(request) {
@ -551,10 +553,14 @@ USBPermissionPrompt.prototype = {
}, },
// PopupNotifications in older UXP builds cannot reliably render a // PopupNotifications in older UXP builds cannot reliably render a
// permission request that contains a variable list of USB devices. Use // permission request that contains a variable list of USB devices. Use a
// the native prompt service so the chooser is always visible and the // dedicated XUL window so the chooser stays visible above the browser.
// selected device can be returned to the content request.
prompt() { prompt() {
if (this.window && !this.window.closed) {
this.window.focus();
return;
}
let choices = getUSBDeviceChoices(this.request); let choices = getUSBDeviceChoices(this.request);
if (!choices.length) { if (!choices.length) {
this.cancel(); this.cancel();
@ -563,19 +569,64 @@ USBPermissionPrompt.prototype = {
let browser = this.browser; let browser = this.browser;
let parent = browser && browser.ownerGlobal ? browser.ownerGlobal : null; let parent = browser && browser.ownerGlobal ? browser.ownerGlobal : null;
let selected = { value: 0 }; if (!parent) {
let accepted = Services.prompt.select(
parent,
getUSBString("webUSB.selectDeviceTitle", "Select USB device"),
getUSBString("webUSB.selectDevice", "Choose a USB device to access:"),
choices.length,
choices,
selected);
if (accepted && selected.value >= 0 && selected.value < choices.length) {
this.request.allow({usb: choices[selected.value]});
} else {
this.cancel(); this.cancel();
return;
}
try {
this.window = parent.openDialog(
"chrome://browser/content/usbDevicePicker.xul", "",
"chrome,dialog=no,dependent,alwaysRaised,resizable=no,width=420,height=260",
{
prompt: this,
choices,
title: getUSBString("webUSB.selectDeviceTitle", "Select USB device"),
message: getUSBString("webUSB.selectDevice",
"Choose a USB device to access:"),
selectLabel: getUSBString("webUSB.selectDeviceButton", "Select"),
cancelLabel: getUSBString("webUSB.cancel", "Cancel"),
});
} catch (ex) {
Cu.reportError("Unable to open USB device picker: " + ex);
this.cancel();
}
},
select(index) {
if (this.completed) {
return;
}
let choices = getUSBDeviceChoices(this.request);
if (index < 0 || index >= choices.length) {
this.cancel();
return;
}
this.completed = true;
try {
this.request.allow({usb: choices[index]});
} finally {
this.closeWindow();
}
},
cancel() {
if (this.completed) {
return;
}
this.completed = true;
try {
this.request.cancel();
} finally {
this.closeWindow();
}
},
closeWindow() {
let window = this.window;
this.window = null;
if (window && !window.closed) {
window.close();
} }
}, },