<!DOCTYPEHTML>
<html>
<head>
<meta charset="utf-8">
<title>Test Web Serial API - Device Hotplug</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="serial_test_utils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script>
/* global SerialPort */ "use strict";
SimpleTest.waitForExplicitFinish();
// Cleanup function specific to hotplug tests
async function removeAllDevices() {
await cleanupSerialPorts();
await navigator.serial.removeAllMockDevices();
}
// Restore default mock devices after all hotplug tests complete
SimpleTest.registerCleanupFunction(async function restoreDefaultDevices() {
await cleanupSerialPorts();
await navigator.serial.resetToDefaultMockDevices();
});
async function waitForSerialConnect(operation) {
let eventProperties = null;
SpecialPowers.wrap(document).notifyUserGestureActivation();
const port = await navigator.serial.requestPort({ filters: [] });
ok(port, "Should be able to request the simulated port");
// Verify the granted port info before disconnecting
const portInfo = port.getInfo();
is(portInfo.usbVendorId, 0xABCD, "check vendorId for connected device");
is(portInfo.usbProductId, 0xEF01, "check productId for connected device");
let ports = await navigator.serial.getPorts();
let foundPort = ports.some(p => {
const info = p.getInfo();
return info.usbVendorId === 0xABCD && info.usbProductId === 0xEF01;
});
ok(foundPort, "getPorts() should return connected port");
// Now disconnect the device (permission is still granted)
info('disconnecting test-hotplug-2');
await waitForSerialDisconnect(() => simulateDeviceDisconnection("test-hotplug-2"));
await port.open({ baudRate: 9600 });
ok(port.connected, "Port should be connected");
await waitForPortDisconnect(port, () => simulateDeviceDisconnection("test-hotplug-3"));
ok(!port.connected, "Port should be disconnected");
});
add_task(async function testDisconnectAfterClose() {
await removeAllDevices();
await simulateDeviceConnection("test-close-disconnect-1", "/dev/ttyUSB95", 0xFEDC, 0xBA98);
SpecialPowers.wrap(document).notifyUserGestureActivation();
const port = await navigator.serial.requestPort({ filters: [] });
ok(port, "Should be able to request the simulated port");
const portInfo = port.getInfo();
is(portInfo.usbVendorId, 0xFEDC, "Vendor ID should match");
is(portInfo.usbProductId, 0xBA98, "Product ID should match");
await port.open({ baudRate: 9600 });
ok(port.connected, "Port should be connected after opening");
await port.close();
ok(port.connected, "Port should still report connected after close (device still physically present)");
await waitForSerialDisconnect(() => simulateDeviceDisconnection("test-close-disconnect-1"));
ok(!port.connected, "Port should report disconnected after physical disconnection");
});
add_task(async function testDisconnectAfterCloseWithPortListener() {
await removeAllDevices();
SpecialPowers.wrap(document).notifyUserGestureActivation();
const port = await navigator.serial.requestPort({ filters: [] });
ok(port, "Should be able to request the simulated port");
await port.open({ baudRate: 115200 });
ok(port.connected, "Port should be connected");
await port.close();
ok(port.connected, "Port should still be connected (device physically present)");
await waitForPortDisconnect(port, () => simulateDeviceDisconnection("test-close-disconnect-2"));
ok(!port.connected, "Port should be disconnected");
});
add_task(async function testDeviceReconnectionFiresEventOnPort() {
await removeAllDevices();
await simulateDeviceConnection("test-hotplug-3", "/dev/ttyUSB97", 0x9999, 0x8888);
SpecialPowers.wrap(document).notifyUserGestureActivation();
const port = await navigator.serial.requestPort({ filters: [] });
ok(port.connected, "Port should be connected");
await waitForPortDisconnect(port, () => simulateDeviceDisconnection("test-hotplug-3"));
ok(!port.connected, "Port should be disconnected");
await waitForPortConnect(port, () => simulateDeviceConnection("test-hotplug-3", "/dev/ttyUSB97", 0x9999, 0x8888));
ok(port.connected, "Port should be reconnected");
});
add_task(async function testMultipleDeviceChanges() {
await removeAllDevices();
// Reconnect device multiple times
await waitForSerialConnect(() => simulateDeviceConnection("hotplug-device", "/dev/ttyUSB96", 0x1111, 0x2222));
info("Received first reconnect");
// Disconnect and reconnect again
await waitForSerialDisconnect(() => simulateDeviceDisconnection("hotplug-device"));
info("Received second disconnect");
await waitForSerialConnect(() => simulateDeviceConnection("hotplug-device", "/dev/ttyUSB96", 0x1111, 0x2222));
info("Received second reconnect");
// Verify port is in getPorts()
const ports = await navigator.serial.getPorts();
const foundDevice = ports.some(p => {
const info = p.getInfo();
return info.usbVendorId === 0x1111 && info.usbProductId === 0x2222;
});
ok(foundDevice, "Device should be present in getPorts()");
});
add_task(async function testConnectEventProperties() {
await removeAllDevices();
// Reconnect device - event should fire now
const eventProperties = await waitForSerialConnect(() => simulateDeviceConnection("test-event-props", "/dev/ttyUSB93", 0xAAAA, 0xBBBB));
ok(eventProperties, "Event properties should be captured");
is(eventProperties.type, "connect", "Event type should be 'connect'");
ok(eventProperties.target, "Event should have a target");
ok(eventProperties.target instanceof SerialPort, "Event target should be a SerialPort");
is(eventProperties.currentTarget, navigator.serial, "Event currentTarget should be navigator.serial");
ok(eventProperties.bubbles, "Event should bubble");
ok(!eventProperties.cancelable, "Event should not be cancelable");
});
add_task(async function testDisconnectEventProperties() {
await removeAllDevices();
// Connect device and grant permission
await simulateDeviceConnection("test-disconnect-props", "/dev/ttyUSB92", 0xCCCC, 0xDDDD);
SpecialPowers.wrap(document).notifyUserGestureActivation();
await navigator.serial.requestPort({ filters: [] });
// Disconnect device - event should fire because we have permission
const eventProperties = await waitForSerialDisconnect(() => simulateDeviceDisconnection("test-disconnect-props"));
ok(eventProperties, "Event properties should be captured");
is(eventProperties.type, "disconnect", "Event type should be 'disconnect'");
ok(eventProperties.target, "Event should have a target");
ok(eventProperties.target instanceof SerialPort, "Event target should be a SerialPort");
is(eventProperties.currentTarget, navigator.serial, "Event currentTarget should be navigator.serial");
ok(eventProperties.bubbles, "Event should bubble");
ok(!eventProperties.cancelable, "Event should not be cancelable");
});
add_task(async function testEventBubblesFromPortToSerial() {
await removeAllDevices();
// Verify the event fired on the port
ok(portEvent, "connect event should fire on port");
is(portEvent.type, "connect", "Event type on port should be 'connect'");
is(portEvent.target, port, "Event target on port should be the port itself");
is(portEvent.currentTarget, port, "Event currentTarget on port should be the port");
is(portEvent.eventPhase, Event.AT_TARGET, "Event on port should be AT_TARGET phase");
// Verify the event bubbled to navigator.serial
ok(serialEvent, "connect event should bubble to navigator.serial");
is(serialEvent.type, "connect", "Event type on serial should be 'connect'");
is(serialEvent.target, port, "Event target on serial should still be the port");
is(serialEvent.currentTarget, navigator.serial, "Event currentTarget on serial should be navigator.serial");
is(serialEvent.eventPhase, Event.BUBBLING_PHASE, "Event on serial should be BUBBLING_PHASE");
}
{
// Verify disconnect also bubbles
let portEvent = null;
let serialEvent = null;
ok(portEvent, "disconnect event should fire on port");
is(portEvent.type, "disconnect", "Event type on port should be 'disconnect'");
is(portEvent.target, port, "Disconnect target on port should be the port");
is(portEvent.currentTarget, port, "Disconnect currentTarget on port should be the port");
is(portEvent.eventPhase, Event.AT_TARGET, "Disconnect on port should be AT_TARGET phase");
ok(serialEvent, "disconnect event should bubble to navigator.serial");
is(serialEvent.type, "disconnect", "Event type on serial should be 'disconnect'");
is(serialEvent.target, port, "Disconnect target on serial should be the port");
is(serialEvent.currentTarget, navigator.serial, "Disconnect currentTarget should be navigator.serial");
is(serialEvent.eventPhase, Event.BUBBLING_PHASE, "Disconnect on serial should be BUBBLING_PHASE");
}
});
add_task(async function testMultipleOpenCloseDisconnect() {
await removeAllDevices();
SpecialPowers.wrap(document).notifyUserGestureActivation();
const port = await navigator.serial.requestPort({ filters: [] });
await port.open({ baudRate: 9600 });
ok(port.connected, "Port should be connected after first open");
await port.close();
ok(port.connected, "Port should still be connected after first close");
await port.open({ baudRate: 115200 });
ok(port.connected, "Port should be connected after second open");
await port.close();
ok(port.connected, "Port should still be connected after second close");
await waitForPortDisconnect(port, () => simulateDeviceDisconnection("test-multi-cycle"));
ok(!port.connected, "Port should be disconnected after physical removal");
});
add_task(async function testDisconnectWithoutOpening() {
await removeAllDevices();
SpecialPowers.wrap(document).notifyUserGestureActivation();
const port = await navigator.serial.requestPort({ filters: [] });
ok(port, "Should be able to request the simulated port");
ok(port.connected, "Port should be connected even without opening");
await waitForPortDisconnect(port, () => simulateDeviceDisconnection("test-no-open"));
ok(!port.connected, "Port should be disconnected");
});
</script>
</body>
</html>
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.21 Sekunden
(vorverarbeitet am 2026-08-25)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.