mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-28 05:07:35 +00:00
LibWeb: Stub WebSerial API
This commit is contained in:
parent
34ec33d71c
commit
1be31c103f
Notes:
github-actions[bot]
2025-08-08 08:24:30 +00:00
Author: https://github.com/EdwinHoksberg
Commit: 1be31c103f
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/5740
Reviewed-by: https://github.com/gmta ✅
19 changed files with 715 additions and 0 deletions
|
@ -359,6 +359,8 @@ set(SOURCES
|
|||
Gamepad/Gamepad.cpp
|
||||
Gamepad/NavigatorGamepad.cpp
|
||||
Geolocation/Geolocation.cpp
|
||||
Serial/Serial.cpp
|
||||
Serial/SerialPort.cpp
|
||||
Geolocation/GeolocationCoordinates.cpp
|
||||
Geolocation/GeolocationPosition.cpp
|
||||
Geolocation/GeolocationPositionError.cpp
|
||||
|
|
|
@ -944,6 +944,20 @@ class Selection;
|
|||
|
||||
}
|
||||
|
||||
namespace Web::Serial {
|
||||
|
||||
class Serial;
|
||||
class SerialPort;
|
||||
|
||||
struct SerialPortFilter;
|
||||
struct SerialPortRequestOptions;
|
||||
struct SerialOptions;
|
||||
struct SerialOutputSignals;
|
||||
struct SerialInputSignals;
|
||||
struct SerialPortInfo;
|
||||
|
||||
}
|
||||
|
||||
namespace Web::ServiceWorker {
|
||||
|
||||
class ServiceWorker;
|
||||
|
|
|
@ -43,6 +43,7 @@ namespace Web::HTML::EventNames {
|
|||
__ENUMERATE_HTML_EVENT(cuechange) \
|
||||
__ENUMERATE_HTML_EVENT(currententrychange) \
|
||||
__ENUMERATE_HTML_EVENT(cut) \
|
||||
__ENUMERATE_HTML_EVENT(disconnect) \
|
||||
__ENUMERATE_HTML_EVENT(dispose) \
|
||||
__ENUMERATE_HTML_EVENT(DOMContentLoaded) \
|
||||
__ENUMERATE_HTML_EVENT(drag) \
|
||||
|
|
|
@ -69,6 +69,7 @@ void Navigator::visit_edges(Cell::Visitor& visitor)
|
|||
visitor.visit(m_plugin_array);
|
||||
visitor.visit(m_clipboard);
|
||||
visitor.visit(m_geolocation);
|
||||
visitor.visit(m_serial);
|
||||
visitor.visit(m_user_activation);
|
||||
visitor.visit(m_service_worker_container);
|
||||
visitor.visit(m_media_capabilities);
|
||||
|
@ -103,6 +104,13 @@ GC::Ref<Geolocation::Geolocation> Navigator::geolocation()
|
|||
return *m_geolocation;
|
||||
}
|
||||
|
||||
GC::Ref<Serial::Serial> Navigator::serial()
|
||||
{
|
||||
if (!m_serial)
|
||||
m_serial = realm().create<Serial::Serial>(realm());
|
||||
return *m_serial;
|
||||
}
|
||||
|
||||
GC::Ref<UserActivation> Navigator::user_activation()
|
||||
{
|
||||
if (!m_user_activation)
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <LibWeb/HTML/PluginArray.h>
|
||||
#include <LibWeb/HTML/UserActivation.h>
|
||||
#include <LibWeb/MediaCapabilitiesAPI/MediaCapabilities.h>
|
||||
#include <LibWeb/Serial/Serial.h>
|
||||
#include <LibWeb/StorageAPI/NavigatorStorage.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
@ -56,6 +57,7 @@ public:
|
|||
[[nodiscard]] GC::Ref<PluginArray> plugins();
|
||||
[[nodiscard]] GC::Ref<Clipboard::Clipboard> clipboard();
|
||||
[[nodiscard]] GC::Ref<Geolocation::Geolocation> geolocation();
|
||||
[[nodiscard]] GC::Ref<Serial::Serial> serial();
|
||||
[[nodiscard]] GC::Ref<UserActivation> user_activation();
|
||||
[[nodiscard]] GC::Ref<CredentialManagement::CredentialsContainer> credentials();
|
||||
|
||||
|
@ -89,6 +91,9 @@ private:
|
|||
// https://w3c.github.io/geolocation/#navigator_interface
|
||||
GC::Ptr<Geolocation::Geolocation> m_geolocation;
|
||||
|
||||
// https://wicg.github.io/serial/#extensions-to-the-navigator-interface
|
||||
GC::Ptr<Serial::Serial> m_serial;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/interaction.html#dom-navigator-useractivation
|
||||
GC::Ptr<UserActivation> m_user_activation;
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#import <HTML/PluginArray.idl>
|
||||
#import <HTML/UserActivation.idl>
|
||||
#import <MediaCapabilitiesAPI/MediaCapabilities.idl>
|
||||
#import <Serial/Serial.idl>
|
||||
#import <ServiceWorker/ServiceWorkerContainer.idl>
|
||||
#import <StorageAPI/NavigatorStorage.idl>
|
||||
|
||||
|
@ -29,6 +30,9 @@ interface Navigator {
|
|||
// https://w3c.github.io/pointerevents/#extensions-to-the-navigator-interface
|
||||
readonly attribute long maxTouchPoints;
|
||||
|
||||
// https://wicg.github.io/serial/#extensions-to-the-navigator-interface
|
||||
[SameObject] readonly attribute Serial serial;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/interaction.html#useractivation
|
||||
[SameObject] readonly attribute UserActivation userActivation;
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ void WorkerNavigator::visit_edges(Cell::Visitor& visitor)
|
|||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_media_capabilities);
|
||||
visitor.visit(m_serial);
|
||||
visitor.visit(m_service_worker_container);
|
||||
}
|
||||
|
||||
|
@ -47,6 +48,13 @@ GC::Ref<MediaCapabilitiesAPI::MediaCapabilities> WorkerNavigator::media_capabili
|
|||
return *m_media_capabilities;
|
||||
}
|
||||
|
||||
GC::Ref<Serial::Serial> WorkerNavigator::serial()
|
||||
{
|
||||
if (!m_serial)
|
||||
m_serial = realm().create<Serial::Serial>(realm());
|
||||
return *m_serial;
|
||||
}
|
||||
|
||||
GC::Ref<ServiceWorker::ServiceWorkerContainer> WorkerNavigator::service_worker()
|
||||
{
|
||||
if (!m_service_worker_container)
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <LibWeb/HTML/NavigatorLanguage.h>
|
||||
#include <LibWeb/HTML/NavigatorOnLine.h>
|
||||
#include <LibWeb/MediaCapabilitiesAPI/MediaCapabilities.h>
|
||||
#include <LibWeb/Serial/Serial.h>
|
||||
#include <LibWeb/ServiceWorker/ServiceWorkerContainer.h>
|
||||
#include <LibWeb/StorageAPI/NavigatorStorage.h>
|
||||
|
||||
|
@ -38,6 +39,8 @@ public:
|
|||
|
||||
GC::Ref<MediaCapabilitiesAPI::MediaCapabilities> media_capabilities();
|
||||
|
||||
[[nodiscard]] GC::Ref<Serial::Serial> serial();
|
||||
|
||||
private:
|
||||
explicit WorkerNavigator(WorkerGlobalScope&);
|
||||
|
||||
|
@ -50,6 +53,9 @@ private:
|
|||
// https://w3c.github.io/media-capabilities/#dom-workernavigator-mediacapabilities
|
||||
GC::Ptr<MediaCapabilitiesAPI::MediaCapabilities> m_media_capabilities;
|
||||
|
||||
// https://wicg.github.io/serial/#extensions-to-the-workernavigator-interface
|
||||
GC::Ptr<Serial::Serial> m_serial;
|
||||
|
||||
GC::Ptr<ServiceWorker::ServiceWorkerContainer> m_service_worker_container;
|
||||
};
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#import <HTML/NavigatorLanguage.idl>
|
||||
#import <HTML/NavigatorOnLine.idl>
|
||||
#import <MediaCapabilitiesAPI/MediaCapabilities.idl>
|
||||
#import <Serial/Serial.idl>
|
||||
#import <StorageAPI/NavigatorStorage.idl>
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/workers.html#workernavigator
|
||||
|
@ -12,6 +13,9 @@ interface WorkerNavigator {
|
|||
// https://w3c.github.io/media-capabilities/#dom-workernavigator-mediacapabilities
|
||||
[SameObject] readonly attribute MediaCapabilities mediaCapabilities;
|
||||
|
||||
// https://wicg.github.io/serial/#extensions-to-the-workernavigator-interface
|
||||
[SameObject] readonly attribute Serial serial;
|
||||
|
||||
// https://w3c.github.io/ServiceWorker/#navigator-serviceworker
|
||||
[SecureContext, SameObject] readonly attribute ServiceWorkerContainer serviceWorker;
|
||||
};
|
||||
|
|
123
Libraries/LibWeb/Serial/Serial.cpp
Normal file
123
Libraries/LibWeb/Serial/Serial.cpp
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Edwin Hoksberg <mail@edwinhoksberg.nl>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/Bindings/SerialPrototype.h>
|
||||
#include <LibWeb/HTML/EventNames.h>
|
||||
#include <LibWeb/HTML/Scripting/Environments.h>
|
||||
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
#include <LibWeb/Platform/EventLoopPlugin.h>
|
||||
#include <LibWeb/Serial/Serial.h>
|
||||
#include <LibWeb/Serial/SerialPort.h>
|
||||
#include <LibWeb/WebIDL/Promise.h>
|
||||
|
||||
namespace Web::Serial {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(Serial);
|
||||
|
||||
Serial::Serial(JS::Realm& realm)
|
||||
: DOM::EventTarget(realm)
|
||||
{
|
||||
}
|
||||
|
||||
void Serial::initialize(JS::Realm& realm)
|
||||
{
|
||||
WEB_SET_PROTOTYPE_FOR_INTERFACE(Serial);
|
||||
Base::initialize(realm);
|
||||
}
|
||||
|
||||
// https://wicg.github.io/serial/#requestport-method
|
||||
WebIDL::ExceptionOr<GC::Ref<WebIDL::Promise>> Serial::request_port(SerialPortRequestOptions const)
|
||||
{
|
||||
auto& realm = this->realm();
|
||||
|
||||
// FIXME: 1. Let promise be a new promise.
|
||||
|
||||
// FIXME: 2. If this's relevant global object's associated Document is not allowed to use the policy-controlled feature named "serial",
|
||||
// reject promise with a "SecurityError" DOMException and return promise.
|
||||
|
||||
// FIXME: 3. If the relevant global object of this does not have transient activation, reject promise with a "SecurityError" DOMException and return promise.
|
||||
|
||||
// FIXME: 4. If options["filters"] is present, then for each filter in options["filters"] run the following steps:
|
||||
|
||||
// FIXME: 5. Run the following steps in parallel:
|
||||
{
|
||||
// FIXME: 1. Let allPorts be an empty list.
|
||||
|
||||
// FIXME: 2. For each Bluetooth device registered with the system:
|
||||
|
||||
// FIXME: 3. For each available non-Bluetooth serial port:
|
||||
{
|
||||
// FIXME: 1. Let port be a SerialPort representing the port.
|
||||
|
||||
// FIXME: 2. Append port to allPorts.
|
||||
}
|
||||
|
||||
// FIXME: 4. Prompt the user to grant the site access to a serial port by presenting them with a list of ports
|
||||
// in allPorts that match any filter in options["filters"] if present and allPorts otherwise.
|
||||
|
||||
// FIXME: 5. If the user does not choose a port, queue a global task on the relevant global object of this using the
|
||||
// serial port task source to reject promise with a "NotFoundError" DOMException and abort these steps.
|
||||
|
||||
// FIXME: 6. Let port be a SerialPort representing the port chosen by the user.
|
||||
|
||||
// FIXME: 7. Queue a global task on the relevant global object of this using the serial port task source to resolve promise with port.
|
||||
}
|
||||
|
||||
// 6. Return promise.
|
||||
dbgln("FIXME: Unimplemented Serial::request_port()");
|
||||
return WebIDL::create_rejected_promise(realm, WebIDL::UnknownError::create(realm, ""_string));
|
||||
}
|
||||
|
||||
// https://wicg.github.io/serial/#getports-method
|
||||
GC::Ref<WebIDL::Promise> Serial::get_ports()
|
||||
{
|
||||
auto& realm = this->realm();
|
||||
|
||||
// FIXME: 1. Let promise be a new promise.
|
||||
|
||||
// FIXME: 2. If this's relevant global object's associated Document is not allowed to use the policy-controlled feature named "serial",
|
||||
// reject promise with a "SecurityError" DOMException and return promise.
|
||||
|
||||
// FIXME: 3. Run the following steps in parallel:
|
||||
{
|
||||
// FIXME: 1. Let availablePorts be the sequence of available serial ports which the user has allowed the site to
|
||||
// access as the result of a previous call to requestPort().
|
||||
|
||||
// FIXME: 2. Let ports be the sequence of the SerialPorts representing the ports in availablePorts.
|
||||
|
||||
// FIXME: 3. Queue a global task on the relevant global object of this using the serial port task source to resolve promise with ports.
|
||||
}
|
||||
|
||||
// 4. Return promise.
|
||||
dbgln("FIXME: Unimplemented Serial::get_ports()");
|
||||
return WebIDL::create_rejected_promise(realm, WebIDL::UnknownError::create(realm, ""_string));
|
||||
}
|
||||
|
||||
// https://wicg.github.io/serial/#onconnect-attribute
|
||||
void Serial::set_onconnect(WebIDL::CallbackType* event_handler)
|
||||
{
|
||||
set_event_handler_attribute(HTML::EventNames::connect, event_handler);
|
||||
}
|
||||
|
||||
WebIDL::CallbackType* Serial::onconnect()
|
||||
{
|
||||
return event_handler_attribute(HTML::EventNames::connect);
|
||||
}
|
||||
|
||||
// https://wicg.github.io/serial/#ondisconnect-attribute
|
||||
void Serial::set_ondisconnect(WebIDL::CallbackType* event_handler)
|
||||
{
|
||||
set_event_handler_attribute(HTML::EventNames::disconnect, event_handler);
|
||||
}
|
||||
|
||||
WebIDL::CallbackType* Serial::ondisconnect()
|
||||
{
|
||||
return event_handler_attribute(HTML::EventNames::disconnect);
|
||||
}
|
||||
|
||||
}
|
52
Libraries/LibWeb/Serial/Serial.h
Normal file
52
Libraries/LibWeb/Serial/Serial.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Edwin Hoksberg <mail@edwinhoksberg.nl>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/DOM/EventTarget.h>
|
||||
#include <LibWeb/WebIDL/Types.h>
|
||||
|
||||
namespace Web::Serial {
|
||||
|
||||
// https://wicg.github.io/serial/#serialportfilter-dictionary
|
||||
struct SerialPortFilter {
|
||||
Optional<WebIDL::UnsignedShort> usb_vendor_id;
|
||||
Optional<WebIDL::UnsignedShort> usb_product_id;
|
||||
Optional<String> bluetooth_service_class_id;
|
||||
};
|
||||
|
||||
// https://wicg.github.io/serial/#serialportrequestoptions-dictionary
|
||||
struct SerialPortRequestOptions {
|
||||
Optional<Vector<SerialPortFilter>> filters;
|
||||
Optional<Vector<String>> allowed_bluetooth_service_class_ids;
|
||||
};
|
||||
|
||||
// https://wicg.github.io/serial/#serial-interface
|
||||
class Serial : public DOM::EventTarget {
|
||||
WEB_PLATFORM_OBJECT(Serial, DOM::EventTarget);
|
||||
GC_DECLARE_ALLOCATOR(Serial);
|
||||
|
||||
public:
|
||||
// https://wicg.github.io/serial/#requestport-method
|
||||
WebIDL::ExceptionOr<GC::Ref<WebIDL::Promise>> request_port(SerialPortRequestOptions = {});
|
||||
// https://wicg.github.io/serial/#getports-method
|
||||
GC::Ref<WebIDL::Promise> get_ports();
|
||||
|
||||
// https://wicg.github.io/serial/#onconnect-attribute
|
||||
void set_onconnect(WebIDL::CallbackType*);
|
||||
WebIDL::CallbackType* onconnect();
|
||||
|
||||
// https://wicg.github.io/serial/#ondisconnect-attribute
|
||||
void set_ondisconnect(WebIDL::CallbackType*);
|
||||
WebIDL::CallbackType* ondisconnect();
|
||||
|
||||
private:
|
||||
explicit Serial(JS::Realm&);
|
||||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
};
|
||||
|
||||
}
|
26
Libraries/LibWeb/Serial/Serial.idl
Normal file
26
Libraries/LibWeb/Serial/Serial.idl
Normal file
|
@ -0,0 +1,26 @@
|
|||
#import <DOM/EventHandler.idl>
|
||||
#import <Serial/SerialPort.idl>
|
||||
|
||||
// https://wicg.github.io/serial/#serial-interface
|
||||
[Exposed=(DedicatedWorker, Window), SecureContext]
|
||||
interface Serial : EventTarget {
|
||||
attribute EventHandler onconnect;
|
||||
attribute EventHandler ondisconnect;
|
||||
Promise<sequence<SerialPort>> getPorts();
|
||||
[Exposed=Window] Promise<SerialPort> requestPort(optional SerialPortRequestOptions options = {});
|
||||
};
|
||||
|
||||
// https://wicg.github.io/serial/#serialportfilter-dictionary
|
||||
dictionary SerialPortFilter {
|
||||
unsigned short usbVendorId;
|
||||
unsigned short usbProductId;
|
||||
// FIXME: Should be a BluetoothServiceUUID
|
||||
DOMString bluetoothServiceClassId;
|
||||
};
|
||||
|
||||
// https://wicg.github.io/serial/#serialportrequestoptions-dictionary
|
||||
dictionary SerialPortRequestOptions {
|
||||
sequence<SerialPortFilter> filters;
|
||||
// FIXME: Should be a BluetoothServiceUUID
|
||||
sequence<DOMString> allowedBluetoothServiceClassIds;
|
||||
};
|
258
Libraries/LibWeb/Serial/SerialPort.cpp
Normal file
258
Libraries/LibWeb/Serial/SerialPort.cpp
Normal file
|
@ -0,0 +1,258 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Edwin Hoksberg <mail@edwinhoksberg.nl>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/HTML/EventNames.h>
|
||||
#include <LibWeb/Serial/SerialPort.h>
|
||||
#include <LibWeb/WebIDL/Promise.h>
|
||||
|
||||
namespace Web::Serial {
|
||||
|
||||
SerialPort::SerialPort(JS::Realm& realm)
|
||||
: DOM::EventTarget(realm)
|
||||
{
|
||||
}
|
||||
|
||||
void SerialPort::initialize(JS::Realm& realm)
|
||||
{
|
||||
WEB_SET_PROTOTYPE_FOR_INTERFACE(SerialPort);
|
||||
Base::initialize(realm);
|
||||
}
|
||||
|
||||
// https://wicg.github.io/serial/#getinfo-method
|
||||
SerialPortInfo SerialPort::get_info() const
|
||||
{
|
||||
// 1. Let info be an empty ordered map.
|
||||
auto info = SerialPortInfo {};
|
||||
|
||||
// FIXME: 2. If the port is part of a USB device, perform the following steps:
|
||||
{
|
||||
// FIXME: 1. Set info["usbVendorId"] to the vendor ID of the device.
|
||||
|
||||
// FIXME: 2. Set info["usbProductId"] to the product ID of the device.
|
||||
}
|
||||
|
||||
// FIXME: 3. If the port is a service on a Bluetooth device, perform the following steps:
|
||||
{
|
||||
// FIXME: 1. Set info["bluetoothServiceClassId"] to the service class UUID of the Bluetooth service.
|
||||
}
|
||||
|
||||
// 4. Return info.
|
||||
return info;
|
||||
}
|
||||
|
||||
// https://wicg.github.io/serial/#open-method
|
||||
GC::Ref<WebIDL::Promise> SerialPort::open(SerialOptions)
|
||||
{
|
||||
auto& realm = this->realm();
|
||||
|
||||
// FIXME: 1. Let promise be a new promise.
|
||||
|
||||
// FIXME: 2. If this.[[state]] is not "closed", reject promise with an "InvalidStateError" DOMException and return promise.
|
||||
|
||||
// FIXME: 3. If options["dataBits"] is not 7 or 8, reject promise with TypeError and return promise.
|
||||
|
||||
// FIXME: 4. If options["stopBits"] is not 1 or 2, reject promise with TypeError and return promise.
|
||||
|
||||
// FIXME: 5. If options["bufferSize"] is 0, reject promise with TypeError and return promise.
|
||||
|
||||
// FIXME: 6. Optionally, if options["bufferSize"] is larger than the implementation is able to support, reject promise with a TypeError and return promise.
|
||||
|
||||
// FIXME: 7. Set this.[[state]] to "opening".
|
||||
|
||||
// FIXME: 8. Perform the following steps in parallel.
|
||||
{
|
||||
// FIXME: 1. Invoke the operating system to open the serial port using the connection parameters (or their defaults) specified in options.
|
||||
|
||||
// FIXME: 2. If this fails for any reason, queue a global task on the relevant global object of this using the serial port task source to reject promise with a "NetworkError" DOMException and abort these steps.
|
||||
|
||||
// FIXME: 3. Set this.[[state]] to "opened".
|
||||
|
||||
// FIXME: 4. Set this.[[bufferSize]] to options["bufferSize"].
|
||||
|
||||
// FIXME: 5. Queue a global task on the relevant global object of this using the serial port task source to resolve promise with undefined.
|
||||
}
|
||||
|
||||
// FIXME: 9. Return promise.
|
||||
dbgln("FIXME: Unimplemented SerialPort::open()");
|
||||
return WebIDL::create_rejected_promise(realm, WebIDL::UnknownError::create(realm, ""_string));
|
||||
}
|
||||
|
||||
// https://wicg.github.io/serial/#setsignals-method
|
||||
GC::Ref<WebIDL::Promise> SerialPort::set_signals(SerialOutputSignals)
|
||||
{
|
||||
auto& realm = this->realm();
|
||||
|
||||
// FIXME: 1. Let promise be a new promise.
|
||||
|
||||
// FIXME: 2. If this.[[state]] is not "opened", reject promise with an "InvalidStateError" DOMException and return promise.
|
||||
|
||||
// FIXME: 3. If all of the specified members of signals are not present reject promise with TypeError and return promise.
|
||||
|
||||
// FIXME: 4. Perform the following steps in parallel:
|
||||
{
|
||||
// FIXME: 1. If signals["dataTerminalReady"] is present, invoke the operating system to either assert (if true) or
|
||||
// deassert (if false) the "data terminal ready" or "DTR" signal on the serial port.
|
||||
|
||||
// FIXME: 2. If signals["requestToSend"] is present, invoke the operating system to either assert (if true) or
|
||||
// deassert (if false) the "request to send" or "RTS" signal on the serial port.
|
||||
|
||||
// FIXME: 3. If signals["break"] is present, invoke the operating system to either assert (if true) or
|
||||
// deassert (if false) the "break" signal on the serial port.
|
||||
|
||||
// FIXME: 4. If the operating system fails to change the state of any of these signals for any reason, queue a global task
|
||||
// on the relevant global object of this using the serial port task source to reject promise with a "NetworkError" DOMException.
|
||||
|
||||
// FIXME: 5. Queue a global task on the relevant global object of this using the serial port task source to resolve promise with undefined.
|
||||
}
|
||||
|
||||
// 5. Return promise.
|
||||
dbgln("FIXME: Unimplemented SerialPort::set_signals()");
|
||||
return WebIDL::create_rejected_promise(realm, WebIDL::UnknownError::create(realm, ""_string));
|
||||
}
|
||||
|
||||
// https://wicg.github.io/serial/#getsignals-method
|
||||
GC::Ref<WebIDL::Promise> SerialPort::get_signals() const
|
||||
{
|
||||
auto& realm = this->realm();
|
||||
|
||||
// FIXME: 1. Let promise be a new promise.
|
||||
|
||||
// FIXME: 2. If this.[[state]] is not "opened", reject promise with an "InvalidStateError" DOMException and return promise.
|
||||
|
||||
// FIXME: 3. Perform the following steps in parallel:
|
||||
{
|
||||
// FIXME: 1. Query the operating system for the status of the control signals that may be asserted by the device connected to the serial port.
|
||||
|
||||
// FIXME: 2. If the operating system fails to determine the status of these signals for any reason, queue a global task on the relevant global object of
|
||||
// this using the serial port task source to reject promise with a "NetworkError" DOMException and abort these steps.
|
||||
|
||||
// FIXME: 3. Let dataCarrierDetect be true if the "data carrier detect" or "DCD" signal has been asserted by the device, and false otherwise.
|
||||
|
||||
// FIXME: 4. Let clearToSend be true if the "clear to send" or "CTS" signal has been asserted by the device, and false otherwise.
|
||||
|
||||
// FIXME: 5. Let ringIndicator be true if the "ring indicator" or "RI" signal has been asserted by the device, and false otherwise.
|
||||
|
||||
// FIXME: 6. Let dataSetReady be true if the "data set ready" or "DSR" signal has been asserted by the device, and false otherwise.
|
||||
|
||||
// FIXME: 7. Let signals be the ordered map «[ "dataCarrierDetect" → dataCarrierDetect, "clearToSend" → clearToSend, "ringIndicator" → ringIndicator, "dataSetReady" → dataSetReady ]».
|
||||
|
||||
// FIXME: 8. Queue a global task on the relevant global object of this using the serial port task source to resolve promise with signals.
|
||||
}
|
||||
|
||||
// 4. Return promise.
|
||||
dbgln("FIXME: Unimplemented SerialPort::get_signals()");
|
||||
return WebIDL::create_rejected_promise(realm, WebIDL::UnknownError::create(realm, ""_string));
|
||||
}
|
||||
|
||||
// https://wicg.github.io/serial/#close-method
|
||||
GC::Ref<WebIDL::Promise> SerialPort::close()
|
||||
{
|
||||
auto& realm = this->realm();
|
||||
|
||||
// FIXME: 1. Let promise be a new promise.
|
||||
|
||||
// FIXME: 2. If this.[[state]] is not "opened", reject promise with an "InvalidStateError" DOMException and return promise.
|
||||
|
||||
// FIXME: 3. Let cancelPromise be the result of invoking cancel on this.[[readable]] or a promise resolved with undefined if this.[[readable]] is null.
|
||||
|
||||
// FIXME: 4. Let abortPromise be the result of invoking abort on this.[[writable]] or a promise resolved with undefined if this.[[writable]] is null.
|
||||
|
||||
// FIXME: 5. Let pendingClosePromise be a new promise.
|
||||
|
||||
// FIXME: 6. If this.[[readable]] and this.[[writable]] are null, resolve pendingClosePromise with undefined.
|
||||
|
||||
// FIXME: 7. Set this.[[pendingClosePromise]] to pendingClosePromise.
|
||||
|
||||
// FIXME: 8. Let combinedPromise be the result of getting a promise to wait for all with «cancelPromise, abortPromise, pendingClosePromise».
|
||||
|
||||
// FIXME: 9. Set this.[[state]] to "closing".
|
||||
|
||||
// FIXME: 10. React to combinedPromise.
|
||||
{
|
||||
// If combinedPromise was fulfilled, then:
|
||||
// FIXME: 1. Run the following steps in parallel:
|
||||
{
|
||||
// FIXME: 1. Invoke the operating system to close the serial port and release any associated resources.
|
||||
|
||||
// FIXME: 2. Set this.[[state]] to "closed".
|
||||
|
||||
// FIXME: 3. Set this.[[readFatal]] and this.[[writeFatal]] to false.
|
||||
|
||||
// FIXME: 4. Set this.[[pendingClosePromise]] to null.
|
||||
|
||||
// FIXME: 5. Queue a global task on the relevant global object of this using the serial port task source to resolve promise with undefined.
|
||||
}
|
||||
|
||||
// If combinedPromise was rejected with reason r, then:
|
||||
{
|
||||
// FIXME: 1. Set this.[[pendingClosePromise]] to null.
|
||||
|
||||
// FIXME: 2. Queue a global task on the relevant global object of this using the serial port task source to reject promise with r.
|
||||
}
|
||||
}
|
||||
|
||||
// 11. Return promise.
|
||||
dbgln("FIXME: Unimplemented SerialPort::close()");
|
||||
return WebIDL::create_rejected_promise(realm, WebIDL::UnknownError::create(realm, ""_string));
|
||||
}
|
||||
|
||||
// https://wicg.github.io/serial/#forget-method
|
||||
GC::Ref<WebIDL::Promise> SerialPort::forget()
|
||||
{
|
||||
auto& realm = this->realm();
|
||||
|
||||
// FIXME: 1. Let promise be a new promise.
|
||||
|
||||
// FIXME: 1. If the user agent can't perform this action (e.g. permission was granted by administrator policy), return a promise resolved with undefined.
|
||||
|
||||
// FIXME: 2. Run the following steps in parallel:
|
||||
{
|
||||
// FIXME: 1. Set this.[[state]] to "forgetting".
|
||||
|
||||
// FIXME: 2. Remove this from the sequence of serial ports on the system which the user has allowed the site to access as the result of a previous call to requestPort().
|
||||
|
||||
// FIXME: 3. Set this.[[state]] to "forgotten".
|
||||
|
||||
// FIXME: 4. Queue a global task on the relevant global object of this using the serial port task source to resolve promise with undefined.
|
||||
}
|
||||
|
||||
// 7. Return promise.
|
||||
dbgln("FIXME: Unimplemented SerialPort::forget()");
|
||||
return WebIDL::create_rejected_promise(realm, WebIDL::UnknownError::create(realm, ""_string));
|
||||
}
|
||||
|
||||
void SerialPort::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_readable);
|
||||
visitor.visit(m_writable);
|
||||
visitor.visit(m_pending_close_promise);
|
||||
}
|
||||
|
||||
// https://wicg.github.io/serial/#onconnect-attribute-0
|
||||
void SerialPort::set_onconnect(WebIDL::CallbackType* event_handler)
|
||||
{
|
||||
set_event_handler_attribute(HTML::EventNames::connect, event_handler);
|
||||
}
|
||||
|
||||
WebIDL::CallbackType* SerialPort::onconnect()
|
||||
{
|
||||
return event_handler_attribute(HTML::EventNames::connect);
|
||||
}
|
||||
|
||||
// https://wicg.github.io/serial/#ondisconnect-attribute-0
|
||||
void SerialPort::set_ondisconnect(WebIDL::CallbackType* event_handler)
|
||||
{
|
||||
set_event_handler_attribute(HTML::EventNames::disconnect, event_handler);
|
||||
}
|
||||
|
||||
WebIDL::CallbackType* SerialPort::ondisconnect()
|
||||
{
|
||||
return event_handler_attribute(HTML::EventNames::disconnect);
|
||||
}
|
||||
|
||||
}
|
132
Libraries/LibWeb/Serial/SerialPort.h
Normal file
132
Libraries/LibWeb/Serial/SerialPort.h
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Edwin Hoksberg <mail@edwinhoksberg.nl>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/Bindings/SerialPortPrototype.h>
|
||||
#include <LibWeb/DOM/EventTarget.h>
|
||||
#include <LibWeb/Streams/ReadableStream.h>
|
||||
#include <LibWeb/Streams/WritableStream.h>
|
||||
#include <LibWeb/WebIDL/Types.h>
|
||||
|
||||
namespace Web::Serial {
|
||||
|
||||
// https://wicg.github.io/serial/#serialoptions-dictionary
|
||||
struct SerialOptions {
|
||||
Optional<WebIDL::UnsignedLong> baud_rate = {};
|
||||
Optional<WebIDL::Octet> data_bits = 8;
|
||||
Optional<WebIDL::Octet> stop_bits = 1;
|
||||
Optional<Bindings::ParityType> parity = Bindings::ParityType::None;
|
||||
Optional<WebIDL::UnsignedLong> buffer_size = 255;
|
||||
Optional<Bindings::FlowControlType> flow_control = Bindings::FlowControlType::None;
|
||||
};
|
||||
|
||||
// https://wicg.github.io/serial/#serialoutputsignals-dictionary
|
||||
struct SerialOutputSignals {
|
||||
Optional<WebIDL::Boolean> data_terminal_ready;
|
||||
Optional<WebIDL::Boolean> request_to_send;
|
||||
Optional<WebIDL::Boolean> break_;
|
||||
};
|
||||
|
||||
// https://wicg.github.io/serial/#serialinputsignals-dictionary
|
||||
struct SerialInputSignals {
|
||||
WebIDL::Boolean data_carrier_detect;
|
||||
WebIDL::Boolean clear_to_send;
|
||||
WebIDL::Boolean ring_indicator;
|
||||
WebIDL::Boolean data_set_ready;
|
||||
};
|
||||
|
||||
// https://wicg.github.io/serial/#serialportinfo-dictionary
|
||||
struct SerialPortInfo {
|
||||
Optional<WebIDL::UnsignedShort> usb_vendor_id;
|
||||
Optional<WebIDL::UnsignedShort> usb_product_id;
|
||||
Optional<String> bluetooth_service_class_id;
|
||||
};
|
||||
|
||||
enum SerialPortState : u8 {
|
||||
Closed,
|
||||
Opening,
|
||||
Opened,
|
||||
Closing,
|
||||
Forgetting,
|
||||
Forgotten,
|
||||
};
|
||||
|
||||
// https://wicg.github.io/serial/#serialport-interface
|
||||
class SerialPort : public DOM::EventTarget {
|
||||
WEB_PLATFORM_OBJECT(SerialPort, DOM::EventTarget);
|
||||
GC_DECLARE_ALLOCATOR(SerialPort);
|
||||
|
||||
// https://wicg.github.io/serial/#getinfo-method
|
||||
SerialPortInfo get_info() const;
|
||||
// https://wicg.github.io/serial/#open-method
|
||||
GC::Ref<WebIDL::Promise> open(SerialOptions);
|
||||
// https://wicg.github.io/serial/#setsignals-method
|
||||
GC::Ref<WebIDL::Promise> set_signals(SerialOutputSignals = {});
|
||||
// https://wicg.github.io/serial/#getsignals-method
|
||||
GC::Ref<WebIDL::Promise> get_signals() const;
|
||||
// https://wicg.github.io/serial/#close-method
|
||||
GC::Ref<WebIDL::Promise> close();
|
||||
// https://wicg.github.io/serial/#forget-method
|
||||
GC::Ref<WebIDL::Promise> forget();
|
||||
|
||||
// https://wicg.github.io/serial/#connected-attribute
|
||||
bool connected() const { return m_connected; }
|
||||
// https://wicg.github.io/serial/#readable-attribute
|
||||
GC::Ref<Streams::ReadableStream> readable() { return *m_readable; }
|
||||
// https://wicg.github.io/serial/#writable-attribute
|
||||
GC::Ref<Streams::WritableStream> writable() { return *m_writable; }
|
||||
|
||||
// https://wicg.github.io/serial/#onconnect-attribute-0
|
||||
void set_onconnect(WebIDL::CallbackType*);
|
||||
WebIDL::CallbackType* onconnect();
|
||||
|
||||
// https://wicg.github.io/serial/#ondisconnect-attribute-0
|
||||
void set_ondisconnect(WebIDL::CallbackType*);
|
||||
WebIDL::CallbackType* ondisconnect();
|
||||
|
||||
protected:
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
private:
|
||||
explicit SerialPort(JS::Realm&);
|
||||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
|
||||
// https://wicg.github.io/serial/#dfn-state
|
||||
// Tracks the active state of the SerialPort
|
||||
SerialPortState m_state { SerialPortState::Closed };
|
||||
|
||||
// https://wicg.github.io/serial/#dfn-buffersize
|
||||
// The amount of data to buffer for transmit and receive
|
||||
unsigned long m_buffer_size = {};
|
||||
|
||||
// https://wicg.github.io/serial/#dfn-connected
|
||||
// A flag indicating the logical connection state of serial port
|
||||
bool m_connected { false };
|
||||
|
||||
// https://wicg.github.io/serial/#dfn-readable
|
||||
// A ReadableStream that receives data from the port
|
||||
GC::Ptr<Streams::ReadableStream> m_readable = {};
|
||||
|
||||
// https://wicg.github.io/serial/#dfn-readfatal
|
||||
// A flag indicating that the port has encountered a fatal read error
|
||||
bool m_read_fatal { false };
|
||||
|
||||
// https://wicg.github.io/serial/#dfn-writable
|
||||
// A WritableStream that transmits data to the port
|
||||
GC::Ptr<Streams::WritableStream> m_writable = {};
|
||||
|
||||
// https://wicg.github.io/serial/#dfn-writefatal
|
||||
// A flag indicating that the port has encountered a fatal write error
|
||||
bool m_write_fatal { false };
|
||||
|
||||
// https://wicg.github.io/serial/#dfn-pendingclosepromise
|
||||
// A Promise used to wait for readable and writable to close
|
||||
GC::Ptr<WebIDL::Promise> m_pending_close_promise = {};
|
||||
};
|
||||
|
||||
}
|
65
Libraries/LibWeb/Serial/SerialPort.idl
Normal file
65
Libraries/LibWeb/Serial/SerialPort.idl
Normal file
|
@ -0,0 +1,65 @@
|
|||
#import <DOM/EventHandler.idl>
|
||||
|
||||
// https://wicg.github.io/serial/#serialport-interface
|
||||
[Exposed=(DedicatedWorker,Window), SecureContext]
|
||||
interface SerialPort : EventTarget {
|
||||
attribute EventHandler onconnect;
|
||||
attribute EventHandler ondisconnect;
|
||||
readonly attribute boolean connected;
|
||||
readonly attribute ReadableStream readable;
|
||||
readonly attribute WritableStream writable;
|
||||
|
||||
SerialPortInfo getInfo();
|
||||
|
||||
Promise<undefined> open(SerialOptions options);
|
||||
Promise<undefined> setSignals(optional SerialOutputSignals signals = {});
|
||||
Promise<SerialInputSignals> getSignals();
|
||||
Promise<undefined> close();
|
||||
Promise<undefined> forget();
|
||||
};
|
||||
|
||||
// https://wicg.github.io/serial/#serialoptions-dictionary
|
||||
dictionary SerialOptions {
|
||||
[EnforceRange] unsigned long baudRate;
|
||||
[EnforceRange] octet dataBits = 8;
|
||||
[EnforceRange] octet stopBits = 1;
|
||||
ParityType parity = "none";
|
||||
[EnforceRange] unsigned long bufferSize = 255;
|
||||
FlowControlType flowControl = "none";
|
||||
};
|
||||
|
||||
// https://wicg.github.io/serial/#serialoutputsignals-dictionary
|
||||
dictionary SerialOutputSignals {
|
||||
boolean dataTerminalReady;
|
||||
boolean requestToSend;
|
||||
boolean break;
|
||||
};
|
||||
|
||||
// https://wicg.github.io/serial/#serialinputsignals-dictionary
|
||||
dictionary SerialInputSignals {
|
||||
required boolean dataCarrierDetect;
|
||||
required boolean clearToSend;
|
||||
required boolean ringIndicator;
|
||||
required boolean dataSetReady;
|
||||
};
|
||||
|
||||
// https://wicg.github.io/serial/#serialportinfo-dictionary
|
||||
dictionary SerialPortInfo {
|
||||
unsigned short usbVendorId;
|
||||
unsigned short usbProductId;
|
||||
// FIXME: Should be a BluetoothServiceUUID
|
||||
DOMString bluetoothServiceClassId;
|
||||
};
|
||||
|
||||
// https://wicg.github.io/serial/#paritytype-enum
|
||||
enum ParityType {
|
||||
"none",
|
||||
"even",
|
||||
"odd"
|
||||
};
|
||||
|
||||
// https://wicg.github.io/serial/#flowcontroltype-enum
|
||||
enum FlowControlType {
|
||||
"none",
|
||||
"hardware"
|
||||
};
|
|
@ -306,6 +306,8 @@ libweb_js_bindings(ResizeObserver/ResizeObserver)
|
|||
libweb_js_bindings(ResizeObserver/ResizeObserverEntry)
|
||||
libweb_js_bindings(ResizeObserver/ResizeObserverSize)
|
||||
libweb_js_bindings(ResourceTiming/PerformanceResourceTiming)
|
||||
libweb_js_bindings(Serial/Serial)
|
||||
libweb_js_bindings(Serial/SerialPort)
|
||||
libweb_js_bindings(ServiceWorker/CacheStorage)
|
||||
libweb_js_bindings(ServiceWorker/ServiceWorker)
|
||||
libweb_js_bindings(ServiceWorker/ServiceWorkerContainer)
|
||||
|
|
|
@ -330,6 +330,7 @@ CppType idl_type_name_to_cpp_type(Type const& type, Interface const& interface)
|
|||
static ByteString make_input_acceptable_cpp(ByteString const& input)
|
||||
{
|
||||
if (input.is_one_of(
|
||||
"break",
|
||||
"char",
|
||||
"class",
|
||||
"continue",
|
||||
|
@ -4841,6 +4842,7 @@ using namespace Web::RequestIdleCallback;
|
|||
using namespace Web::ResizeObserver;
|
||||
using namespace Web::ResourceTiming;
|
||||
using namespace Web::Selection;
|
||||
using namespace Web::Serial;
|
||||
using namespace Web::ServiceWorker;
|
||||
using namespace Web::StorageAPI;
|
||||
using namespace Web::Streams;
|
||||
|
|
|
@ -37,6 +37,7 @@ static constexpr Array libweb_interface_namespaces = {
|
|||
"ResizeObserver"sv,
|
||||
"SVG"sv,
|
||||
"Selection"sv,
|
||||
"Serial"sv,
|
||||
"ServiceWorker"sv,
|
||||
"Streams"sv,
|
||||
"UIEvents"sv,
|
||||
|
|
|
@ -388,6 +388,8 @@ ScreenOrientation
|
|||
ScriptProcessorNode
|
||||
SecurityPolicyViolationEvent
|
||||
Selection
|
||||
Serial
|
||||
SerialPort
|
||||
ServiceWorker
|
||||
ServiceWorkerContainer
|
||||
ServiceWorkerRegistration
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue