/* * Copyright (c) 2025, Edwin Hoksberg * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include 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 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, Utf16String {})); } // https://wicg.github.io/serial/#setsignals-method GC::Ref 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, Utf16String {})); } // https://wicg.github.io/serial/#getsignals-method GC::Ref 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, Utf16String {})); } // https://wicg.github.io/serial/#close-method GC::Ref 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, Utf16String {})); } // https://wicg.github.io/serial/#forget-method GC::Ref 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, Utf16String {})); } 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); } }