Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ All notable changes to OriginWeave are documented in this file. The format follo

### Changed

- Retained the originating connection when sending a pointer click so later response validation can reject acknowledgments received through a replacement connection; sending still does not prove that the click completed.
- Integrated the current connection-provenance and pointer-click prerequisites into bounded click transport, retaining both public adapters and keeping frame-write evidence separate from browser-action completion.
- Integrated the current connection-provenance prerequisite into bounded pointer-click serialization, preserving its command validation and inert authority boundary while restoring the inherited executable release contract.
- Removed an unused private correlated-response accessor while retaining connection-generation validation at the receiving-message boundary, and corrected the Rust `AtomicU64` standard-library reference to its canonical type-alias page.
- Integrated the current teardown prerequisites into transport-closure observation, including the previously uncollected release-record check, while retaining the unresolved connection-provenance finding and its downstream repair ownership.
Expand All @@ -14,6 +16,7 @@ All notable changes to OriginWeave are documented in this file. The format follo
### Added

- Deterministic WebDriver BiDi primary-button click serialization for an already admitted remote node: it emits one fixed `input.performActions` mouse sequence from bounded command/context/node identifiers and remains inert until a trusted adapter binds it to current session, origin, document, policy, and approval authority.
- Typed outbound WebDriver BiDi primary-button click transport over the bounded client WebSocket stream: it rejects invalid frame deadlines before correlation registration, retires only the just-registered id when local frame preflight proves no command bytes were emitted, preserves correlation across ambiguous writes, and does not treat frame-write success as proof that the browser performed the click.
- Typed outbound WebDriver BiDi `session.end` over the bounded client WebSocket stream: it serializes only the standards-defined method with empty params, rejects invalid frame deadlines before correlation registration, retires only the just-registered id when frame preflight proves no command bytes were emitted, preserves exact command-kind correlation across ambiguous writes, and does not treat frame-write success as proof that the browser session ended.
- Typed `session.end` response admission that consumes only the exact outstanding command-kind correlation after complete envelope validation, preserves remote protocol errors as failures, and does not claim browser-process exit or resource cleanup from a protocol acknowledgment.
- Fail-closed `session.end` teardown assessment that binds only the typed observation produced by consuming the exact transport, keeps browser-process-exit and task-profile-removal evidence unavailable until their runtime owners exist, and therefore cannot report operational completion from caller-supplied booleans.
Expand Down
7 changes: 6 additions & 1 deletion crates/originweave-network/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
//! unmasked server-frame reads, assembles bounded WebDriver BiDi text messages,
//! binds received fragmented text to one exact verified connection, classifies
//! complete local-end JSON envelopes, tracks bounded command-response correlation,
//! sends narrowly typed `session.status` and `session.end` commands, admits typed
//! transports a narrowly typed pointer click, sends narrowly typed
//! `session.status` and `session.end` commands, admits typed
//! correlated status and end responses, binds `session.end` ACK and closure evidence
//! to one private process-local connection generation, observes bounded peer Close
//! or clean-EOF transport cessation, and keeps protocol/transport evidence separate
Expand All @@ -23,6 +24,7 @@ mod connection;
mod webdriver_bidi_command_correlation;
mod webdriver_bidi_connection;
mod webdriver_bidi_json_envelope;
mod webdriver_bidi_pointer_click_transport;
mod webdriver_bidi_received_message;
mod webdriver_bidi_session_end_command;
mod webdriver_bidi_session_end_response;
Expand Down Expand Up @@ -56,6 +58,9 @@ pub use webdriver_bidi_json_envelope::{
MAX_WEBDRIVER_BIDI_JS_UINT, MAX_WEBDRIVER_BIDI_JSON_DEPTH, WebDriverBiDiJsonEnvelope,
WebDriverBiDiJsonEnvelopeError, WebDriverBiDiJsonEnvelopeKind,
};
pub use webdriver_bidi_pointer_click_transport::{
WebDriverBiDiPointerClickSendError, send_webdriver_bidi_pointer_click,
};
pub use webdriver_bidi_received_message::{
WebDriverBiDiConnectionMessageRead, WebDriverBiDiConnectionMessageReadError,
WebDriverBiDiReceivedTextMessage, WebDriverBiDiWebSocketMessageReader,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use std::{error::Error, fmt, time::Duration};

use originweave_core::WebDriverBiDiPointerClickCommand;

use crate::{
MAX_WEBSOCKET_FRAME_TIMEOUT, WebDriverBiDiCommandCorrelation,
WebDriverBiDiCommandCorrelationError, WebDriverBiDiCommandKind,
WebDriverBiDiWebSocketEstablished, WebDriverBiDiWebSocketFrameError,
WebDriverBiDiWebSocketMaskKey,
};

/// Fail-closed errors while transporting one already validated pointer-click command.
#[derive(Debug)]
pub enum WebDriverBiDiPointerClickSendError {
/// The bounded correlation registry rejected the command before network I/O.
Correlation {
/// Exact typed correlation failure.
source: WebDriverBiDiCommandCorrelationError,
},
/// Writing the already-registered command frame failed and the transport is not reusable.
FrameWrite {
/// Exact typed bounded WebSocket frame-write failure.
source: WebDriverBiDiWebSocketFrameError,
},
}

impl fmt::Display for WebDriverBiDiPointerClickSendError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(match self {
Self::Correlation { .. } => {
"WebDriver BiDi pointer-click command correlation was rejected"
}
Self::FrameWrite { .. } => "WebDriver BiDi pointer-click command frame write failed",
})
}
}

