Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9ab369c
test(network): require navigation committed subscription
seonghobae Aug 31, 2026
911dedd
feat(network): add context-bound navigation subscription
seonghobae Aug 31, 2026
d45a0da
feat(network): export navigation subscription boundary
seonghobae Aug 31, 2026
87e23d2
test(network): revalidate subscription context before send
seonghobae Aug 31, 2026
e648e9c
style(network): apply subscription rustfmt diagnostics
seonghobae Aug 31, 2026
b7d94c9
test(network): remove synthetic subscription coverage residual
seonghobae Aug 31, 2026
d3a8243
test(network): cover subscription send failure contracts
seonghobae Aug 31, 2026
64974f9
style(network): apply canonical subscription formatting
seonghobae Aug 31, 2026
48a65d1
test(network): exercise subscription production surface
seonghobae Aug 31, 2026
4ae26f6
test(network): keep subscription coverage on public boundary
seonghobae Aug 31, 2026
2ea92c1
test(network): accept bounded extended BiDi command frames
seonghobae Aug 31, 2026
b50aa56
test(network): cover subscription constructor authority failure
seonghobae Aug 31, 2026
efc6e8c
test(network): require typed subscribe result
seonghobae Aug 31, 2026
7912633
feat(network): retain typed BiDi subscription id
seonghobae Aug 31, 2026
0897113
feat(network): expose typed BiDi subscription result
seonghobae Aug 31, 2026
082a783
style(network): apply canonical rustfmt
seonghobae Aug 31, 2026
2f2f394
fix(network): make subscription response errors comparable
seonghobae Aug 31, 2026
57a0b8d
test(network): cover subscription response failure boundary
seonghobae Aug 31, 2026
4bbde31
test(network): close subscription projection coverage gaps
seonghobae Aug 31, 2026
e6646c3
style(network): apply canonical rustfmt
seonghobae Aug 31, 2026
6902fa3
test(network): exercise UTF-8 subscription extension
seonghobae Aug 31, 2026
65f934a
style(network): apply canonical subscription test formatting
seonghobae Aug 31, 2026
16b20ba
test(network): cover malformed surrogate separator
seonghobae Aug 31, 2026
93e2e32
fix(network): isolate validated protocol-error projection
seonghobae Aug 31, 2026
9df2fc2
merge: converge navigation subscription on teardown authority repair
seonghobae Aug 31, 2026
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
32 changes: 22 additions & 10 deletions crates/originweave-network/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,17 @@
//! unmasked server-frame reads, assembles bounded WebDriver BiDi text messages,
//! classifies complete local-end JSON envelopes, tracks bounded command-response
//! correlation, transports a narrowly typed pointer click, admits its typed
//! correlated protocol acknowledgment, admits a bounded navigation-committed
//! post-condition observation for one exact registered context and URL, rotates
//! the matched context's document epoch only from an exact caller-captured
//! pre-action epoch, derives and binds the committed HTTP(S) URL's canonical
//! origin to that newly advanced document, sends narrowly typed `session.status`
//! and `session.end` commands, admits typed correlated status and end responses,
//! observes bounded peer Close or clean-EOF transport cessation, and keeps
//! protocol/transport evidence separate from explicit operational teardown
//! observations without exposing generic JSON bodies or granting browser, TLS,
//! policy, secret, process, profile, or Agent authority.
//! correlated protocol acknowledgment, sends a context-bound subscription for
//! committed-navigation events, retains its typed bounded correlated subscription
//! identifier, admits a bounded navigation-committed post-condition observation
//! for one exact registered context and URL, rotates the matched context's document
//! epoch only from an exact caller-captured pre-action epoch, derives and binds the
//! committed HTTP(S) URL's canonical origin to that newly advanced document, sends
//! narrowly typed `session.status` and `session.end` commands, admits typed
//! correlated status and end responses, observes bounded peer Close or clean-EOF
//! transport cessation, and keeps protocol/transport evidence separate from
//! explicit operational teardown observations without exposing generic JSON bodies
//! or granting browser, TLS, policy, secret, process, profile, or Agent authority.

