LibWeb: Stub WebSerial API

This commit is contained in:
Edwin Hoksberg 2025-08-06 09:07:40 +02:00 committed by Jelle Raaijmakers
commit 1be31c103f
Notes: github-actions[bot] 2025-08-08 08:24:30 +00:00
19 changed files with 715 additions and 0 deletions

View file

@ -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

View file

@ -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;

View file

@ -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) \

View file

@ -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)

View file

@ -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;

View file

@ -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;

View file

@ -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)

View file

@ -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;
};

View file

@ -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;
};

View 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);
}
}

View 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;
};
}

View 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;
};

View 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);
}
}

View 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 = {};
};
}

View 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"
};

View file

@ -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)

View file

@ -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;

View file

@ -37,6 +37,7 @@ static constexpr Array libweb_interface_namespaces = {
"ResizeObserver"sv,
"SVG"sv,
"Selection"sv,
"Serial"sv,
"ServiceWorker"sv,
"Streams"sv,
"UIEvents"sv,

View file

@ -388,6 +388,8 @@ ScreenOrientation
ScriptProcessorNode
SecurityPolicyViolationEvent
Selection
Serial
SerialPort
ServiceWorker
ServiceWorkerContainer
ServiceWorkerRegistration