impl Error for WebDriverBiDiPointerClickSendError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::Correlation { source } => Some(source),
Self::FrameWrite { source } => Some(source),
}
}
}

/// Register and write one already validated `input.performActions` pointer-click command.
///
/// Invalid local frame deadlines fail before registration. Correlation then occurs before the first
/// possible remote side effect and retains the exact connection's private generation for later
/// connection-bound response admission. A frame preflight rejection that proves no write began retires the
/// exact id; a partial or complete remote side effect remains ambiguous and leaves it outstanding.
///
/// This boundary accepts only [`WebDriverBiDiPointerClickCommand`], not arbitrary JSON or method
/// names. It does not authenticate the browser, grant session/context/origin/document-epoch
/// authority, authorize policy or TypedInput capability, admit nodes, correlate a response, prove an
/// observed post-condition, retry, reconnect, or choose another destination. A trusted caller must
/// establish those independent authorities before transport and retain response/post-condition
/// evidence afterward.
pub fn send_webdriver_bidi_pointer_click(
command: &WebDriverBiDiPointerClickCommand,
established: WebDriverBiDiWebSocketEstablished,
correlation: &mut WebDriverBiDiCommandCorrelation,
masking_key: WebDriverBiDiWebSocketMaskKey,
frame_timeout: Duration,
) -> Result<WebDriverBiDiWebSocketEstablished, WebDriverBiDiPointerClickSendError> {
if frame_timeout.is_zero() {
return Err(invalid_frame_timeout(frame_timeout));
}
if frame_timeout > MAX_WEBSOCKET_FRAME_TIMEOUT {
return Err(invalid_frame_timeout(frame_timeout));
}
match correlation.register_command_for_connection(
command.command_id(),
WebDriverBiDiCommandKind::PointerClick,
established.transport_evidence().connection_generation(),
) {
Ok(()) => {}
Err(source) => {
return Err(WebDriverBiDiPointerClickSendError::Correlation { source });
}
}
match established.write_text_frame(command.as_json(), masking_key, frame_timeout) {
Ok(established) => Ok(established),
Err(source) => Err(map_frame_failure(correlation, command.command_id(), source)),
}
}

fn invalid_frame_timeout(frame_timeout: Duration) -> WebDriverBiDiPointerClickSendError {
WebDriverBiDiPointerClickSendError::FrameWrite {
source: WebDriverBiDiWebSocketFrameError::InvalidFrameTimeout {
frame_timeout,
maximum_timeout: MAX_WEBSOCKET_FRAME_TIMEOUT,
},
}
}

fn map_frame_failure(
correlation: &mut WebDriverBiDiCommandCorrelation,
command_id: u64,
source: WebDriverBiDiWebSocketFrameError,
) -> WebDriverBiDiPointerClickSendError {
match source {
WebDriverBiDiWebSocketFrameError::MalformedFrame { reason } => {
let _retirement =
correlation.retire_command_for(command_id, WebDriverBiDiCommandKind::PointerClick);
WebDriverBiDiPointerClickSendError::FrameWrite {
source: WebDriverBiDiWebSocketFrameError::MalformedFrame { reason },
}
}
source => WebDriverBiDiPointerClickSendError::FrameWrite { source },
}
}
Loading
Loading