#![forbid(unsafe_code)]
#![deny(missing_docs)]
Expand All @@ -28,6 +29,8 @@ mod webdriver_bidi_command_correlation;
mod webdriver_bidi_connection;
mod webdriver_bidi_json_envelope;
mod webdriver_bidi_navigation_committed_postcondition;
mod webdriver_bidi_navigation_committed_subscription;
mod webdriver_bidi_navigation_committed_subscription_response;
mod webdriver_bidi_navigation_document_advance;
mod webdriver_bidi_navigation_document_origin;
mod webdriver_bidi_pointer_click_response;
Expand Down Expand Up @@ -69,6 +72,15 @@ pub use webdriver_bidi_navigation_committed_postcondition::{
WebDriverBiDiNavigationCommittedObservationError,
WebDriverBiDiNavigationCommittedProjectionError,
};
pub use webdriver_bidi_navigation_committed_subscription::{
WebDriverBiDiNavigationCommittedSubscriptionCommand,
WebDriverBiDiNavigationCommittedSubscriptionCommandError,
};
pub use webdriver_bidi_navigation_committed_subscription_response::{
MAX_WEBDRIVER_BIDI_SUBSCRIPTION_IDENTIFIER_BYTES,
WebDriverBiDiNavigationCommittedSubscriptionResponseError,
WebDriverBiDiNavigationCommittedSubscriptionResult,
};
pub use webdriver_bidi_navigation_document_advance::{
WebDriverBiDiNavigationCommittedDocumentAdvance,
WebDriverBiDiNavigationCommittedDocumentAdvanceError,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
use std::{error::Error, fmt, time::Duration};

use originweave_core::{
BrowserAuthorityRegistry, BrowserRegistryError, BrowserSessionId, BrowsingContextId,
};

use crate::{
MAX_WEBDRIVER_BIDI_JS_UINT, WEBDRIVER_BIDI_NAVIGATION_COMMITTED_METHOD,
WebDriverBiDiCommandCorrelation, WebDriverBiDiCommandCorrelationError,
WebDriverBiDiWebSocketEstablished, WebDriverBiDiWebSocketFrameError,
WebDriverBiDiWebSocketMaskKey,
};

const SESSION_SUBSCRIBE_METHOD: &str = "session.subscribe";

/// One context-scoped subscription for the committed-navigation WebDriver BiDi event.
///
/// This command is deliberately narrower than the protocol's generic `session.subscribe` surface:
/// it can request only `browsingContext.navigationCommitted`, for one external context that already
/// maps to the exact supplied OriginWeave session/context pair. It does not expose arbitrary event
/// names, global subscriptions, user-context subscriptions, generic JSON, or arbitrary method
/// dispatch. Successful construction or transport does not authenticate Chromium, authorize a
/// navigation, grant destination or policy authority, or make later event data reusable Agent
/// authority.
pub struct WebDriverBiDiNavigationCommittedSubscriptionCommand {
command_id: u64,
browser_session: BrowserSessionId,
browsing_context: BrowsingContextId,
external_context: String,
}

impl WebDriverBiDiNavigationCommittedSubscriptionCommand {
/// Construct one bounded context-scoped committed-navigation subscription command.
///
/// The external protocol identifier must already name the exact registered OriginWeave
/// session/context pair. No registry state is created as a side effect of untrusted adapter text.
pub fn new(
command_id: u64,
registry: &BrowserAuthorityRegistry,
browser_session: BrowserSessionId,
browsing_context: BrowsingContextId,
external_context: &str,
) -> Result<Self, WebDriverBiDiNavigationCommittedSubscriptionCommandError> {
if command_id > MAX_WEBDRIVER_BIDI_JS_UINT {
return Err(
WebDriverBiDiNavigationCommittedSubscriptionCommandError::CommandIdOutOfRange {
command_id,
maximum_command_id: MAX_WEBDRIVER_BIDI_JS_UINT,
},
);
}
require_registered_context(
registry,
browser_session,
browsing_context,
external_context,
)?;
Ok(Self {
command_id,
browser_session,
browsing_context,
external_context: external_context.to_owned(),
})
}

/// Return the exact local correlation identifier serialized by this command.
#[must_use]
pub const fn command_id(&self) -> u64 {
self.command_id
}

/// Return the exact registered OriginWeave browser session bound during construction.
#[must_use]
pub const fn browser_session(&self) -> BrowserSessionId {
self.browser_session
}

/// Return the exact registered OriginWeave browsing context bound during construction.
#[must_use]
pub const fn browsing_context(&self) -> BrowsingContextId {
self.browsing_context
}

/// Borrow the exact external WebDriver BiDi context identifier serialized by this command.
#[must_use]
pub fn external_context(&self) -> &str {
&self.external_context
}

/// Revalidate, register, and write this exact subscription on an established verified BiDi stream.
///
/// Context binding is revalidated immediately before command correlation and network I/O so a
/// command retained across registry retirement cannot subscribe a stale or replacement context.
/// Correlation registration then occurs before the first possible remote side effect. A binding
/// or correlation failure therefore writes nothing. After successful registration, a frame-write
/// failure consumes the transport and intentionally leaves the identifier outstanding because a
/// partial or fully emitted frame has ambiguous remote effect.
pub fn send(
self,
registry: &BrowserAuthorityRegistry,
established: WebDriverBiDiWebSocketEstablished,
correlation: &mut WebDriverBiDiCommandCorrelation,
masking_key: WebDriverBiDiWebSocketMaskKey,
frame_timeout: Duration,
) -> Result<
WebDriverBiDiWebSocketEstablished,
WebDriverBiDiNavigationCommittedSubscriptionCommandError,
> {
require_registered_context(
registry,
self.browser_session,
self.browsing_context,
&self.external_context,
)?;
correlation
.register_command(self.command_id)
.map_err(|source| {
WebDriverBiDiNavigationCommittedSubscriptionCommandError::Correlation { source }
})?;
let message = self.serialized();
established
.write_text_frame(&message, masking_key, frame_timeout)
.map_err(|source| {
WebDriverBiDiNavigationCommittedSubscriptionCommandError::FrameWrite { source }
})
}

fn serialized(&self) -> String {
let mut message = format!(
"{{\"id\":{},\"method\":\"{SESSION_SUBSCRIBE_METHOD}\",\"params\":{{\"events\":[\"{WEBDRIVER_BIDI_NAVIGATION_COMMITTED_METHOD}\"],\"contexts\":[",
self.command_id
);
push_json_string(&mut message, &self.external_context);
message.push_str("]}}");
message
}
}

fn require_registered_context(
registry: &BrowserAuthorityRegistry,
browser_session: BrowserSessionId,
browsing_context: BrowsingContextId,
external_context: &str,
) -> Result<(), WebDriverBiDiNavigationCommittedSubscriptionCommandError> {
registry
.require_registered_context_external_identifier(
browser_session,
browsing_context,
external_context,
)
.map_err(|source| {
WebDriverBiDiNavigationCommittedSubscriptionCommandError::ContextBinding { source }
})
}

fn push_json_string(target: &mut String, value: &str) {
target.push('"');
for character in value.chars() {
match character {
'"' => target.push_str("\\\""),
'\\' => target.push_str("\\\\"),
_ => target.push(character),
}
}
target.push('"');
}

/// Fail-closed failures while constructing or sending one typed committed-navigation subscription.
#[derive(Debug)]
pub enum WebDriverBiDiNavigationCommittedSubscriptionCommandError {
/// The requested command identifier is outside WebDriver BiDi's `js-uint` range.
CommandIdOutOfRange {
/// Rejected command identifier.
command_id: u64,
/// Largest JavaScript-safe identifier admitted by this boundary.
maximum_command_id: u64,
},
/// The external protocol context does not name the exact registered OriginWeave context.
ContextBinding {
/// Exact typed browser-registry authority failure.
source: BrowserRegistryError,
},
/// The bounded local 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 WebDriverBiDiNavigationCommittedSubscriptionCommandError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::CommandIdOutOfRange { .. } => formatter.write_str(
"WebDriver BiDi navigation subscription command id is outside the js-uint range",
),
Self::ContextBinding { .. } => formatter.write_str(
"WebDriver BiDi navigation subscription context does not match registered authority",
),
Self::Correlation { .. } => formatter.write_str(
"WebDriver BiDi navigation subscription command correlation was rejected",
),
Self::FrameWrite { .. } => formatter.write_str(
"WebDriver BiDi navigation subscription command frame write failed",
),
}
}
}

impl Error for WebDriverBiDiNavigationCommittedSubscriptionCommandError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::CommandIdOutOfRange { .. } => None,
Self::ContextBinding { source } => Some(source),
Self::Correlation { source } => Some(source),
Self::FrameWrite { source } => Some(source),
}
}
}
Loading
Loading