WebDriver BiDi

Editor’s Draft,

More details about this document
This version:
https://w3c.github.io/webdriver-bidi/
Issue Tracking:
GitHub
Inline In Spec

Abstract

This document defines the BiDirectional WebDriver Protocol, a mechanism for remote control of user agents.

Status of this document

This is a public copy of the editors’ draft. It is provided for discussion only and may change at any moment. Its publication here does not imply endorsement of its contents by W3C. Don’t cite this document other than as work in progress.

GitHub Issues are preferred for discussion of this specification.

This document was produced by the Browser Testing and Tools Working Group.

This document was produced by a group operating under the W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.

This document is governed by the 2 November 2021 W3C Process Document.

1. Introduction

This section is non-normative.

WebDriver defines a protocol for introspection and remote control of user agents. This specification extends WebDriver by introducing bidirectional communication. In place of the strict command/response format of WebDriver, this permits events to stream from the user agent to the controlling software, better matching the evented nature of the browser DOM.

2. Infrastructure

This specification depends on the Infra Standard. [INFRA]

Network protocol messages are defined using CDDL. [RFC8610]

This specification defines a wait queue which is a map.

Surely there’s a better mechanism for doing this "wait for an event" thing.

When an algorithm algorithm running in parallel awaits a set of events events, and resume id:

  1. Pause the execution of algorithm.

  2. Assert: wait queue does not contain resume id.

  3. Set wait queue[resume id] to (events, algorithm).

To resume given name, id and parameters:
  1. If wait queue does not contain id, return.

  2. Let (events, algorithm) be wait queue[id]

  3. For each event in events:

    1. If event equals name:

      1. Remove id from wait queue.

      2. Resume running the steps in algorithm from the point at which they were paused, passing name and parameters as the result of the await.

        Should we have something like microtasks to ensure this runs before any other tasks on the event loop?

3. Protocol

This section defines the basic concepts of the WebDriver BiDi protocol. These terms are distinct from their representation at the transport layer.

The protocol is defined using a CDDL definition. For the convenience of implementors two separate CDDL definitions are defined; the remote end definition which defines the format of messages produced on the local end and consumed on the remote end, and the local end definition which defines the format of messages produced on the remote end and consumed on the local end

3.1. Definition

Should this be an appendix?

This section gives the initial contents of the remote end definition and local end definition. These are augmented by the definition fragments defined in the remainder of the specification.

Remote end definition

Command = {
  id: js-uint,
  CommandData,
  Extensible,
}

CommandData = (
  BrowserCommand //
  BrowsingContextCommand //
  InputCommand //
  NetworkCommand //
  ScriptCommand //
  SessionCommand
)

EmptyParams = {
   Extensible
}

Local end definition

Message = (
  CommandResponse //
  ErrorResponse //
  Event
)

CommandResponse = {
  id: js-uint,
  result: ResultData,
  Extensible
}

ErrorResponse = {
  id: js-uint / null,
  error: ErrorCode,
  message: text,
  ? stacktrace: text,
  Extensible
}

ResultData = (
  BrowsingContextResult //
  EmptyResult //
  NetworkResult //
  ScriptResult //
  SessionResult
)

EmptyResult = {
  Extensible
}

Event = {
  EventData,
  Extensible
}

EventData = (
  BrowsingContextEvent //
  LogEvent //
  NetworkEvent //
  ScriptEvent
)

Remote end definition and Local end definition

Extensible = (*text => any)

js-int = -9007199254740991..9007199254740991
js-uint = 0..9007199254740991

3.2. Session

WebDriver BiDi extends the session concept from WebDriver.

A session has a BiDi flag, which is false unless otherwise stated.

A BiDi session is a session which has the BiDi flag set to true.

The set of active BiDi sessions is given by:
  1. Let BiDi sessions be a new set.

  2. For each session in active sessions:

    1. If session is a BiDi session append session to BiDi sessions.

  3. Return BiDi sessions

3.3. Modules

The WebDriver BiDi protocol is organized into modules.

Each module represents a collection of related commands and events pertaining to a certain aspect of the user agent. For example, a module might contain functionality for inspecting and manipulating the DOM, or for script execution.

Each module has a module name which is a string. The command name and event name for commands and events defined in the module start with the module name followed by a period ".".

Modules which contain commands define remote end definition fragments. These provide choices in the CommandData group for the module’s commands, and can also define additional definition properties. They can also define local end definition fragments that provide additional choices in the ResultData group for the results of commands in the module.

Modules which contain events define local end definition fragments that are choices in the Event group for the module’s events.

An implementation may define extension modules. These must have a module name that contains a single colon ":" character. The part before the colon is the prefix; this is typically the same for all extension modules specific to a given implementation and should be unique for a given implementation. Such modules extend the local end definition and remote end definition providing additional groups as choices for the defined commands and events.

3.4. Commands

A command is an asynchronous operation, requested by the local end and run on the remote end, resulting in either a result or an error being returned to the local end. Multiple commands can run at the same time, and commands can potentially be long-running. As a consequence, commands can finish out-of-order.

Each command is defined by:

A command that can run without an active session is a static command. Commands are not static commands unless stated in their definition.

When commands are sent from the local end they have a command id. This is an identifier used by the local end to identify the response from a particular command. From the point of view of the remote end this identifier is opaque and cannot be used internally to identify the command.

Note: This is because the command id is entirely controlled by the local end and isn’t necessarily unique over the course of a session. For example a local end which ignores all responses could use the same command id for each command.

The set of all command names is a set containing all the defined command names, including any belonging to extension modules.

3.5. Errors

WebDriver BiDi extends the set of error codes from WebDriver with the following additional codes:

no such script
Tried to remove an unknown preload script.
no such handle
Tried to deserialize an unknown RemoteObjectReference.
no such node
Tried to deserialize an unknown SharedReference.
unable to close browser
Tried to close the browser, but failed to do so.
ErrorCode = ("invalid argument" /
             "invalid session id" /
             "no such alert" /
             "no such frame" /
             "no such handle" /
             "no such node" /
             "no such script" /
             "session not created" /
             "unable to close browser" /
             "unknown command" /
             "unknown error" /
             "unsupported operation")

3.6. Events

An event is a notification, sent by the remote end to the local end, signaling that something of interest has occurred on the remote end.

A BiDi session has a global event set which is a set containing the event names for events that are enabled for all browsing contexts. This initially contains the event name for events that are in the default event set.

A BiDi session has a browsing context event map, which is a map with top-level browsing context keys and values that are a set of event names for events that are enabled in the given browsing context.

To obtain a list of event enabled browsing contexts given session and event name:

  1. Let contexts be an empty set.

  2. For each contextevents of session’s browsing context event map:

    1. If events contains event name, append context to contexts

  3. Return contexts.

The set of sessions for which an event is enabled given event name and browsing contexts is:

  1. Let sessions be a new set.

  2. For each session in active BiDI sessions:

    1. If event is enabled with session, event name and browsing contexts, append session to sessions.

  3. Return sessions

To determine if an event is enabled given session, event name and browsing contexts:

Note: browsing contexts is a set because a shared worker can be associated with multiple contexts.

  1. Let top-level browsing contexts be an empty set.

  2. For each browsing context of browsing contexts, append browsing context’s top-level browsing context to top-level browsing contexts.

  3. Let event map be the browsing context event map for session.

  4. For each browsing context of top-level browsing contexts:

    1. If event map contains browsing context, let browsing context events be event map[browsing context]. Otherwise let browsing context events be null.

    2. If browsing context events is not null, and browsing context events contains event name, return true.

  5. If the global event set for session contains event name return true.

  6. Return false.

To obtain a set of event names given an name:
  1. Let events be an empty set.

  2. If name contains a U+002E (period):

    1. If name is the event name for an event, append name to events and return success with data events.

    2. Return an error with error code invalid argument

  3. Otherwise name is interpreted as representing all the events in a module. If name is not a module name return an error with error code invalid argument.

  4. Append the event name for each event in the module with name name to events.

  5. Return success with data events.

To emit events given body and related browsing contexts:
  1. Assert: body has size 2 and contains "method" and "params".

  2. For each session in the set of sessions for which an event is enabled given body["method"] and related browsing contexts:

    1. Emit an event with session and body.

4. Transport

Message transport is provided using the WebSocket protocol. [RFC6455]

Note: In the terms of the WebSocket protocol, the local end is the client and the remote end is the server / remote host.

Note: The encoding of commands and events as messages is similar to JSON-RPC, but this specification does not normatively reference it. [JSON-RPC] The normative requirements on remote ends are instead given as a precise processing model, while no normative requirements are given for local ends.

A WebSocket listener is a network endpoint that is able to accept incoming WebSocket connections.

A WebSocket listener has a host, a port, a secure flag, and a list of WebSocket resources.

When a WebSocket listener listener is created, a remote end must start to listen for WebSocket connections on the host and port given by listener’s host and port. If listener’s secure flag is set, then connections established from listener must be TLS encrypted.

A remote end has a set of WebSocket listeners active listeners, which is initially empty.

A remote end has a set of WebSocket connections not associated with a session, which is initially empty.

A WebSocket connection is a network connection that follows the requirements of the WebSocket protocol

A BiDi session has a set of session WebSocket connections whose elements are WebSocket connections. This is initially empty.

A BiDi session session is associated with connection connection if session’s session WebSocket connections contains connection.

Note: Each WebSocket connection is associated with at most one BiDi session.

When a client establishes a WebSocket connection connection by connecting to one of the set of active listeners listener, the implementation must proceed according to the WebSocket server-side requirements, with the following steps run when deciding whether to accept the incoming connection:

  1. Let resource name be the resource name from reading the client’s opening handshake. If resource name is not in listener’s list of WebSocket resources, then stop running these steps and act as if the requested service is not available.

  2. If resource name is the byte string "/session", and the implementation supports BiDi-only sessions:

    1. Run any other implementation-defined steps to decide if the connection should be accepted, and if it is not stop running these steps and act as if the requested service is not available.

    2. Add the connection to WebSocket connections not associated with a session.

    3. Return.

  3. Get a session ID for a WebSocket resource with resource name and let session id be that value. If session id is null then stop running these steps and act as if the requested service is not available.

  4. If there is a session in the list of active sessions with session id as its session ID then let session be that session. Otherwise stop running these steps and act as if the requested service is not available.

  5. Run any other implementation-defined steps to decide if the connection should be accepted, and if it is not stop running these steps and act as if the requested service is not available.

  6. Otherwise append connection to session’s session WebSocket connections, and proceed with the WebSocket server-side requirements when a server chooses to accept an incoming connection.

Do we support > 1 connection for a single session?

When a WebSocket message has been received for a WebSocket connection connection with type type and data data, a remote end must handle an incoming message given connection, type and data.

When the WebSocket closing handshake is started or when the WebSocket connection is closed for a WebSocket connection connection, a remote end must handle a connection closing given connection.

Note: Both conditions are needed because it is possible for a WebSocket connection to be closed without a closing handshake.

To construct a WebSocket resource name given a session session:

  1. If session is null, return "/session"

  2. Return the result of concatenating the string "/session/" with session’s session ID.

To construct a WebSocket URL given a WebSocket listener listener and session session:

  1. Let resource name be the result of constructing a WebSocket resource name given session.

  2. Return a WebSocket URI constructed with host set to listener’s host, port set to listener’s port, path set to resource name, following the wss-URI construct if listener’s secure flag is set and the ws-URL construct otherwise.

To get a session ID for a WebSocket resource given resource name:

  1. If resource name doesn’t begin with the byte string "/session/", return null.

  2. Let session id be the bytes in resource name following the "/session/" prefix.

  3. If session id is not the string representation of a UUID, return null.

  4. Return session id.

To start listening for a WebSocket connection given a session session:
  1. If there is an existing WebSocket listener in active listeners which the remote end would like to reuse, let listener be that listener. Otherwise let listener be a new WebSocket listener with implementation-defined host, port, secure flag, and an empty list of WebSocket resources.

  2. Let resource name be the result of constructing a WebSocket resource name given session.

  3. Append resource name to the list of WebSocket resources for listener.

  4. Append listener to the remote end's active listeners.

  5. Return listener.

Note: An intermediary node handling multiple sessions can use one or many WebSocket listeners. WebDriver defines that an endpoint node supports at most one session at a time, so it’s expected to only have a single listener.

Note: For an endpoint node the host in the above steps will typically be "localhost".

To handle an incoming message given a WebSocket connection connection, type type and data data:
  1. If type is not text, send an error response given connection, null, and invalid argument, and finally return.

  2. Assert: data is a scalar value string, because the WebSocket handling errors in UTF-8-encoded data would already have failed the WebSocket connection otherwise.

    Nothing seems to define what status code is used for UTF-8 errors.

  3. If there is a BiDi Session associated with connection connection, let session be that session. Otherwise if connection is in WebSocket connections not associated with a session, let session be null. Otherwise, return.

  4. Let parsed be the result of parsing JSON into Infra values given data. If this throws an exception, then send an error response given connection, null, and invalid argument, and finally return.

  5. If session is not null and not in active sessions then return.

  6. Match parsed against the remote end definition. If this results in a match:

    1. Let matched be the map representing the matched data.

    2. Assert: matched contains "id", "method", and "params".

    3. Let command id be matched["id"].

    4. Let method be matched["method"]

    5. Let command be the command with command name method.

    6. If session is null and command is not a static command, then send an error response given connection, command id, and invalid session id, and return.

    7. Run the following steps in parallel:

      1. Let result be the result of running the remote end steps for command given session and command parameters matched["params"]

      2. If result is an error, then send an error response given connection, command id, and result’s error code, and finally return.

      3. Let value be result’s data.

      4. Assert: value matches the definition for the result type corresponding to the command with command name method.

      5. If method is "session.new", let session be the entry in the list of active sessions whose session ID is equal to the "sessionId" property of value, append connection to session’s session WebSocket connections, and remove connection from the WebSocket connections not associated with a session.

      6. Let response be a new map matching the CommandResponse production in the local end definition with the id field set to command id and the value field set to value.

      7. Let serialized be the result of serialize an infra value to JSON bytes given response.

      8. Send a WebSocket message comprised of serialized over connection.

  7. Otherwise:

    1. Let command id be null.

    2. If parsed is a map and parsed["id"] exists and is an integer greater than or equal to zero, set command id to that integer.

    3. Let error code be invalid argument.

    4. If parsed is a map and parsed["method"] exists and is a string, but parsed["method"] is not in the set of all command names, set error code to unknown command.

    5. Send an error response given connection, command id, and error code.

To get related browsing contexts given an settings object settings:

  1. Let related browsing contexts be an empty set

  2. If settingsrelevant global object is a Window, append settingsrelevant global object's associated Document’s browsing context to related browsing contexts.

    Otherwise if the global object specified by settings is a WorkerGlobalScope, for each owner in the global object's owner set, if owner is a Document, append owner’s browsing context to related browsing contexts.

  3. Return related browsing contexts.

To emit an event given session, and body:
  1. Assert: body has size 2 and contains "method" and "params".

  2. Let serialized be the result of serialize an infra value to JSON bytes given body.

  3. For each connection in session’s session WebSocket connections:

    1. Send a WebSocket message comprised of serialized over connection.

To send an error response given a WebSocket connection connection, command id, and error code:
  1. Let error data be a new map matching the ErrorResponse production in the local end definition, with the id field set to command id, the error field set to error code, the message field set to an implementation-defined string containing a human-readable definition of the error that occurred and the stacktrace field optionally set to an implementation-defined string containing a stack trace report of the active stack frames at the time when the error occurred.

  2. Let response be the result of serialize an infra value to JSON bytes given error data.

    Note: command id can be null, in which case the id field will also be set to null, not omitted from response.

  3. Send a WebSocket message comprised of response over connection.

To handle a connection closing given a WebSocket connection connection:

  1. If there is a BiDi session associated with connection connection:

    1. Let session be the BiDi session associated with connection connection.

    2. Remove connection from session’s session WebSocket connections.

  2. Otherwise, if WebSocket connections not associated with a session contains connection, remove connection from that set.

Note: This does not end any session.

Need to hook in to the session ending to allow the UA to close the listener if it wants.

To close the WebSocket connections given session:

  1. For each connection in session’s session WebSocket connections:

    1. Start the WebSocket closing handshake with connection.

      Note: this will result in the steps in handle a connection closing being run for connection, which will clean up resources associated with connection.

4.1. Establishing a Connection

WebDriver clients opt in to a bidirectional connection by requesting a capability with the name "webSocketUrl" and value true.

This specification defines an additional webdriver capability with the capability name "webSocketUrl".

The additional capability deserialization algorithm for the "webSocketUrl" capability, with parameter value is:
  1. If value is not a boolean, return error with code invalid argument.

  2. Return success with data value.

The matched capability serialization algorithm for the "webSocketUrl" capability, with parameter value is:
  1. If value is false, return success with data null.

  2. Return success with data true.

The WebDriver new session algorithm defined by this specification, with parameters session, capabilities, and flags is:
  1. If flags contains "bidi", return.

  2. Let webSocketUrl be the result of getting a property named "webSocketUrl" from capabilities.

  3. If webSocketUrl is undefined or false, return.

  4. Assert: webSocketUrl is true.

  5. Let listener be the result of start listening for a WebSocket connection given session.

  6. Set webSocketUrl to the result of constructing a WebSocket URL given listener and session.

  7. Set a property on capabilities named "webSocketUrl" to webSocketUrl.

  8. Set session’s BiDi flag to true.

  9. Append "bidi" to flags.

Implementations should also allow clients to establish a BiDi Session which is not a HTTP Session. In this case the URL to the WebSocket server is communicated out-of-band. An implementation that allows this supports BiDi-only sessions. At the time such an implementation is ready to accept requests to start a WebDriver session, it must:

  1. Start listening for a WebSocket connection given null.

5. Sandboxed Script Execution

A common requirement for automation tools is to execute scripts which have access to the DOM of a document, but don’t have information about any changes to the DOM APIs made by scripts running in the browsing context containing the document.

A BiDi session has a sandbox map which is a weak map in which the keys are Window objects, and the values are maps between strings and SandboxWindowProxy objects.

Note: The definition of sandboxes here is an attempt to codify the behaviour of existing implementations. It exposes parts of the implementations that have previously been considered internal by specifications, in particular the distinction between the internal state of platform objects (which is typically implemented as native objects in the main implementation language of the browser engine) and the ECMAScript-visible state. Because existing sandbox implementations happen at a low level in the engine, implementations converging toward the specification in all details might be a slow process. In the meantime, implementors are encouraged to provide detailed documentation on any differences with the specification, and users of this feature are encouraged to explicitly test that scripts running in sandboxes work in all implementations.

5.1. Sandbox Realms

Each sandbox is a unique ECMAScript Realm. However the sandbox realm provides access to platform objects in an existing Window realm via SandboxProxy objects.

To get or create a sandbox realm given name and browsing context:
  1. If name is an empty string, then return error with error code invalid argument.

  2. Let window be browsing context’s active window.

  3. If sandbox map does not contain window, set sandbox map[window] to a new map.

  4. Let sandboxes be sandbox map[window].

  5. If sandboxes does not contain name, set sandboxes[name] to create a sandbox realm with browsing context.

  6. Return success with data sandboxes[name].

To create a sandbox realm with window:

Define creation of sandbox realm. This is going to return a SandboxWindowProxy wrapping window.

To get a sandbox name given target realm:

  1. Let realms maps be get the values of sandbox map.

  2. For each realms map in realms maps:

    1. For each namerealm in realms map:

      1. If realm is target realm, return name.

  3. Return null.

5.2. Sandbox Proxy Objects

A SandboxProxy object is an exotic object that mediates sandboxed access to objects from another realm. Sandbox proxy objects are designed to enforce the following restrictions:

There is no SandboxProxy interface object.

Define in detail how SandboxProxy works

To get unwrapped object:
  1. While object is SandboxProxy or SandboxWindowProxy, set object to it’s wrapped object.

  2. Return object.

5.3. SandboxWindowProxy

A SandboxWindowProxy is an exotic object that represents a Window object wrapped by a SandboxProxy object. This provides sandboxed access to that data in a Window global.

Define how this works.

6. Modules

6.1. The session Module

The session module contains commands and events for monitoring the status of the remote end.

6.1.1. Definition

remote end definition

SessionCommand = (
  session.End //
  session.New //
  session.Status //
  session.Subscribe //
  session.Unsubscribe
)

local end definition

SessionResult = (
   session.NewResult //
   session.StatusResult
)
To end the session given session:
  1. Remove session from active sessions.

  2. If active sessions is empty, set the webdriver-active flag to false.

To cleanup the session given session:

  1. Close the WebSocket connections with session.

  2. Perform any implementation-specific cleanup steps.

To update the event map, given session, requested event names, browsing contexts, and enabled:

Note: The return value of this algorithm is a map between event names and contexts. When the events are being enabled, the contexts in the return value are those for which the event are now enabled but were not previously. When events are disabled, the return value is always empty.

  1. Let global event set be a clone of the global event set for session.

  2. Let event map be a new map.

  3. For each keyvalue of the browsing context event map for session:

    1. Set event map[key] to a clone of value.

  4. Let event names be an empty set.

  5. For each entry name in requested event names, let event names be the union of event names and the result of trying to obtain a set of event names with name.

  6. Let enabled events be a new map.

  7. If browsing contexts is null:

    1. If enabled is true:

      1. For each event name of event names:

        1. If global event set doesn’t contain event name:

          1. Let already enabled contexts be the event enabled browsing contexts given session and event name

          2. Add event name to global event set.

          3. For each context of already enabled contexts, remove event name from event map[context].

          4. Let newly enabled contexts be a list of all top-level browsing contexts that are not contained in already enabled contexts,

          5. Set enabled events[event name] to newly enabled contexts.

    2. If enabled is false:

      1. For each event name in event names:

        1. If global event set contains event name, remove event name from global event set. Otherwise return error with error code invalid argument.

  8. Otherwise, if browsing contexts is not null:

    1. Let targets be an empty map.

    2. For each context id in browsing contexts:

      1. Let context be the result of trying to get a browsing context with context id.

      2. Let top-level context be the top-level browsing context for context.

      3. If event map does not contain top-level context, set event map[top-level context] to a new set.

      4. Set targets[top-level context] to event map[top-level context].

    3. For each event name in event names:

      1. If enabled is true and global event set contains event name, continue.

      2. For each contexttarget in targets:

        1. If enabled is true and target does not contain event name:

          1. Add event name to target.

          2. If enabled events does not contain event name, set enabled events[event name] to a new set.

          3. Append context to enabled events[event name].

        2. If enabled is false:

          1. If target contains event name, remove event name from target. Otherwise return error with error code invalid argument.

  9. Set the global event set for session to global event set.

  10. Set the browsing context event map for session to event map.

  11. Return success with data enabled events.

Note: Implementations that do additional work when an event is enabled, e.g. subscribing to the relevant engine-internal events, will likely perform those additional steps when updating the event map. This specification uses a model where hooks are always called and then the event map is used to filter only those that ought to be returned to the local end.

6.1.2. Types

6.1.2.1. The session.CapabilitiesRequest Type
session.CapabilitiesRequest = {
  ? alwaysMatch: session.CapabilityRequest,
  ? firstMatch: [*session.CapabilityRequest]
}

The session.CapabilitiesRequest type represents the capabilities requested for a session.

6.1.2.2. The session.CapabilityRequest Type

remote end definition and local end definition

session.CapabilityRequest = {
  ? acceptInsecureCerts: bool,
  ? browserName: text,
  ? browserVersion: text,
  ? platformName: text,
  ? proxy: {
    ? proxyType: "pac" / "direct" / "autodetect" / "system" / "manual",
    ? proxyAutoconfigUrl: text,
    ? ftpProxy: text,
    ? httpProxy: text,
    ? noProxy: [*text],
    ? sslProxy: text,
    ? socksProxy: text,
    ? socksVersion: 0..255,
  },
  Extensible
};

The session.CapabilityRequest type represents a sepecific set of requested capabilities.

6.1.2.3. The session.SubscriptionRequest Type
session.SubscriptionRequest = {
  events: [*text],
  ? contexts: [*browsingContext.BrowsingContext],
}

The session.SubscriptionRequest type represents a request to subscribe to or unsubscribe from a specific set of events.

6.1.3. Commands

6.1.3.1. The session.status Command

The session.status command returns information about whether a remote end is in a state in which it can create new sessions, but may additionally include arbitrary meta information that is specific to the implementation.

This is a static command.

Command Type
session.Status = {
  method: "session.status",
  params: EmptyParams,
}
Result Type
session.StatusResult = {
  ready: bool,
  message: text,
}

The remote end steps given session, and command parameters are:

  1. Let body be a new map with the following properties:

    "ready"
    The remote end’s readiness state.
    "message"
    An implementation-defined string explaining the remote end’s readiness state.
  2. Return success with data body

6.1.3.2. The session.new Command

The session.new command allows creating a new BiDi session.

Note: A session created this way will not be accessible via HTTP.

This is a static command.

Command Type
session.New = {
  method: "session.new",
  params: session.NewParameters
}

session.NewParameters = {
  capabilities: session.CapabilitiesRequest
}
Result Type
session.NewResult = {
  sessionId: text,
  capabilities: {
    acceptInsecureCerts: bool,
    browserName: text,
    browserVersion: text,
    platformName: text,
    proxy: {
      ? proxyType: "pac" / "direct" / "autodetect" / "system" / "manual",
      ? proxyAutoconfigUrl: text,
      ? ftpProxy: text,
      ? httpProxy: text,
      ? noProxy: [*text],
      ? sslProxy: text,
      ? socksProxy: text,
      ? socksVersion: 0..255,
    },
    setWindowRect: bool,
    Extensible
  }
}

The remote end steps given session and command parameters are:

  1. If session is not null, return an error with error code session not created.

  2. If the implementation is unable to start a new session for any reason, return an error with error code session not created.

  3. Let flags be a set containing "bidi".

  4. Let capabilities be the result of trying to process capabilities with command parameters and flags.

  5. Let session be the result of trying to create a session with capabilities and flags.

  6. Set session’s BiDi flag to true.

    Note: the connection for this session will be set to the current connection by the caller.

  7. Let body be a new map matching the session.NewResult production, with the sessionId field set to session’s session ID, and the capabilities field set to capabilities.

  8. Return success with data body.

6.1.3.3. The session.end Command

The session.end command ends the current session.

Command Type
session.End = {
  method: "session.end",
  params: EmptyParams
}

Result Type
EmptyResult

The remote end steps given session and command parameters are:

  1. End the session with session.

  2. Return success with data null, and in parallel run the following steps:

    1. Wait until the Send a WebSocket message steps have been called with the response to this command.

      this is rather imprecise language, but hopefully it’s clear that the intent is that we send the response to the command before starting shutdown of the connections.

    2. Cleanup the session with session.

6.1.3.4. The session.subscribe Command

The session.subscribe command enables certain events either globally or for a set of browsing contexts

This needs to be generalized to work with realms too

Command Type
session.Subscribe = {
  method: "session.subscribe",
  params: session.SubscriptionRequest
}
Result Type
EmptyResult
The remote end steps with session and command parameters are:
  1. Let the list of event names be the value of the events field of command parameters

  2. Let the list of contexts be the value of the contexts field of command parameters if it is present or null if it isn’t.

  3. Let enabled events be the result of trying to update the event map with session, list of event names , list of contexts and enabled true.

  4. Let subscribe step events be a new map.

  5. For each event namecontexts in enabled events:

    1. If the event with event name event name defines remote end subscribe steps, set subscribe step events[event name] to contexts.

  6. Sort in ascending order subscribe step events using the following less than algorithm given two entries with keys event name one and event name two:

    1. Let event one be the event with name event name one

    2. Let event two be the event with name event name two

    3. Return true if event one’s subscribe priority is less than event two’s subscribe priority, or false otherwise.

  7. If list of contexts is null, let include global be true, otherwise let include global be false.

  8. For each event namecontexts in subscribe step events:

    1. Run the remote end subscribe steps for the event with event name event name given session, contexts and include global.

  9. Return success with data null.

6.1.3.5. The session.unsubscribe Command

The session.unsubscribe command disables events either globally or for a set of browsing contexts

This needs to be generalised to work with realms too

Command Type
session.Unsubscribe = {
  method: "session.unsubscribe",
  params: session.SubscriptionRequest
}
Result Type
EmptyResult
The remote end steps with session and command parameters are:
  1. Let the list of event names be the value of the events field of command parameters.

  2. Let the list of contexts be the value of the contexts field of command parameters if it is present or null if it isn’t.

  3. Try to update the event map with session, list of event names, list of contexts and enabled false.

  4. Return success with data null.

6.2. The browser Module

The browser module contains commands for managing the remote end browser process.

6.2.1. Definition

remote end definition

BrowserCommand = (
  browser.Close
)

local end definition


   

6.2.2. Commands

6.2.2.1. The browser.close Command

The browser.close command terminates all WebDriver sessions and cleans up automation state in the remote browser instance.

Command Type
browser.Close = {
  method: "browser.close",
  params: EmptyParams,
}
Return Type
EmptyResult
The remote end steps with session and command parameters are:
  1. End the session with session.

  2. If active sessions is not empty an implementation may return error with error code unable to close browser, and then run the following steps in parallel:

    1. Wait until the Send a WebSocket message steps have been called with the response to this command.

    2. Cleanup the session with session.

    Note: The behaviour in cases where the browser has multiple automation sessions is currently unspecified. It might be that any session can close the browser, or that only the final open session can actually close the browser, or only the first session started can. This behaviour might be fully specified in a future version of this specification.

  3. For each active session in active sessions:

    1. End the session active session.

    2. Cleanup the session with active session

  4. Return success with data null, and run the following steps in parallel.

    1. Wait until the Send a WebSocket message steps have been called with the response to this command.

    2. Cleanup the session with session.

    3. Close any top-level browsing contexts without prompting to unload.

      Note: This implicitly only affects browsing contexts that were under automation in the first place.

    4. Perform implementation defined steps to clean up resources associated with the remote end under automation.

      Note: For example this might include cleanly shutting down any OS-level processes associated with the browser under automation, removing temporary state, such as user profile data, created by the remote end while under automation, or shutting down the WebSocket Listener. Because of differences between browsers and operating systems it is not possible to specify in detail precise invariants local ends can depend on here.

6.3. The browsingContext Module

The browsingContext module contains commands and events relating to browsing contexts.

The progress of navigation is communicated using an immutable WebDriver navigation status struct, which has the following items:

id
The navigation id for the navigation, or null when the navigation is canceled before making progress.
status
A status code that is either "canceled", "pending", or "complete".
url
The URL which is being loaded in the navigation

6.3.1. Definition

remote end definition

BrowsingContextCommand = (
  browsingContext.CaptureScreenshot //
  browsingContext.Close //
  browsingContext.Create //
  browsingContext.GetTree //
  browsingContext.HandleUserPrompt //
  browsingContext.Navigate //
  browsingContext.Print //
  browsingContext.Reload
)

local end definition

BrowsingContextResult = (
  browsingContext.CaptureScreenshotResult //
  browsingContext.CreateResult //
  browsingContext.GetTreeResult //
  browsingContext.NavigateResult //
  browsingContext.PrintResult
)

BrowsingContextEvent = (
  browsingContext.ContextCreated //
  browsingContext.ContextDestroyed //
  browsingContext.NavigationStarted //
  browsingContext.FragmentNavigated //
  browsingContext.DomContentLoaded //
  browsingContext.Load //
  browsingContext.DownloadWillBegin //
  browsingContext.NavigationAborted //
  browsingContext.NavigationFailed //
  browsingContext.UserPromptClosed //
  browsingContext.UserPromptOpened
)

6.3.2. Types

6.3.2.1. The browsingContext.BrowsingContext Type

remote end definition and local end definition

browsingContext.BrowsingContext = text;

Each browsing context has an associated browsing context id, which is a string uniquely identifying that browsing context. This is implicitly set when the context is created. For browsing contexts with an associated WebDriver window handle the browsing context id must be the same as the window handle.

To get a browsing context given context id:
  1. If context id is null, return success with data null.

  2. If there is no browsing context with browsing context id context id return error with error code no such frame

  3. Let context be the browsing context with id context id.

  4. Return success with data context

6.3.2.2. The browsingContext.Info Type

local end definition

browsingContext.InfoList = [*browsingContext.Info]

browsingContext.Info = {
  context: browsingContext.BrowsingContext,
  url: text,
  children: browsingContext.InfoList / null
  ? parent: browsingContext.BrowsingContext / null,
}

The browsingContext.Info type represents the properties of a browsing context.

To get the parent browsing context given browsing context:
  1. Let navigable be the navigable whose active document is browsing context’s active document.

  2. Let parent navigable be navigable’s parent.

  3. If parent navigable is null, then return null.

  4. Return parent navigable’s active browsing context

To get the child browsing contexts given browsing context:

TODO: make this return a list in document order

  1. Let navigable be the navigable whose active document is browsing context’s active document.

  2. Let child navigables be a set containing all navigables that are a child navigable of navigable.

  3. Let child browsing contexts be an empty set.

  4. For each child navigable in child navigables:

    1. Append child navigable’s active browsing context to child browsing contexts.

  5. Return child browsing contexts.

To get the browsing context info given context, max depth and is root:
  1. Let context id be the browsing context id for context.

  2. Let parent browsing context be get the parent browsing context given context.

  3. If parent browsing context is not null let parent id be the browsing context id of parent browsing context. Otherwise let parent id be null.

  4. Let document be context’s active document.

  5. Let url be the result of running the URL serializer, given document’s URL.

    Note: This includes the fragment component of the URL.

  6. Let child infos be null.

  7. If max depth is null, or max depth is greater than 0:

    1. Let child contexts be get the child browsing contexts given context.

    2. Let child depth be max depth - 1 if max depth is not null, or null otherwise.

    3. Set child infos to an empty list.

    4. For each context of child contexts:

      1. Let info be the result of get the browsing context info given context, child depth, and false.

      2. Append info to child infos

  8. Let context info be a map matching the browsingContext.Info production with the context field set to context id, the parent field set to parent id if is root is true, or unset otherwise, the url field set to url, and the children field set to child infos.

  9. Return context info.

To await a navigation given context, request, wait condition, and optionally history handling (default: "default") and ignore cache (default: false):
  1. Let navigation id be the string representation of a UUID based on truly random, or pseudo-random numbers.

  2. Let navigable be the navigable whose active document is context’s active document.

  3. Navigate navigable with resource request, and using context’s active document as the source Document, with navigation id navigation id, and history handling behavior history handling. If ignore cache is true, the navigation must not load resources from the HTTP cache.

    property specify how the ignore cache flag works. This needs to consider whether only the first load of a resource bypasses the cache (i.e. whether this is like initially clearing the cache and proceeding like normal), or whether resources not directly loaded by the HTML parser (e.g. loads initiated by scripts or stylesheets) also bypass the cache.

  4. Let (event received, navigate status) be await given «"navigation started", "navigation failed", "fragment navigated"», and navigation id.

  5. Assert: navigate status’s id is navigation id.

  6. If navigate status’s status is "complete":

    1. Let body be a map matching the browsingContext.NavigateResult production, with the navigation field set to navigation id, and the url field set to the result of the URL serializer given navigate status’s url.

    2. Return success with data body.

    Note: this is the case if the navigation only caused the fragment to change.

  7. If navigate status’s status is "canceled" return error with error code unknown error.

    TODO: is this the right way to handle errors here?

  8. Assert: navigate status’s status is "pending" and navigation id is not null.

  9. If wait condition is "none":

    1. Let body be a map matching the browsingContext.NavigateResult production, with the navigation field set to navigation id, and the url field set to the result of the URL serializer given navigate status’s url.

    2. Return success with data body.

  10. If wait condition is "interactive", let event name be "domContentLoaded", otherwise let event name be "load".

  11. Let (event received, status) be await given «event name, "download started", "navigation aborted", "navigation failed"» and navigation id.

  12. If event received is "navigation failed" return error with error code unknown error.

    Are we surfacing enough information about what failed and why with an error here? What error code do we want? Is there going to be a problem where local ends parse the implementation-defined strings to figure out what actually went wrong?

  13. Let body be a map matching the browsingContext.NavigateResult production, with the navigation field set to status’s id, and the url field set to the result of the URL serializer given status’s url.

  14. Return success with data body.

6.3.2.3. The browsingContext.Navigation Type

remote end definition and local end definition

browsingContext.Navigation = text;

The browsingContext.Navigation type is a unique string identifying an ongoing navigation.

TODO: Link to the definition in the HTML spec.

6.3.2.4. The browsingContext.NavigationInfo Type

local end definition:

browsingContext.NavigationInfo = {
  context: browsingContext.BrowsingContext,
  navigation: browsingContext.Navigation / null,
  timestamp: js-uint,
  url: text,
}

The browsingContext.NavigationInfo type provides details of an ongoing navigation.

To get the navigation info, given context and navigation status:
  1. Let context id be the browsing context id for context.

  2. Let navigation id be navigation status’s id.

  3. Let timestamp be a time value representing the current date and time in UTC.

  4. Let url be navigation status’s url.

  5. Return a map matching the browsingContext.NavigationInfo production, with the context field set to context id, the navigation field set to navigation id, the timestamp field set to timestamp, and the url field set to the result of the URL serializer given url.

6.3.2.5. The browsingContext.ReadinessState Type
browsingContext.ReadinessState = "none" / "interactive" / "complete"

The browsingContext.ReadinessState type represents the stage of document loading at which a navigation command will return.

6.3.3. Commands

6.3.3.1. The browsingContext.captureScreenshot Command

The browsingContext.captureScreenshot command captures an image of the given browsing context, and returns it as a Base64-encoded string.

Command Type
browsingContext.CaptureScreenshot = {
  method: "browsingContext.captureScreenshot",
  params: browsingContext.CaptureScreenshotParameters
}

browsingContext.CaptureScreenshotParameters = {
  context: browsingContext.BrowsingContext
}
Result Type
browsingContext.CaptureScreenshotResult = {
  data: text
}

TODO: Full page screenshots, and multiple output formats

The remote end steps with session and command parameters are:

  1. Let context id be the value of the context field of command parameters.

  2. Let context be the result of trying to get a browsing context with context id.

  3. If the implementation is unable to capture a screenshot of context for any reason then return error with error code unsupported operation.

  4. Let document be context’s active document.

  5. Immediately after the next invocation of the run the animation frame callbacks algorithm for document:

    This ought to be integrated into the update rendering algorithm in some more explicit way.

  6. Let root rect be document’s document element's rectangle.

  7. Let canvas be the result of trying to draw a bounding box from the framebuffer with root rect.

  8. Let encoding result be the result of trying to encode a canvas as Base64 with canvas.

  9. Let body be a map matching the browsingContext.CaptureScreenshotResult production, with the data field set to encoding result.

  10. Return success with data body.

6.3.3.2. The browsingContext.close Command

The browsingContext.close command closes a top-level browsing context.

Command Type
browsingContext.Close = {
  method: "browsingContext.close",
  params: browsingContext.CloseParameters
}

browsingContext.CloseParameters = {
  context: browsingContext.BrowsingContext
}
Result Type
EmptyResult
The remote end steps with command parameters are:
  1. Let context id be the value of the context field of command parameters.

  2. Let context be the result of trying to get a browsing context with context id.

  3. Assert: context is not null.

  4. If context is not a top-level browsing context, return error with error code invalid argument.

  5. Close context.

  6. Return success with data null.

There is an open discussion about the behavior when closing the last top-level browsing context. We could expect to close the browser, close the session or leave this up to the implementation. [Issue #w3c/webdriver-bidi#170]

6.3.3.3. The browsingContext.create Command

The browsingContext.create command creates a new browsing context, either in a new tab or in a new window, and returns its browsing context id.

Command Type
browsingContext.Create = {
  method: "browsingContext.create",
  params: browsingContext.CreateParameters
}

browsingContext.CreateType = "tab" / "window"

browsingContext.CreateParameters = {
  type: browsingContext.CreateType,
  ? referenceContext: browsingContext.BrowsingContext
}
Result Type
browsingContext.CreateResult = {
  context: browsingContext.BrowsingContext
}
The remote end steps with command parameters are:
  1. Let type be the value of the type field of command parameters.

  2. Let reference context id be the value of the referenceContext field of command parameters, if present, or null otherwise.

  3. If reference context id is not null, let reference context be the result of trying to get a browsing context with reference context id. Otherwise let reference context be null.

  4. If reference context is not null and is not a top-level browsing context, return error with error code invalid argument.

  5. If the implementation is unable to create a new browsing context for any reason then return error with error code unsupported operation.

  6. Create a new top-level browsing context by running the window open steps with url set to "about:blank", target set to the empty string, and features set to "noopener". This must be done without invoking the focusing steps for the created browsing context. Which OS window the new browsing context is created in depends on type and reference context:

    • If type is "tab" and the implementation supports multiple browsing contexts in the same OS window:

      • The new browsing context should reuse an existing OS window, if any.

      • If reference context is not null, the new browsing context should reuse the window containing reference context, if any. If the top-level browsing contexts inside an OS window have a definite ordering, the new browsing context should be immediately after reference context’s top-level browsing context in that ordering.

    • If type is "window", and the implementation supports multiple browsing contexts in separate OS windows, the created browsing context should be in a new OS window.

    • Otherwise, the details of how the browsing context is presented to the user are implementation defined.

  7. Let context id be the browsing context id of the newly created browsing context.

  8. Let body be a map matching the browsingContext.CreateResult production, with the context field set to context id.

  9. Return success with data body.

6.3.3.4. The browsingContext.getTree Command

The browsingContext.getTree command returns a tree of all descendent browsing contexts including the given parent itself, or all top-level contexts when no parent is provided.

Command Type
browsingContext.GetTree = {
  method: "browsingContext.getTree",
  params: browsingContext.GetTreeParameters
}

browsingContext.GetTreeParameters = {
  ? maxDepth: js-uint,
  ? root: browsingContext.BrowsingContext,
}
Result Type
browsingContext.GetTreeResult = {
  contexts: browsingContext.InfoList
}
The remote end steps with session and command parameters are:
  1. Let root id be the value of the root field of command parameters if present, or null otherwise.

  2. Let max depth be the value of the maxDepth field of command parameters if present, or null otherwise.

  3. Let contexts be an empty list.

  4. If root id is not null, append the result of trying to get a browsing context given root id to contexts. Otherwise append all top-level browsing contexts to contexts.

  5. Let contexts info be an empty list.

  6. For each context of contexts:

    1. Let info be the result of get the browsing context info given context, max depth, and true.

    2. Append info to contexts info

  7. Let body be a map matching the browsingContext.GetTreeResult production, with the contexts field set to contexts info.

  8. Return success with data body.

6.3.3.5. The browsingContext.handleUserPrompt Command

The browsingContext.handleUserPrompt command allows closing an open prompt

Command Type
browsingContext.HandleUserPrompt = {
  method: "browsingContext.handleUserPrompt",
  params: browsingContext.HandleUserPromptParameters
}

browsingContext.HandleUserPromptParameters = {
  context: browsingContext.BrowsingContext,
  ? accept: bool,
  ? userText: text,
}
Result Type
EmptyResult

The remote end steps with session and command parameters are:

  1. Let context id be the value of the context field of command parameters.

  2. Let context be the result of trying to get a browsing context with context id.

  3. Let accept be the value of the accept field of command parameters if present, or true otherwise.

  4. Let text be the value of the text field of command parameters if present, or the empty string otherwise.

  5. If context is currently showing a simple dialog from a call to alert then acknowledge the prompt.

    Otherwise if context is currently showing a simple dialog from a call to confirm, then respond positively if accept is true, or respond negatively if accept is false.

    Otherwise if context is currently showing a simple dialog from a call to prompt, then respond with the string value text if accept is true, or abort if accept is false.

    Otherwise, if context is currently showing a prompt as part of the prompt to unload steps, then confirm the navigation if accept is true, otherwise refuse the navigation.

    Otherwise return error with error code no such alert.

  6. Return success with data null.

6.3.3.6. The browsingContext.navigate Command

The browsingContext.navigate command navigates a browsing context to the given URL.

Command Type
browsingContext.Navigate = {
  method: "browsingContext.navigate",
  params: browsingContext.NavigateParameters
}

browsingContext.NavigateParameters = {
  context: browsingContext.BrowsingContext,
  url: text,
  ? wait: browsingContext.ReadinessState,
}
Result Type
browsingContext.NavigateResult = {
  navigation: browsingContext.Navigation / null,
  url: text,
}
The remote end steps with session and command parameters are:
  1. Let context id be the value of the context field of command parameters.

  2. Let context be the result of trying to get a browsing context with context id.

  3. Assert: context is not null.

  4. Let wait condition be the value of the wait field of command parameters if present, or "none" otherwise.

  5. Let url be the value of the url field of command parameters.

  6. Let document be context’s active document.

  7. Let base be document’s base URL.

  8. Let url record be the result of applying the URL parser to url, with base URL base.

  9. If url record is failure, return error with error code invalid argument.

  10. Let request be a new request whose URL is url record.

  11. Return the result of await a navigation with context, request and wait condition.

6.3.3.7. The browsingContext.print Command

The browsingContext.print command creates a paginated representation of a document, and returns it as a PDF document represented as a Base64-encoded string.

Command Type
browsingContext.Print = {
  method: "browsingContext.print",
  params: browsingContext.PrintParameters
}

browsingContext.PrintParameters = {
  context: browsingContext.BrowsingContext,
  ? background: bool .default false,
  ? margin: browsingContext.PrintMarginParameters,
  ? orientation: ("portrait" / "landscape") .default "portrait",
  ? page: browsingContext.PrintPageParameters,
  ? pageRanges: [*(js-uint / text)],
  ? scale: 0.1..2.0 .default 1.0,
  ? shrinkToFit: bool .default true,
}

browsingContext.PrintMarginParameters = {
  ? bottom: (float .ge 0.0) .default 1.0,
  ? left: (float .ge 0.0) .default 1.0,
  ? right: (float .ge 0.0) .default 1.0,
  ? top: (float .ge 0.0) .default 1.0,
}

browsingContext.PrintPageParameters = {
  ? height: (float .ge 0.0) .default 27.94,
  ? width: (float .ge 0.0) .default 21.59,
}
Result Type
browsingContext.PrintResult = {
  data: text
}

The remote end steps with session and command parameters are:

  1. Let context id be the value of the context field of command parameters.

  2. Let context be the result of trying to get a browsing context with context id.

  3. If the implementation is unable to provide a paginated representation of context for any reason then return error with error code unsupported operation.

  4. Let margin be the value of the margin field of command parameters if present, or otherwise a map matching the browsingContext.PrintMarginParameters with the fields set to their default values.

  5. Let page size be the value of the page field of command parameters if present, or otherwise a map matching the browsingContext.PrintPageParameters with the fields set to their default values.

  6. Let page ranges be the value of the pageRanges field of command parameters if present or an empty list otherwise.

  7. Let document be context’s active document.

  8. Immediately after the next invocation of the run the animation frame callbacks algorithm for document:

    This ought to be integrated into the update rendering algorithm in some more explicit way.

    1. Let pdf data be the result taking UA-specific steps to generate a paginated representation of document, with the CSS media type set to print, encoded as a PDF, with the following paper settings:

      Property Value
      Width in cm page size["width"] if command parameters["orientation"] is "portrait" otherwise page size["height"]
      Height in cm page size["height"] if command parameters["orientation"] is "portrait" otherwise page size["width"]
      Top margin, in cm margin["top"]
      Bottom margin, in cm margin["bottom"]
      Left margin, in cm margin["left"]
      Right margin, in cm margin["right"]

      In addition, the following formatting hints should be applied by the UA:

      If command parameters["scale"] is not equal to 1:
      Zoom the size of the content by a factor command parameters["scale"]
      If command parameters["background"] is false:
      Suppress output of background images
      If command parameters["shrinkToFit"] is true:
      Resize the content to match the page width, overriding any page width specified in the content
    2. If page ranges is not empty, let pages be the result of trying to parse a page range with page ranges and the number of pages contained in pdf data, then remove any pages from pdf data whose one-based index is not contained in pages.

    3. Let encoding result be the result of calling Base64 Encode on pdf data.

    4. Let encoded data be encoding result’s data.

    5. Let body be a map matching the browsingContext.PrintResult production, with the data field set to encoded data.

    6. Return success with data body.

6.3.3.8. The browsingContext.reload Command

The browsingContext.reload command reloads a browsing context.

Command Type
browsingContext.Reload = {
  method: "browsingContext.reload",
  params: browsingContext.ReloadParameters
}

browsingContext.ReloadParameters = {
  context: browsingContext.BrowsingContext,
  ? ignoreCache: bool,
  ? wait: browsingContext.ReadinessState,
}
Return Type
EmptyResult
The remote end steps with command parameters are:
  1. Let context id be the value of the context field of command parameters.

  2. Let context be the result of trying to get a browsing context with context id.

  3. Assert: context is not null.

  4. Let ignore cache be the the value of the ignoreCache field of command parameters if present, or false otherwise.

  5. Let wait condition be the value of the wait field of command parameters if present, or "none" otherwise.

  6. Let document be context’s active document.

  7. Let url be document’s URL.

  8. Let request be a new request whose URL is url.

  9. Return the result of await a navigation with context, request, wait condition, history handling "reload", and ignore cache ignore cache.

  10. Return success with data null.

6.3.4. Events

6.3.4.1. The browsingContext.contextCreated Event
Event Type
 browsingContext.ContextCreated = {
  method: "browsingContext.contextCreated",
  params: browsingContext.Info
}

To Recursively emit context created events given session and context:

  1. Emit a context created event with session and context.

  2. For each child browsing context, child, of context:

    1. Recursively emit context created events given session and child.

To Emit a context created event given session and context:

  1. Let params be the result of get the browsing context info given context, 0, and true.

  2. Let body be a map matching the browsingContext.ContextCreated production, with the params field set to params.

  3. Emit an event with session and body.

The remote end event trigger is:

When the create a new browsing context algorithm is invoked, after the active document of the browsing context is set, run the following steps:

  1. Let context be the newly created browsing context.

  2. Let related browsing contexts be a set containing context.

  3. For each session in the set of sessions for which an event is enabled given "browsingContext.contextCreated" and related browsing contexts:

    1. Emit a context created event given session and context.

The remote end subscribe steps, with subscribe priority 1, given session, contexts and include global are:

  1. For each context in contexts:

    1. Recursively emit context created events given session and context.

6.3.4.2. The browsingContext.contextDestroyed Event
Event Type
 browsingContext.ContextDestroyed = {
  method: "browsingContext.contextDestroyed",
  params: browsingContext.Info
}
The remote end event trigger is:

Define the following browsing context tree discarded steps:

  1. Let context be the browsing context being discarded.

  2. Let params be the result of get the browsing context info, given context, 0, and true.

  3. Let body be a map matching the browsingContext.ContextDestroyed production, with the params field set to params.

  4. Let related browsing contexts be a set containing the result of get the parent browsing context with context, if that is not null, or an empty set otherwise.

  5. For each session in the set of sessions for which an event is enabled given "browsingContext.contextDestroyed" and related browsing contexts:

    1. Emit an event with session and body.

the way this hooks into HTML feels very fragile. See https://github.com/whatwg/html/issues/6194

It’s unclear if we ought to only fire this event for browsing contexts that have active documents; navigation can also cause contexts to become inaccessible but not yet get discarded because bfcache.

6.3.4.3. The browsingContext.navigationStarted Event
Event Type
 browsingContext.NavigationStarted = {
  method: "browsingContext.navigationStarted",
  params: browsingContext.NavigationInfo
}
The remote end event trigger is the WebDriver BiDi navigation started steps given context and navigation status:
  1. Let params be the result of get the navigation info given context and navigation status.

  2. Let body be a map matching the browsingContext.NavigationStarted production, with the params field set to params.

  3. Let navigation id be navigation status’s id.

  4. Let related browsing contexts be a set containing context.

  5. Resume with "navigation started", navigation id, and navigation status.

  6. For each session in the set of sessions for which an event is enabled given "browsingContext.navigationStarted" and related browsing contexts:

    1. Emit an event with session and body.

6.3.4.4. The browsingContext.fragmentNavigated Event
Event Type
 browsingContext.FragmentNavigated = {
  method: "browsingContext.fragmentNavigated",
  params: browsingContext.NavigationInfo
}
The remote end event trigger is the WebDriver BiDi fragment navigated steps given context and navigation status:
  1. Let params be the result of get the navigation info given context and navigation status.

  2. Let body be a map matching the browsingContext.FragmentNavigated production, with the params field set to params.

  3. Let navigation id be navigation status’s id.

  4. Let related browsing contexts be a set containing context.

  5. Resume with "fragment navigated", navigation id, and navigation status.

  6. For each session in the set of sessions for which an event is enabled given "browsingContext.fragmentNavigated" and related browsing contexts:

    1. Emit an event with session and body.

6.3.4.5. The browsingContext.domContentLoaded Event
Event Type
 browsingContext.DomContentLoaded = {
  method: "browsingContext.domContentLoaded",
  params: browsingContext.NavigationInfo
}
The remote end event trigger is the WebDriver BiDi DOM content loaded steps given context and navigation status:
  1. Let params be the result of get the navigation info given context and navigation status.

  2. Let body be a map matching the browsingContext.DomContentLoaded production, with the params field set to params.

  3. Let related browsing contexts be a set containing context.

  4. Let navigation id be navigation status’s id.

  5. Resume with "domContentLoaded", navigation id, and navigation status.

  6. For each session in the set of sessions for which an event is enabled given "browsingContext.domContentLoaded" and related browsing contexts:

    1. Emit an event with session and body.

6.3.4.6. The browsingContext.load Event
Event Type
 browsingContext.Load = {
  method: "browsingContext.load",
  params: browsingContext.NavigationInfo
}
The remote end event trigger is the WebDriver BiDi load complete steps given context and navigation status:
  1. Let params be the result of get the navigation info given context and navigation status.

  2. Let body be a map matching the browsingContext.Load production, with the params field set to params.

  3. Let related browsing contexts be a set containing context.

  4. Let navigation id be navigation status’s id.

  5. Resume with "load", navigation id and navigation status.

  6. For each session in the set of sessions for which an event is enabled given "browsingContext.load" and related browsing contexts:

    1. Emit an event with session and body.

6.3.4.7. The browsingContext.downloadWillBegin Event
Event Type
 browsingContext.DownloadWillBegin = {
  method: "browsingContext.downloadWillBegin",
  params: browsingContext.NavigationInfo
}
The remote end event trigger is the WebDriver BiDi download started steps given context and navigation status:
  1. Let params be the result of get the navigation info given context and navigation status.

  2. Let body be a map matching the browsingContext.DownloadWillBegin production, with the params field set to params.

  3. Let navigation id be navigation status’s id.

  4. Let related browsing contexts be a set containing context.

  5. Resume with "download started", navigation id, and navigation status.

  6. For each session in the set of sessions for which an event is enabled given "browsingContext.downloadWillBegin" and related browsing contexts:

    1. Emit an event with session and body.

6.3.4.8. The browsingContext.navigationAborted Event
Event Type
 browsingContext.NavigationAborted = {
  method: "browsingContext.navigationAborted",
  params: browsingContext.NavigationInfo
}
The remote end event trigger is the WebDriver BiDi navigation aborted steps given context and navigation status:
  1. Let params be the result of get the navigation info given context and navigation status.

  2. Let body be a map matching the browsingContext.NavigationAborted production, with the params field set to params.

  3. Let navigation id be navigation status’s id.

  4. Let related browsing contexts be a set containing context.

  5. Resume with "navigation aborted", navigation id, and navigation status.

  6. For each session in the set of sessions for which an event is enabled given "browsingContext.navigationAborted" and related browsing contexts:

    1. Emit an event with session and body.

6.3.4.9. The browsingContext.navigationFailed Event
Event Type
 browsingContext.NavigationFailed = {
  method: "browsingContext.navigationFailed",
  params: browsingContext.NavigationInfo
}
The remote end event trigger is the WebDriver BiDi navigation failed steps given context and navigation status:
  1. Let params be the result of get the navigation info given context and navigation status.

  2. Let body be a map matching the browsingContext.NavigationFailed production, with the params field set to params.

  3. Let navigation id be navigation status’s id.

  4. Let related browsing contexts be a set containing context.

  5. Resume with "navigation failed", navigation id, and navigation status.

  6. For each session in the set of sessions for which an event is enabled given "browsingContext.navigationFailed" and related browsing contexts:

    1. Emit an event with session and body.

6.3.4.10. The browsingContext.userPromptClosed Event
Event Type
browsingContext.UserPromptClosed = {
  method: "browsingContext.userPromptClosed",
  params: browsingContext.UserPromptClosedParameters
}

browsingContext.UserPromptClosedParameters = {
  context: browsingContext.BrowsingContext,
  accepted: bool,
  ? userText: text
}
The remote end event trigger is the WebDriver BiDi user prompt closed steps given window, accepted and optional user text (default: null).
  1. Let context be window’s browsing context.

  2. Let context id be the browsing context id for context.

  3. Let params be a map matching the browsingContext.UserPromptClosedParameters production with the context field set to context id, the accepted field set to accepted, and the userText field set to user text if user text is not null or omitted otherwise.

  4. Let body be a map matching the BrowsingContextUserPromptClosedEvent production, with the params field set to params.

  5. Let related browsing contexts be a set containing context.

  6. For each session in the set of sessions for which an event is enabled given "browsingContext.userPromptClosed" and related browsing contexts:

    1. Emit an event with session and body.

6.3.4.11. The browsingContext.userPromptOpened Event
Event Type
browsingContext.UserPromptOpened = {
  method: "browsingContext.userPromptOpened",
  params: browsingContext.UserPromptOpenedParameters
}

browsingContext.UserPromptOpenedParameters = {
  context: browsingContext.BrowsingContext,
  type: "alert" / "confirm" / "prompt" / "beforeunload",
  message: text
}
The remote end event trigger is the WebDriver BiDi user prompt opened steps given window, type and message.
  1. Let context be window’s browsing context.

  2. Let context id be the browsing context id for context.

  3. Let params be a map matching the browsingContext.UserPromptOpenedParameters production with the context field set to context id, the type field set to type, and the message field set to message.

  4. Let body be a map matching the browsingContext.UserPromptOpened production, with the params field set to params.

  5. Let related browsing contexts be a set containing context.

  6. For each session in the set of sessions for which an event is enabled given "browsingContext.userPromptOpened" and related browsing contexts:

    1. Emit an event with session and body.

6.4. The network Module

The network module contains commands and events relating to network requests.

6.4.1. Definition

remote end definition

NetworkCommand = (
)

local end definition

NetworkResult = (
)

NetworkEvent = (
    network.BeforeRequestSent //
    network.FetchError //
    network.ResponseStarted //
    network.ResponseCompleted
)

A remote end has a before request sent map which is initially an empty map. It’s used to track the network events for which a network.beforeRequestSent event has already been sent.

6.4.2. Types

6.4.2.1. The network.BaseParameters Type
network.BaseParameters = {
    context: browsingContext.BrowsingContext / null,
    navigation: browsingContext.Navigation / null,
    redirectCount: js-uint,
    request: network.RequestData,
    timestamp: js-uint,
}

The network.BaseParameters type is an abstract type representing the data that’s common to all network events.

Consider including the `sharedId` of the document node that initiated the request in addition to the context.

To get the base network event data given request and redirect count:
  1. Let request data be the result of get the request data with request.

  2. Let navigation be request’s navigation id.

  3. Let context id be null.

  4. If request’s client is not null, let related browsing contexts be the result of get related browsing contexts with request’s client. Otherwise let related browsing contexts be an empty set.

  5. If request’s window is an environment settings object:

    1. Let environment settings be request’s window

    2. If there is a browsing context whose active window is environment settingsglobal object, let context id be the browsing context id for that context.

  6. Let timestamp be a time value representing the current date and time in UTC.

  7. Let params be map matching the network.BaseParameters production, with the request field set to request data, the navigation field set to navigation, the context field set to context id, the timestamp field set to timestamp, and the redirectCount field set to redirect count.

  8. Return (related browsing contexts, params)

Remote end definition and local end definition

network.Cookie = {
    name: text,
    ? value: text,
    ? binaryValue: [ uint ]
    domain: text,
    path: text,
    ? expires: js-uint,
    size: js-uint,
    httpOnly: bool,
    secure: bool,
    sameSite: "strict" / "lax" / "none",
};

The network.Cookie type represents a cookie.

If the cookie value can be represented as a UTF-8 encoded string, the value field will be present. Otherwise the binaryValue field will be present and consist of an array of integers representing the bytes of the cookie value.

To get a cookie given stored cookie:

Note: The definitions of stored cookie’s fields are from [COOKIES], except samesite-flag, which is from [SAME-SITE-COOKIES].

  1. Let name be the result of UTF-8 decode with stored cookie’s name field.

  2. Let utf8 decoded value be the result of UTF-8 decode without BOM or fail with stored cookie’s value.

  3. If utf8 decoded value is failure, then:

    1. Let value be null and binary value be an empty list.

    2. For each byte in stored cookie’s value:

      1. Append the value of byte to binary value.

    Otherwise: Let value be utf8 decoded value and binary value be null.

  4. Let domain be stored cookie’s domain field.

  5. Let path be stored cookie’s path field.

  6. Let expires be stored cookie’s expires field represented as a unix timestamp, if set, or null otherwise.

  7. Let size be the byte length of the result of serializing stored cookie as it would be represented in a Cookie header.

  8. Let http only be true if stored cookie’s http-only-flag is true, or false otherwise.

  9. Let secure be true if stored cookie’s secure-only-flag is true, or false otherwise.

  10. Let same site be "none" if stored cookie’s samesite-flag is "None", "lax" if it is "Lax, or "strict" if it is "Strict".

  11. Return a map matching the network.Cookie production, with the name field set to name, the value field set to value if it’s not null or omitted otherwise, the binaryValue field set to binary value if it’s not null or omitted otherwise, the domain field set to domain, the path field set to path, the expires field set to expires if it’s not null, or omitted otherwise, the size field set to size, the httpOnly field set to http only, the secure field set to secure, and the sameSite field set to same site.

6.4.2.3. The network.FetchTimingInfo Type

Remote end definition and local end definition

network.FetchTimingInfo = {
    timeOrigin: float,
    requestTime: float,
    redirectStart: float,
    redirectEnd: float,
    fetchStart: float,
    dnsStart: float,
    dnsEnd: float,
    connectStart: float,
    connectEnd: float,
    tlsStart: float,
    requestStart: float,
    responseStart: float,
    responseEnd: float,
};

The network.FetchTimingInfo type represents the time of each part of the request, relative to the time origin of the request's client.

To get the fetch timings given request:
  1. Let global be request’s client.

  2. If global is null, return a map matching the network.FetchTimingInfo production, with all fields set to 0.

  3. Let time origin be get time origin timestamp with global.

  4. Let timings be request’s fetch timing info.

  5. Let connection timing be timingsfinal connection timing info if it’s not null, or a new connection timing info otherwise.

  6. Let request time be convert fetch timestamp given timingsstart time and global.

  7. Let redirect start be convert fetch timestamp given timingsredirect start time and global.

  8. Let redirect end be convert fetch timestamp given timingsredirect end time and global.

  9. Let fetch start be convert fetch timestamp given timingspost-redirect start time and global.

  10. Let DNS start be convert fetch timestamp given connection timing’s domain lookup start time and global.

  11. Let DNS end be convert fetch timestamp given connection timing’s domain lookup end time and global.

  12. Let TLS start be convert fetch timestamp given connection timing’s secure connection start time and global.

  13. Let connect start be convert fetch timestamp given connection timing’s connection start time and global.

  14. Let connect end be convert fetch timestamp given connection timing’s connection end time and global.

  15. Let request start be convert fetch timestamp given timingsfinal network-request start time and global.

  16. Let response start be convert fetch timestamp given timingsfinal network-response start time and global.

  17. Let response end be convert fetch timestamp given timingsend time and global.

  18. Return a map matching the network.FetchTimingInfo production with the timeOrigin field set to time origin, the requestTime field set to request time, the redirectStart field set to redirect start, the redirectEnd field set to redirect end, the fetchStart field set to fetch start, the dnsStart field set to DNS start, the dnsEnd field set to DNS end, the connectStart field set to connect start, the connectEnd field set to connect end, the tlsStart field set to TLS start, the requestStart field set to request start, the responseStart field set to response start, and the responseEnd field set to response end.

TODO: Add service worker fields

6.4.2.4. The network.Header Type

Remote end definition and local end definition

network.Header = {
    name: text,
    ? value: text,
    ? binaryValue: [ uint ]
};

The network.Header type represents a single request header.

If the header value can be represented as a UTF-8 encoded string, the value field will be present. Otherwise the binaryValue field will be present and consist of an array of integers representing the bytes of the header.

To get a header given name bytes and value bytes:
  1. Let name be the result of UTF-8 decode with name bytes.

    Assert: Since header names are constrained to be ASCII-only this cannot fail.

  2. Let utf8 decoded value be the result of UTF-8 decode without BOM or fail with value bytes.

  3. If utf8 decoded value is failure, then:

    1. Let value be null and binary value be an empty list.

    2. For each byte in value bytes:

      1. Append the value of byte to binary value.

    Otherwise: let value be utf8 decoded value and let binary value be null.

  4. Return a map matching the network.Header production, with the name field set to name, the value field set to value if it’s not null, or omitted otherwise, and the binaryValue field set to binary value if it’s not null, or omitted otherwise.

6.4.2.5. The network.Initiator Type

Remote end definition and local end definition

network.Initiator = {
    type: "parser" / "script" / "preflight" / "other",
    ? columnNumber: js-uint,
    ? lineNumber: js-uint,
    ? stackTrace: script.StackTrace,
    ? request: network.Request
};

The network.Initiatior type represents the source of a network request.

To get the initiator given request:
  1. Let request id be request’s request id.

  2. Let type be "other".

  3. If request is a CORS-Preflight Request, set type to "preflight".

  4. TODO: Get the type. It’s not quite clear how this ought to work; the CDP data depends on whether the navigation was kicked off by the parser or by script (so e.g. inserting an image from script causes the initiator to be "script"), but that doesn’t correspond to anything in Fetch.

  5. If request’s initiator type is "fetch" or "xmlhttprequest":

    1. Let stack trace be the current stack trace.

    2. If stack trace has size of 1 or greater, let line number be value of the lineNumber field in stack trace[0], and let column number be the value of the columnNumber field stack trace[0]. Otherwise let line number and column number be 0.

    Otherwise, let stack trace, column number, and line number all be null.

    TODO: Chrome includes the current parser position as column number / line number for parser-inserted resources.

  6. Return a map matching the network.Initiator production, with the type field set to type, the columnNumber field set to column number if it’s not null, or omitted otherwise, the lineNumber field set to line number if it’s not null, or omitted otherwise, the stackTrace field set to stack trace if it’s not null, or omitted otherwise, and the request field set to request id.

6.4.2.6. The network.Request Type

Remote end definition and local end definition

network.Request = text;

Each network request has an associated request id, which is a string uniquely identifying that request. The identifier for a request resulting from a redirect matches that of the request that initiated it.

6.4.2.7. The network.RequestData Type

Remote end definition and local end definition

network.RequestData = {
    request: network.Request,
    url: text,
    method: text,
    headers: [*network.Header],
    cookies: [*network.Cookie],
    headersSize: js-uint,
    bodySize: js-uint / null,
    timings: network.FetchTimingInfo,
};

The network.RequestData type represents an ongoing network request.

To get the request data given request:

  1. Let request id be request’s request id.

  2. Let url be the result of running the URL serializer with request’s URL.

  3. Let method be request’s method.

  4. Let body size be null.

  5. Let body be request’s body.

  6. If body is a byte sequence, let body size be the length of that sequence. Otherwise, if body is a body then let body size be that body’s length.

  7. Let headers size be the size in bytes of request’s headers list when serialized as mandated by [HTTP11].

    Note: For protocols which allow header compression, this is the compressed size of the headers, as sent over the network.

  8. Let headers be an empty list.

  9. Let cookies be an empty list.

  10. For each (name, value) in request’s headers list:

    1. Append the result of get a header with name and value to headers.

    2. If name is a byte-case-insensitive match for "Cookie" then:

      1. For each cookie in the user agent’s cookie store that are included in request:

        Note: [COOKIES] defines some baseline requirements for which cookies in the store can be included in a request, but user agents are free to impose additional constraints.

        1. Append the result of get a cookie given cookie to cookies.

  11. Let timings be get the fetch timings with request.

  12. Return a map matching the network.RequestData production, with the request field set to request id, url field set to url, the method field set to method, the headers field set to headers, the cookies field set to cookies, and the headersSize field set to headers size, the bodySize field set to body size, and the timings field set to timings.

6.4.2.8. The network.ResponseContent Type

Remote end definition and local end definition

network.ResponseContent = {
    size: js-uint
};

The network.ResponseContent type represents the decoded response to a network request.

To get the response content info given response.
  1. Return a new map matching the network.ResponseContent production, with the size field set to response’s response body info's decoded size

6.4.2.9. The network.ResponseData Type

Remote end definition and local end definition

network.ResponseData = {
    url: text,
    protocol: text,
    status: js-uint,
    statusText: text,
    fromCache: bool,
    headers: [*network.Header],
    mimeType: text,
    bytesReceived: js-uint,
    headersSize: js-uint / null,
    bodySize: js-uint / null,
    content: network.ResponseContent
};

The network.ResponseData type represents the response to a network request.

To get the protocol given response:

  1. Let protocol be the empty string.

  2. If response’s final connection timing info is not null, set protocol to response’s final connection timing info's ALPN negotiated protocol.

  3. If protocol is the empty string, or is equal to "unknown":

    1. Set protocol to response’s url's scheme

    2. If protocol is equal to either "http" or "https" and response has an associated HTTP Response.

      Note: [FETCH] isn’t clear about the relation between a HTTP network response and a response object.

      1. Let http version be the HTTP Response’s Status line’s HTTP-version [HTTP11].

      2. If http version starts with "HTTP/":

        1. Let version be the code unit substring of http version from 5 to http version’s length.

        2. If version is "0.9", set protocol to "http/0.9", otherwise if version is "1.0", set protocol to "http/1.0", otherwise if version is "1.1", set protocol to "http/1.1".

  4. Return protocol.

To get the response data given response:
  1. Let url be the result of running the URL serializer with response’s URL.

  2. Set protocol to get the protocol given response.

  3. Let status be response’s status.

  4. Let status text be response’s status message.

  5. If response’s cache state is "local", let from cache be true, otherwise let it be false.

  6. Let headers be an empty list.

  7. Let mime type be the essence of the computed mime type for response.

    Note: this is whatever MIME type the browser is actually using, even if it isn’t following the exact algorithm in the [MIMESNIFF] specification.

  8. For each (name, value) in response’s headers list:

    1. Append the result of get a header with name and value to headers.

  9. Let bytes received be the total number of bytes transmitted as part of the HTTP response associated with response.

  10. Let headers size be the number of bytes transmitted as part of the header fields section of the HTTP response.

  11. Let body size be response’s response body info's encoded size.

  12. Let content be the result of get the response content info with response.

  13. Return a map matching the network.ResponseData production, with the url field set to url, the protocol field set to protocol, the status field set to status, the statusText field set to status text, the fromCache field set to from cache, the headers field set to headers, the mimeType field set to mime type, the bytesReceived field set to bytes received, the headersSize field set to headers size, the bodySize field set to body size, and the content field set to content.

6.4.3. Events

6.4.3.1. The network.beforeRequestSent Event
Event Type
 network.BeforeRequestSent = {
  method: "network.beforeRequestSent",
  params: network.BeforeRequestSentParameters
}

network.BeforeRequestSentParameters = {
  network.BaseParameters,
  initiator: network.Initiator,
}

This event is emitted before a request is sent (either over the network or before it’s handled by a serviceworker or a local cache).

The remote end event trigger is the WebDriver BiDi before request sent steps given request:
  1. If before request sent map does not contain request, set before request sent map[request] to a new set.

  2. Let redirect count be request’s redirect count.

  3. Add redirect count to before request sent map[request].

  4. Let (related browsing contexts, params) be the result of get the base network event data with request, and redirect count.

  5. Let initiator be the result of get the initiator with request.

  6. Set the initiator field of params to initiator.

  7. Assert: params matches the network.BeforeRequestSentParameters production.

  8. Let body be a map matching the network.BeforeRequestSent production, with the params field set to params.

  9. Emit events with body and related browsing contexts.

6.4.3.2. The network.fetchError Event
Event Type
 network.FetchError = {
  method: "network.fetchError",
  params: network.FetchErrorParameters
}

network.FetchErrorParameters = {
  network.BaseParameters,
  errorText: text,
}

This event is emitted when a network request ends in an error.

The remote end event trigger is the WebDriver BiDi fetch error steps given request:

  1. If before request sent map[request] does not contain request’s redirect count, then run the WebDriver BiDi before request sent steps with request.

    Note: This ensures that a network.beforeRequestSent can always be emitted before a network.fetchError, without the caller needing to explicitly invoke the WebDriver BiDi before request sent steps on every error path.

  2. Let (related browsing contexts, params) be the result of get the base network event data given request.

  3. TODO: Set the errorText field of params.

  4. Assert: params matches the network.FetchErrorParameters production.

  5. Let body be a map matching the network.FetchError production, with the params field set to params.

  6. Emit events with body and related browsing contexts.

6.4.3.3. The network.responseCompleted Event
Event Type
 network.ResponseCompleted = {
  method: "network.responseCompleted",
  params: network.ResponseCompletedParameters
}

network.ResponseCompletedParameters = {
  network.BaseParameters,
  response: network.ResponseData,
}

This event is emitted after the full response body is received.

The remote end event trigger is the WebDriver BiDi response completed steps given request and response:
  1. Let redirect count be request’s redirect count.

  2. Assert: before request sent map[request] contains redirect count.

    Note: This implies that every caller needs to ensure that the WebDriver BiDi before request sent steps are invoked with request before these steps.

  3. Let (related browsing contexts, params) be the result of get the base network event data given request and redirect count.

  4. Let response data be the result of get the response data with response.

  5. Set the response field of params to response data.

  6. Assert: params matches the network.ResponseCompletedParameters production.

  7. Let body be a map matching the network.ResponseCompleted production, with the params field set to params.

  8. Emit events with body and related browsing contexts.

6.4.3.4. The network.responseStarted Event
Event Type
 network.ResponseStarted = {
  method: "network.responseStarted",
  params: network.ResponseStartedParameters
}

network.ResponseStartedParameters = {
  network.BaseParameters,
  response: network.ResponseData,
}

This event is emitted after the response headers are received but before the body is complete.

The remote end event trigger is the WebDriver BiDi response started steps given request and response:
  1. Let redirect count be request’s redirect count.

  2. Assert: before request sent map[request] is equal to redirect count.

    Note: This implies that every caller needs to ensure that the WebDriver BiDi before request sent steps are invoked with request before these steps.

  3. Let (related browsing contexts, params) be the result of get the base network event data with request and redirect count.

  4. Let response data be the result of get the response data with response.

  5. Set the response field of params to response data.

  6. Assert: params matches the network.ResponseStartedParameters production.

  7. Let body be a map matching the network.ResponseStarted production, with the params field set to params.

  8. Emit events with body and related browsing contexts.

6.5. The script Module

The script module contains commands and events relating to script realms and execution.

6.5.1. Definition

Remote end definition

ScriptCommand = (
  script.AddPreloadScriptCommand //
  script.CallFunction //
  script.Disown //
  script.Evaluate //
  script.GetRealms //
  script.RemovePreloadScriptCommand
)

local end definition

ScriptResult = (
  script.AddPreloadScriptResult //
  script.EvaluateResult //
  script.GetRealmsResult
)

ScriptEvent = (
  script.RealmCreated //
  script.RealmDestroyed
)

6.5.2. Preload Scripts

A Preload script is one which runs on creation of a new Window, before any author-defined script have run.

TODO: Extend this to scripts in other kinds of realms.

A BiDi session has a preload script map which is a map in which the keys are UUIDs, and the values are structs with an item named function declaration, which is a string, arguments, and an item named sandbox which is a string or null.

Note: If executing a preload script fails, either due to a syntax error, or a runtime exception, an [ECMAScript] exception is reported in the realm in which it was being executed, and other preload scripts run as normal.

To run WebDriver BiDi preload scripts given environment settings:
  1. Let document be environment settingsrelevant global object's associated Document.

  2. Let browsing context be document’s browsing context.

  3. For each session in active BiDi sessions:

    1. For each preload script in session’s preload script map's values:

      1. If preload script’s sandbox is not null, let realm be get or create a sandbox realm with preload script’s sandbox and browsing context. Otherwise let realm be environment settingsrealm execution context's Realm component.

      2. Let arguments be preload script’s arguments.

      3. Let deserialized arguments be an empty list.

      4. For each argument in arguments:

        1. Let channel be create a channel with session, realm and argument.

        2. Append channel to deserialized arguments.

      5. Let base URL be the API base URL of environment settings.

      6. Let options be the default classic script fetch options.

      7. Let function declaration be preload script’s function declaration.

      8. Let (script, function body evaluation status) be the result of evaluate function body with function declaration, environment settings, base URL, and options.

      9. If function body evaluation status is an abrupt completion, then report the exception given by function body evaluation status.[[Value]] for script.

      10. Let function object be function body evaluation status.[[Value]].

      11. If IsCallable(function object) is false:

        1. Let error be a new TypeError object in realm.

        2. Report the exception error.

      12. Prepare to run script with environment settings.

      13. Set evaluation status to Call(function object, null, deserialized arguments).

      14. Clean up after running script with environment settings.

      15. If evaluation status is an abrupt completion, then report the exception given by evaluation status.[[Value]] for script.

6.5.3. Types

6.5.3.1. The script.Channel Type

Remote end definition and local end definition

script.Channel = text;

The script.Channel type represents the id of a specific channel used to send custom messages from the remote end to the local end.

6.5.3.2. The script.ChannelValue Type

Remote end definition

script.ChannelValue = {
  type: "channel",
  value: script.ChannelProperties,
};

script.ChannelProperties = {
  channel: script.Channel,
  ? serializationOptions: script.SerializationOptions,
  ? ownership: script.ResultOwnership,
}

The script.ChannelValue type represents an ArgumentValue that can be deserialized into a function that sends messages from the remote end to the local end.

To create a channel given session, realm and protocol value:

  1. Let channel properties be protocol value["value"].

  2. Let steps be the following steps given the argument message:

    1. Let current realm be the current Realm Record.

    2. Emit a script message with session, current realm, channel properties and message.

  3. Return CreateBuiltinFunction(steps, 1, "", « », realm).

6.5.3.3. The script.EvaluateResult Type

Remote end definition and local end definition

script.EvaluateResult = (
  script.EvaluateResultSuccess //
  script.EvaluateResultException
)

script.EvaluateResultSuccess = {
  type: "success",
  result: script.RemoteValue,
  realm: script.Realm
}

script.EvaluateResultException = {
  type: "exception",
  exceptionDetails: script.ExceptionDetails
  realm: script.Realm
}

The script.EvaluateResult type indicates the return value of a command that executes script. The script.EvaluateResultSuccess variant is used in cases where the script completes normally and the script.EvaluateResultException variant is used in cases where the script completes with a thrown exception.

6.5.3.4. The script.ExceptionDetails Type

Remote end definition and local end definition

script.ExceptionDetails = {
  columnNumber: js-uint,
  exception: script.RemoteValue,
  lineNumber: js-uint,
  stackTrace: script.StackTrace,
  text: text,
};

The script.ExceptionDetails type represents a JavaScript exception.

To get exception details given a realm, a completion record record, an ownership type and a session:

  1. Assert: record.[[Type]] is throw.

  2. Let text be an implementation-defined textual description of the error represented by record.

    TODO: Tighten up the requirements here; people will probably try to parse this data with regex or something equally bad.

  3. Let serialization options be a map matching the script.SerializationOptions production with the fields set to their default values.

  4. Let exception be the result of serialize as a remote value with record.[[Value]], serialization options, ownership type, a new map as serialization internal map, realm and session.

  5. Let stack trace be the stack trace for an exception given record.

  6. If stack trace has size of 1 or greater, let line number be value of the lineNumber field in stack trace[0], and let column number be the value of the columnNumber field stack trace[0]. Otherwise let line number and column number be 0.

  7. Let exception details be a map matching the script.ExceptionDetails production, with the text field set to text, the exception field set to exception, the lineNumber field set to line number, the columnNumber field set to column number, and the stackTrace field set to stack trace.

  8. Return exception details.

6.5.3.5. The script.Handle Type

Remote end definition and local end definition

script.Handle = text;

The script.Handle type represents a handle to an object owned by the ECMAScript runtime. The handle is only valid in a specific Realm.

Each ECMAScript Realm has a corresponding handle object map. This is a strong map from handle ids to their corresponding objects.

6.5.3.6. The script.LocalValue Type

Remote end definition and local end definition

script.LocalValue = (
  script.PrimitiveProtocolValue //
  script.ArrayLocalValue //
  script.DateLocalValue //
  script.MapLocalValue //
  script.ObjectLocalValue //
  script.RegExpLocalValue //
  script.SetLocalValue
)

script.ListLocalValue = [*script.LocalValue];

script.ArrayLocalValue = {
  type: "array",
  value: script.ListLocalValue,
}

script.DateLocalValue = {
  type: "date",
  value: text
}

script.MappingLocalValue = [*[(script.LocalValue / text), script.LocalValue]];

script.MapLocalValue = {
  type: "map",
  value: script.MappingLocalValue,
}

script.ObjectLocalValue = {
  type: "object",
  value: script.MappingLocalValue,
}

script.RegExpValue = {
  pattern: text,
  ? flags: text,
}

script.RegExpLocalValue = {
  type: "regexp",
  value: script.RegExpValue,
}

script.SetLocalValue = {
  type: "set",
  value: script.ListLocalValue,
}

The script.LocalValue type represents both primitive and non-primitive values which can be deserialized to ECMAScript without reference to existing objects.

To deserialize key-value list given serialized key-value list, realm and session:

  1. Let deserialized key-value list be a new list.

  2. For each serialized key-value in the serialized key-value list:

    1. If size of serialized key-value is not 2, return error with error code invalid argument.

    2. Let serialized key be serialized key-value[0].

    3. If serialized key is a string, let deserialized key be serialized key.

    4. Otherwise let deserialized key be result of trying to given deserialize local value with serialized key, realm and session.

    5. Let serialized value be serialized key-value[1].

    6. Let deserialized value be result of trying to deserialize local value given serialized value, realm and session.

    7. Append CreateArrayFromListdeserialized key, deserialized value») to deserialized key-value list.

  3. Return success with data CreateArrayFromList(deserialized key-value list).

TODO distinguish between `List` and `Array`.

To deserialize value list given serialized value list, realm and session:

  1. Let deserialized values be a new list.

  2. For each serialized value in the serialized value list:

    1. Let deserialized value be result of trying to deserialize local value given serialized value, realm and session.

    2. Append deserialized value to deserialized values;

  3. Return success with data deserialized values.

To deserialize local value given local protocol value, realm and session:

  1. If local protocol value matches the script.RemoteReference production, return deserialize remote reference of given local protocol value, realm and session.

  2. If local protocol value matches the script.PrimitiveProtocolValue production, return deserialize primitive protocol value with local protocol value.

  3. If local protocol value matches the script.ChannelValue production, return create a channel with session, realm and local protocol value.

  4. Let type be the value of the type field of local protocol value or undefined if no such a field.

  5. Let value be the value of the value field of local protocol value or undefined if no such a field.

  6. In the following list of conditions and associated steps, run the first set of steps for which the associated condition is true:

    type is the string "array"
    1. Let deserialized value list be a result of trying to deserialize value list given value, realm and session.

    2. Return success with data CreateArrayFromList(deserialized value list).

    type is the string "date"
    1. If value does not match Date Time String Format, return error with error code invalid argument.

    2. Let date result be Construct(Date, value).

    3. Assert: date result is not an abrupt completion.

    4. Return success with data date result.

    type is the string "map"
    1. Let deserialized key-value list be a result of trying to deserialize key-value list with value, realm and session.

    2. Let iterable be CreateArrayFromList(deserialized key-value list)

    3. Return success with data Map(iterable).

    type is the string "object"
    1. Let deserialized key-value list be a result of trying to deserialize key-value list with value, realm and session.

    2. Let iterable be CreateArrayFromList(deserialized key-value list)

    3. Return success with data Object.fromEntries(iterable).

    type is the string "regexp"
    1. Let pattern be the value of the pattern field of local protocol value.

    2. Let flags be the value of the flags field of local protocol value or undefined if no such a field.

    3. Let regex_result be Regexp(pattern, flags). If this throws exception, return error with error code invalid argument.

    4. Return success with data regex_result.

    type is the string "set"
    1. Let deserialized value list be a result of trying to deserialize value list given value, realm and session.

    2. Let iterable be CreateArrayFromList(deserialized key-value list)

    3. Return success with data Set object(iterable).

    otherwise
    Return error with error code invalid argument.
6.5.3.7. The script.PreloadScript Type

Remote end definition and local end definition

script.PreloadScript = text;

The script.PreloadScript type represents a handle to a script that will run on realm creation.

6.5.3.8. The script.Realm Type

Remote end definition and local end definition

script.Realm = text;

Each realm has an associated realm id, which is a string uniquely identifying that realm. This is implicitly set when the realm is created.

The realm id for a realm is opaque and must not be derivable from the handle id of the corresponding global object in the handle object map or, where relevant, from the browsing context id of any browsing context.

Note: this is to ensure that users do not rely on implementation-specific relationships between different ids.

To get a realm given realm id:
  1. If realm id is null, return success with data null.

  2. If there is no realm with id realm id return error with error code no such frame

  3. Let realm be the realm with id realm id.

  4. Return success with data realm

This has the wrong error code

6.5.3.9. The script.PrimitiveProtocolValue Type

Remote end definition and local end definition

script.PrimitiveProtocolValue = (
  script.UndefinedValue //
  script.NullValue //
  script.StringValue //
  script.NumberValue //
  script.BooleanValue //
  script.BigIntValue
)

script.UndefinedValue = {
  type: "undefined",
}

script.NullValue = {
  type: "null",
}

script.StringValue = {
  type: "string",
  value: text,
}

script.SpecialNumber = "NaN" / "-0" / "Infinity" / "-Infinity";

script.NumberValue = {
  type: "number",
  value: number / script.SpecialNumber,
}

script.BooleanValue = {
  type: "boolean",
  value: bool,
}

script.BigIntValue = {
  type: "bigint",
  value: text,
}

The script.PrimitiveProtocolValue represents values which can only be represented by value, never by reference.

To serialize primitive protocol value given a value:

  1. Let remote value be undefined.

  2. In the following list of conditions and associated steps, run the first set of steps for which the associated condition is true, if any:

    Type(value) is undefined
    Let remote value be a map matching the script.UndefinedValue production in the local end definition.
    Type(value) is Null
    Let remote value be a map matching the script.NullValue production in the local end definition.
    Type(value) is String
    Let remote value be a map matching the script.StringValue production in the local end definition, with the value property set to value.

    This doesn’t handle lone surrogates

    Type(value) is Number
    1. Switch on the value of value:

      NaN
      Let serialized be "NaN"
      -0
      Let serialized be "-0"
      Infinity
      Let serialized be "Infinity"
      -Infinity
      Let serialized be "-Infinity"
      Otherwise:
      Let serialized be value
    2. Let remote value be a map matching the script.NumberValue production in the local end definition, with the value property set to serialized.

    Type(value) is Boolean
    Let remote value be a map matching the script.BooleanValue production in the local end definition, with the value property set to value.
    Type(value) is BigInt
    Let remote value be a map matching the script.BigIntValue production in the local end definition, with the value property set to the result of running the ToString operation on value.
  3. Return remote value

To deserialize primitive protocol value given a primitive protocol value:

  1. Let type be the value of the type field of primitive protocol value.

  2. Let value be undefined.

  3. If primitive protocol value has field value:

    1. Let value be the value of the value field of primitive protocol value.

  4. In the following list of conditions and associated steps, run the first set of steps for which the associated condition is true:

    type is the string "undefined"
    Return success with data undefined.
    type is the string "null"
    Return success with data null.
    type is the string "string"
    Return success with data value.
    type is the string "number"
    1. If Type(value) is Number, return success with data value.

    2. Assert: Type(value) is String.

    3. If value is the string "NaN", return success with data NaN.

    4. Let number_result be StringToNumber(value).

    5. If number_result is NaN, return error with error code invalid argument

    6. Return success with data number_result.

    type is the string "boolean"
    Return success with data value.
    type is the string "bigint"
    1. Let bigint_result be StringToBigInt(value).

    2. If bigint_result is undefined, return error with error code invalid argument

    3. Return success with data bigint_result.

  5. Return error with error code invalid argument

6.5.3.10. The script.RealmInfo Type

Local end definition

script.RealmInfo = (
  script.WindowRealmInfo //
  script.DedicatedWorkerRealmInfo //
  script.SharedWorkerRealmInfo //
  script.ServiceWorkerRealmInfo //
  script.WorkerRealmInfo //
  script.PaintWorkletRealmInfo //
  script.AudioWorkletRealmInfo //
  script.WorkletRealmInfo
)

script.BaseRealmInfo = {
  realm: script.Realm,
  origin: text
}

script.WindowRealmInfo = {
  script.BaseRealmInfo,
  type: "window",
  context: browsingContext.BrowsingContext,
  ? sandbox: text
}

script.DedicatedWorkerRealmInfo = {
  script.BaseRealmInfo,
  type: "dedicated-worker"
}

script.SharedWorkerRealmInfo = {
  script.BaseRealmInfo,
  type: "shared-worker"
}

script.ServiceWorkerRealmInfo = {
  script.BaseRealmInfo,
  type: "service-worker"
}

script.WorkerRealmInfo = {
  script.BaseRealmInfo,
  type: "worker"
}

script.PaintWorkletRealmInfo = {
  script.BaseRealmInfo,
  type: "paint-worklet"
}

script.AudioWorkletRealmInfo = {
  script.BaseRealmInfo,
  type: "audio-worklet"
}

script.WorkletRealmInfo = {
  script.BaseRealmInfo,
  type: "worklet"
}

Note: there’s a 1:1 relationship between the script.RealmInfo variants and values of script.RealmType.

The script.RealmInfo type represents the properties of a realm.

To get the browsing context with given realm:
  1. Let global object be the global object of the realm.

  2. Let global object be the unwrapped global object.

  3. If global object is not a Window object, return null.

  4. Let document be global object’s wrapped Window's associated Document.

  5. Return document’s browsing context.

To get the realm info given environment settings:
  1. Let realm be environment settingsrealm execution context's Realm component.

  2. Let realm id be the realm id for realm.

  3. Let origin be the serialization of an origin given environment settings’s origin.

  4. Let global object be the global object specified by environment settings

  5. Run the steps under the first matching condition:

    global object is a Window object
    1. Let document be environment settingsrelevant global object's associated Document.

    2. Let browsing context be document’s browsing context.

    3. Let context id be the browsing context id for browsing context.

    4. Let realm info be a map matching the script.WindowRealmInfo production, with the realm field set to realm id, the origin field set to origin, and the context field set to context id.

    global object is SandboxWindowProxy object
    TODO: Unclear if this is the right formulation for handling sandboxes.
    1. Let document be global object’s wrapped Window's associated Document.

    2. Let browsing context be document’s browsing context.

    3. Let context id be the browsing context id for browsing context.

    4. Let sandbox name be the result of get a sandbox name given realm.

    5. Assert: sandbox name is not null.

    6. Let realm info be a map matching the script.WindowRealmInfo production, with the realm field set to realm id, the origin field set to origin, the context field set to context id, and the sandbox field set to sandbox name.

    global object is a DedicatedWorkerGlobalScope object
    1. Let realm info be a map matching the script.DedicatedWorkerRealmInfo production, with the realm field set to realm id, and the origin field set to origin.

    global object is a SharedWorkerGlobalScope object
    1. Let realm info be a map matching the script.SharedWorkerRealmInfo production, with the realm field set to realm id, and the origin field set to origin.

    global object is a ServiceWorkerGlobalScope object
    1. Let realm info be a map matching the script.ServiceWorkerRealmInfo production, with the realm field set to realm id, and the origin field set to origin.

    global object is a WorkerGlobalScope object
    1. Let realm info be a map matching the script.WorkerRealmInfo production, with the realm field set to realm id, and the origin field set to origin.

    global object is a PaintWorkletGlobalScope object
    1. Let realm info be a map matching the script.PaintWorkletRealmInfo production, with the realm field set to realm id, and the origin field set to origin.

    global object is a AudioWorkletGlobalScope object
    1. Let realm info be a map matching the script.AudioWorkletRealmInfo production, with the realm field set to realm id, and the origin field set to origin.

    global object is a WorkletGlobalScope object
    1. Let realm info be a map matching the script.WorkletRealmInfo production, with the realm field set to realm id, and the origin field set to origin.

    Otherwise:
    1. Let realm info be null.

  6. Return realm info

Note: Future variations of this specification will retain the invariant that the last component of the type name after splitting on "-" will always be "worker" for globals implementing WorkerGlobalScope, and "worklet" for globals implementing WorkletGlobalScope.

6.5.3.11. The script.RealmType Type

Remote end definition and local end definition

script.RealmType = "window" / "dedicated-worker" / "shared-worker" / "service-worker" /
                   "worker" / "paint-worklet" / "audio-worklet" / "worklet"

The script.RealmType type represents the different types of Realm.

6.5.3.12. The script.RemoteReference Type

Remote end definition

script.RemoteReference = (
  script.SharedReference //
  script.RemoteObjectReference
)

script.SharedReference = {
   sharedId: script.SharedId
   ? handle: script.Handle,
   Extensible
}

script.RemoteObjectReference = {
   handle: script.Handle,
   ? sharedId: script.SharedId
   Extensible
}

The script.RemoteReference type is either a script.RemoteObjectReference representing a remote reference to an existing ECMAScript object in handle object map in the given Realm, or is a script.SharedReference representing a reference to a node.

handle "stale object reference" case.

Note: if the provided reference has both handle and sharedId, the algorithm will ignore handle and respect only sharedId.

To deserialize remote reference given remote reference, realm and session:
  1. Assert remote reference matches the script.RemoteReference production.

  2. If remote reference matches the script.SharedReference production, return deserialize shared reference with remote reference, realm and session.

  3. Return deserialize remote object reference with remote reference and realm.

To deserialize remote object reference given remote object reference and realm:
  1. Let handle id be the value of the handle field of remote object reference.

  2. Let handle map be realm’s handle object map

  3. If handle map does not contain handle id, then return error with error code no such handle.

  4. Return success with data handle map[handle id].

To deserialize shared reference given shared reference, realm and session:
  1. Assert shared reference matches the script.SharedReference production.

  2. Let browsing context be get the browsing context with realm.

  3. If browsing context is null, return error with error code no such node.

    Note: This happens when the realm isn’t a Window global.

  4. Let shared id be the value of the sharedId field of shared reference.

  5. Let node be result of trying to get a node with session, browsing context and shared id.

  6. If node is null, return error with error code no such node.

  7. Let environment settings be the environment settings object whose realm execution context's Realm component is realm.

  8. If node’s node document's origin is not same origin domain with environment settings’s origin then return error with error code no such node.

    Note: This ensures that WebDriver-BiDi can not be used to pass objects between realms that do not otherwise permit script access.

  9. Let realm global object be the global object of the realm.

  10. If the realm global object is SandboxWindowProxy object, set node to the SandboxProxy wrapping node in the realm.

  11. Return success with data node.

6.5.3.13. The script.RemoteValue Type

Remote end definition and local end definition

script.RemoteValue = (
  script.PrimitiveProtocolValue //
  script.SymbolRemoteValue //
  script.ArrayRemoteValue //
  script.ObjectRemoteValue //
  script.FunctionRemoteValue //
  script.RegExpRemoteValue //
  script.DateRemoteValue //
  script.MapRemoteValue //
  script.SetRemoteValue //
  script.WeakMapRemoteValue //
  script.WeakSetRemoteValue //
  script.IteratorRemoteValue //
  script.GeneratorRemoteValue //
  script.ErrorRemoteValue //
  script.ProxyRemoteValue //
  script.PromiseRemoteValue //
  script.TypedArrayRemoteValue //
  script.ArrayBufferRemoteValue //
  script.NodeListRemoteValue //
  script.HTMLCollectionRemoteValue //
  script.NodeRemoteValue //
  script.WindowProxyRemoteValue
)

script.InternalId = js-uint;

script.ListRemoteValue = [*script.RemoteValue];

script.MappingRemoteValue = [*[(script.RemoteValue / text), script.RemoteValue]];

script.SymbolRemoteValue = {
  type: "symbol",
  ? handle: script.Handle,
  ? internalId: script.InternalId,
}

script.ArrayRemoteValue = {
  type: "array",
  ? handle: script.Handle,
  ? internalId: script.InternalId,
  ? value: script.ListRemoteValue,
}

script.ObjectRemoteValue = {
  type: "object",
  ? handle: script.Handle,
  ? internalId: script.InternalId,
  ? value: script.MappingRemoteValue,
}

script.FunctionRemoteValue = {
  type: "function",
  ? handle: script.Handle,
  ? internalId: script.InternalId,
}

script.RegExpRemoteValue = {
  script.RegExpLocalValue,
  ? handle: script.Handle,
  ? internalId: script.InternalId,
}

script.DateRemoteValue = {
  script.DateLocalValue,
  ? handle: script.Handle,
  ? internalId: script.InternalId,
}

script.MapRemoteValue = {
  type: "map",
  ? handle: script.Handle,
  ? internalId: script.InternalId,
  ? value: script.MappingRemoteValue,
}

script.SetRemoteValue = {
  type: "set",
  ? handle: script.Handle,
  ? internalId: script.InternalId,
  ? value: script.ListRemoteValue
}

script.WeakMapRemoteValue = {
  type: "weakmap",
  ? handle: script.Handle,
  ? internalId: script.InternalId,
}

script.WeakSetRemoteValue = {
  type: "weakset",
  ? handle: script.Handle,
  ? internalId: script.InternalId,
}

script.IteratorRemoteValue = {
  type: "iterator",
  ? handle: script.Handle,
  ? internalId: script.InternalId,
}

script.GeneratorRemoteValue = {
  type: "generator",
  ? handle: script.Handle,
  ? internalId: script.InternalId,
}

script.ErrorRemoteValue = {
  type: "error",
  ? handle: script.Handle,
  ? internalId: script.InternalId,
}

script.ProxyRemoteValue = {
  type: "proxy",
  ? handle: script.Handle,
  ? internalId: script.InternalId,
}

script.PromiseRemoteValue = {
  type: "promise",
  ? handle: script.Handle,
  ? internalId: script.InternalId,
}

script.TypedArrayRemoteValue = {
  type: "typedarray",
  ? handle: script.Handle,
  ? internalId: script.InternalId,
}

script.ArrayBufferRemoteValue = {
  type: "arraybuffer",
  ? handle: script.Handle,
  ? internalId: script.InternalId,
}

script.NodeListRemoteValue = {
  type: "nodelist",
  ? handle: script.Handle,
  ? internalId: script.InternalId,
  ? value: script.ListRemoteValue,
}

script.HTMLCollectionRemoteValue = {
  type: "htmlcollection",
  ? handle: script.Handle,
  ? internalId: script.InternalId,
  ? value: script.ListRemoteValue,
}

script.NodeRemoteValue = {
  type: "node",
  ? sharedId: script.SharedId,
  ? handle: script.Handle,
  ? internalId: script.InternalId,
  ? value: script.NodeProperties,
}

script.NodeProperties = {
  nodeType: js-uint,
  childNodeCount: js-uint,
  ? attributes: {*text => text},
  ? children: [*script.NodeRemoteValue],
  ? localName: text,
  ? mode: "open" / "closed",
  ? namespaceURI: text,
  ? nodeValue: text,
  ? shadowRoot: script.NodeRemoteValue / null,
}

script.WindowProxyRemoteValue = {
  type: "window",
  ? handle: script.Handle,
  ? internalId: script.InternalId,
}

Add WASM types?

Should WindowProxy get attributes in a similar style to Node?

Describe `script.IteratorRemoteValue` serialization.

Describe `script.GeneratorRemoteValue` serialization.

Describe `script.ProxyRemoteValue` serialization.

handle String / Number / etc. wrapper objects specially?

Values accessible from the ECMAScript runtime are represented by a mirror object, specified as script.RemoteValue. The value’s type is specified in the type property. In the case of JSON-representable primitive values, this contains the value in the value property; in the case of non-JSON-representable primitives, the value property contains a string representation of the value.

For non-primitive objects, the handle property, when present, contains a unique string handle to the object. The handle is unique for each serialization. The remote end will keep objects with a corresponding handle alive until such a time that script.disown is called with that handle, or the realm itself is to be discarded (e.g. due to navigation).

For some non-primitive types, the value property contains a representation of the data in the ECMAScript object; for container types this can contain further script.RemoteValue instances. The value property can be null or omitted if there is a duplicate object i.e. the object has already been serialized in the current script.RemoteValue, perhaps as part of a cycle, or otherwise when the maximum serialization depth is reached.

In case of duplicated objects in the same script.RemoteValue, the value is provided only for one of the remote values, while the unique-per-ECMAScript-object internalId is provided for all the duplicated objects for a given serialization.

Nodes are also represented by script.RemoteValue instances. These have a partial serialization of the node in the value property.

reconsider mirror objects' lifecycle.

Note: mirror objects do not keep the original object alive in the runtime. If an object is discarded in the runtime, subsequent attempts to access it via the protocol will result in an error.

To get the handle for an object given realm, ownership type and object:
  1. If ownership type is equal "none", return null.

  2. Let handle id be a new, unique, string handle for object.

  3. Let handle map be realm’s handle object map

  4. Set handle map[handle id] to object.

  5. Return handle id as a result.

To get shared id for a node given node, realm and session:
  1. Let node be unwrapped node.

  2. If node does not implement Node, return null.

  3. Let browsing context be get the browsing context with realm.

  4. If browsing context is null, return null.

  5. Return get or create a node reference with session, browsing context and node.

To set internal ids if needed given serialization internal map, remote value and object:
  1. If the serialization internal map does not contain object, set serialization internal map[object] to remote value.

  2. Otherwise, run the following steps:

    1. Let previously serialized remote value be serialization internal map[object].

    2. If previously serialized remote value does not have a field internalId, run the following steps:

      1. Let internal id be a unique across the internalId fields of the values of serialization internal map integer.

      2. Set the internalId field of previously serialized remote value to internal id.

    3. Set the internalId field of remote value to a field internalId in previously serialized remote value.

To serialize as a remote value given value, serialization options, an ownership type, a serialization internal map, a realm and a session:

  1. Let remote value be a result of serialize primitive protocol value given a value.

  2. If remote value is not undefined, return remote value.

  3. In the following list of conditions and associated steps, run the first set of steps for which the associated condition is true:

  4. Let handle id be the handle for an object with realm, ownership type and value.

  5. Set ownership type to "none".

  6. Let known object be true, if value is in the serialization internal map, otherwise false.

    Type(value) is Symbol
    Let remote value be a map matching the script.SymbolRemoteValue production in the local end definition, with the handle property set to handle id if it’s not null, or omitted otherwise.
    IsArray(value)
    Let remote value be serialize an Array-like with session, script.ArrayRemoteValue, handle id, known object, value, serialization options, ownership type, serialization internal map, realm, and session.
    IsRegExp(value)
    1. Let pattern be ToString(Get(value, "source")).

    2. Let flags be ToString(Get(value, "flags")).

    3. Let value be a map matching the script.RegExpValue production in the local end definition, with the pattern property set to the pattern and the the flags property set to the flags.

    4. Let remote value be a map matching the script.RegExpRemoteValue production in the local end definition, with the handle property set to handle id if it’s not null, or omitted otherwise, and the value property set to value.

    value has a [[DateValue]] internal slot.
    1. Set serialized to Call(Date.toISOString, value).

    2. Assert: serialized is not a throw completion.

    3. Let remote value be a map matching the script.DateRemoteValue production in the local end definition, with the handle property set to handle id if it’s not null, or omitted otherwise, and the value set to serialized.

    value has a [[MapData]] internal slot
    1. Let remote value be a map matching the script.MapRemoteValue production in the local end definition, with the handle property set to handle id if it’s not null, or omitted otherwise.

    2. Set internal ids if needed with serialization internal map, remote value and value.

    3. Let serialized be null.

    4. If known object is false, and serialization options["maxObjectDepth"] is not 0, run the following steps:

      1. Let serialized be the result of serialize as a mapping with CreateMapIterator(value, key+value), serialization options, ownership type, serialization internal map, realm, and session.

    5. If serialized is not null, set field value of remote value to serialized.

    value has a [[SetData]] internal slot
    1. Let remote value be a map matching the script.SetRemoteValue production in the local end definition, with the handle property set to handle id if it’s not null, or omitted otherwise.

    2. Set internal ids if needed with serialization internal map, remote value and value.

    3. Let serialized be null.

    4. If known object is false, and serialization options["maxObjectDepth"] is not 0, run the following steps:

      1. Let serialized be the result of serialize as a list with CreateSetIterator(value, value), serialization options, ownership type, serialization internal map, realm, and session.

    5. If serialized is not null, set field value of remote value to serialized.

    value has a [[WeakMapData]] internal slot
    Let remote value be a map matching the script.WeakMapRemoteValue production in the local end definition, with the handle property set to handle id if it’s not null, or omitted otherwise.
    value has a [[WeakSetData]] internal slot
    Let remote value be a map matching the script.WeakSetRemoteValue production in the local end definition, with the handle property set to handle id if it’s not null, or omitted otherwise.
    value has an [[ErrorData]] internal slot
    Let remote value be a map matching the script.ErrorRemoteValue production in the local end definition, with the handle property set to handle id if it’s not null, or omitted otherwise.
    IsPromise(value)
    Let remote value be a map matching the script.PromiseRemoteValue production in the local end definition, with the handle property set to handle id if it’s not null, or omitted otherwise.
    value has a [[TypedArrayName]] internal slot
    Let remote value be a map matching the script.TypedArrayRemoteValue production in the local end definition, with the handle property set to handle id if it’s not null, or omitted otherwise.
    value has an [[ArrayBufferData]] internal slot
    Let remote value be a map matching the script.ArrayBufferRemoteValue production in the local end definition, with the handle property set to handle id if it’s not null, or omitted otherwise.
    value is a platform object that implements NodeList
    Let remote value be serialize an Array-like with script.NodeListRemoteValue,handle id, known object, value, serialization options, ownership type, serialization internal map, realm, and session.
    value is a platform object that implements HTMLCollection
    Let remote value be serialize an Array-like with script.HTMLCollectionRemoteValue, handle id, known object, value, serialization options, ownership type, known object, serialization internal map, realm, and session.
    value is a platform object that implements Node
    1. Let shared id be get shared id for a node with value, realm and session.

    2. Let remote value be a map matching the script.NodeRemoteValue production in the local end definition, with the sharedId property set to shared id if it’s not null, or omitted otherwise, and the handle property set to handle id if it’s not null, or omitted otherwise.

    3. Set internal ids if needed with serialization internal map, remote value and value.

    4. Let serialized be null.

    5. If known object is false, run the following steps:

      1. Let serialized be a map.

      2. Set serialized["nodeType"] to Get(value, "nodeType").

      3. Set node value to Get(value, "nodeValue").

      4. If node value is not null set serialized["nodeValue"] to node value.

      5. If value implements Element or Attr:

        1. Set serialized["localName"] to Get(value, "localName").

        2. Set serialized["namespaceURI"] to Get(value, "namespaceURI")

      6. Let child node count be the size of value’s children.

      7. Set serialized["childNodeCount"] to child node count.

      8. If serialization options["maxDomDepth"] is equal to 0, or if value implements ShadowRoot and serialization options["includeShadowTree"] is "none", or if serialization options["includeShadowTree"] is "open" and value’s mode is "closed", let children be null.

        Otherwise, let children be an empty list and, for each node child in the children of value:

        1. Let child serialization options be a clone of serialization options.

        2. If child serialization options["maxDomDepth"] is not null, set child serialization options["maxDomDepth"] to child serialization options["maxDomDepth"] - 1.

        3. Let serialized be the result of serialize as a remote value with child, child serialization options, ownership type, serialization internal map, realm, and session.

        4. Append serialized to children.

      9. If children is not null, set serialized["children"] to children.

      10. If value implements Element:

        1. Let attributes be a new map.

        2. For each attribute in value’s attribute list:

          1. Let name be attribute’s qualified name

          2. Let value be attribute’s value.

          3. Set attributes[name] to value

        3. Set serialized["attributes"] to attributes.

        4. Let shadow root be value’s shadow root.

        5. If shadow root is null, let serialized shadow be null. Otherwise run the following substeps:

          1. Let serialized shadow be the result of serialize as a remote value with shadow root, serialization options, ownership type, serialization internal map, realm, and session.

        6. Set serialized["shadowRoot"] to serialized shadow.

      11. If value implements ShadowRoot, set serialized["mode"] to value’s mode.

    6. If serialized is not null, set field value of remote value to serialized.

    value is a platform object that implements WindowProxy
    1. Let remote value be a map matching the script.WindowProxyRemoteValue production in the local end definition, with the handle property set to handle id if it’s not null, or omitted otherwise.
    value is a platform object
    1. Let remote value be a map matching the script.ObjectRemoteValue production in the local end definition, with the handle property set to handle id if it’s not null, or omitted otherwise.
    IsCallable(value)
    Let remote value be a map matching the script.FunctionRemoteValue production in the local end definition, with the handle property set to handle id if it’s not null, or omitted otherwise.
    Otherwise:
    1. Assert: type(value) is Object

    2. Let remote value be a map matching the script.ObjectRemoteValue production in the local end definition, with the handle property set to handle id if it’s not null, or omitted otherwise.

    3. Set internal ids if needed with serialization internal map, remote value and value.

    4. Let serialized be null.

    5. If known object is false, and serialization options["maxObjectDepth"] is not 0, run the following steps:

      1. Let serialized be the result of serialize as a mapping with EnumerableOwnPropertyNames(value, key+value), serialization options, ownership type, serialization internal map, realm, and session.

    6. If serialized is not null, set field value of remote value to serialized.

  7. Return remote value

children and child nodes are different things. Either childNodeCount should reference to childNodes, or it should be renamed to childrenCount.

nodeValue can be null. Omit the field in such a case, or adjust type to allow null value.

To serialize an Array-like given production, handle id, known object, value, serialization options, ownership type, serialization internal map, realm, and session:
  1. Let remote value be a map matching production, with the handle property set to handle id if it’s not null, or omitted otherwise.

  2. Set internal ids if needed with serialization internal map, remote value and value.

  3. If known object is false, and serialization options["maxObjectDepth"]| is not 0:

    1. Let serialized be the result of serialize as a list with CreateArrayIterator(value, value), serialization options, ownership type, serialization internal map, realm, and session.

    2. If serialized is not null, set field value of remote value to serialized.

  4. Return remote value

To serialize as a list given iterable, serialization options, ownership type, serialization internal map, realm, and session:
  1. If serialization options["maxObjectDepth"] is not null, assert: serialization options["maxObjectDepth"] is greater than 0.

  2. Let serialized be a new list.

  3. For each child value in iterable:

    1. Let child serialization options be a clone of serialization options.

    2. If child serialization options["maxObjectDepth"] is not null, set child serialization options["maxObjectDepth"] to child serialization options["maxObjectDepth"] - 1.

    3. Let serialized child be the result of serialize as a remote value with child value, child serialization options, ownership type, serialization internal map, realm, and session.

    4. Append serialized child to serialized.

  4. Return serialized

this assumes for-in works on iterators

To serialize as a mapping given iterable, serialization options, ownership type, serialization internal map, realm, and session:
  1. If serialization options["maxObjectDepth"] is not null, assert: serialization options["maxObjectDepth"] is greater than 0.

  2. Let serialized be a new list.

  3. For item in iterable:

    1. Assert: IsArray(item)

    2. Let property be CreateListFromArrayLike(item)

    3. Assert: property is a list of size 2

    4. Let key be property[0] and let value be property[1]

    5. Let child serialization options be a clone of serialization options.

    6. If child serialization options["maxObjectDepth"] is not null, set child serialization options["maxObjectDepth"] to child serialization options["maxObjectDepth"] - 1.

    7. If Type(key) is String, let serialized key be child key, otherwise let serialized key be the result of serialize as a remote value with child key, child serialization options, ownership type, serialization internal map, realm, and session.

    8. Let serialized value be the result of serialize as a remote value with value, child serialization options, ownership type, serialization internal map, realm, and session.

    9. Let serialized child be («serialized key, serialized value»).

    10. Append serialized child to serialized.

  4. Return serialized

6.5.3.14. The script.ResultOwnership Type
script.ResultOwnership = "root" / "none"

The script.ResultOwnership specifies how the serialized value ownership will be treated.

6.5.3.15. The script.SerializationOptions Type

Remote end definition

script.SerializationOptions = {
  ? maxDomDepth: (js-uint / null) .default 0,
  ? maxObjectDepth: (js-uint / null) .default null,
  ? includeShadowTree: ("none" / "open" / "all") .default "none",
}

The script.SerializationOptions allows specifying how ECMAScript objects will be serialized.

6.5.3.16. The script.SharedId Type

Remote end definition and local end definition

script.SharedId = text;

The script.SharedId type represents a reference to a DOM Node that is usable in any realm (including Sandbox Realms).

6.5.3.17. The script.StackFrame Type

Remote end definition and local end definition

script.StackFrame = {
  columnNumber: js-uint,
  functionName: text,
  lineNumber: js-uint,
  url: text,
}

A frame in a stack trace is represented by a StackFrame object. This has a url property, which represents the URL of the script, a functionName property which represents the name of the executing function, and lineNumber and columnNumber properties, which represent the line and column number of the executed code.

6.5.3.18. The script.StackTrace Type

Remote end definition and local end definition

script.StackTrace = {
  callFrames: [*script.StackFrame],
}

The script.StackTrace type represents the javascript stack at a point in script execution.

Note: The details of how to get a list of stack frames, and the properties of that list are underspecified, and therefore the details here are implementation defined.

It is assumed that an implementation is able to generate a list of stack frames, which is a list with one entry for each item in the javascript call stack, starting from the most recent. Each entry is a single stack frame corresponding to execution of a statement or expression in a script script, which contains the following fields:

script url
The url of the resource containing script
function
The name of the function being executed
line number
The zero-based line number of the executed code, relative to the top of the resource containing script.
column number
The zero-based column number of the executed code, relative to the start of the line in the resource containing script.

To construct a stack trace, with a list of stack frames stack:

  1. Let call frames be a new list.

  2. For each stack frame frame in stack, starting from the most recently executed frame, run the following steps:

    1. Let url be the result of running the URL serializer, given the URL of frame’s script url.

    2. Let frame info be a new map matching the script.StackFrame production, with the url field set to url, the functionName field set to frame’s function, the lineNumber field set to frame’s line number and the columnNumber field set to frame’s column number.

  3. Append frame info to call frames.

  4. Let stack trace be a new map matching the script.StackTrace production, with the callFrames property set to call frames.

  5. Return stack trace.

The current stack trace is the result of construct a stack trace given a list of stack frames representing the callstack of the running execution context.

The stack trace for an exception with an exception, or a Completion Record of type throw, exception, is given by:

  1. If exception is a value that has been thrown as an exception, let record be the Completion Record created to throw exception. Otherwise let record be exception.

  2. Let stack be the list of stack frames corresponding to execution at the point record was created.

  3. Return construct a stack trace given stack.

6.5.3.19. The script.Source Type

Local end definition

script.Source = (
  realm: script.Realm,
  ? context: browsingContext.BrowsingContext
);

The script.Source type represents a script.Realm with an optional browsingContext.BrowsingContext in which a script related event occurred.

To get the source given source realm:
  1. Let realm be the realm id for source realm.

  2. Let environment settings be the environment settings object whose realm execution context's Realm component is source realm.

  3. If environment settings has a associated Document:

    1. Let document be environment settings’ associated Document.

    2. Let browsing context be document’s browsing context.

    3. Let context id be the browsing context id for browsing context.

    Otherwise let browsing context be null.

  4. Let source be a map matching the script.Source production with the realm field set to realm, and the context field set to context id if browsing context is not null, or unset otherwise.

  5. Return source.

6.5.3.20. The script.Target Type

Remote end definition

script.RealmTarget = {
  realm: script.Realm
};

script.ContextTarget = {
  context: browsingContext.BrowsingContext,
  ? sandbox: text
}

script.Target = (
  script.RealmTarget //
  script.ContextTarget
);

The script.Target type represents a value that is either a script.Realm or a browsingContext.BrowsingContext. This is useful in cases where a context identifier can stand in for the realm associated with the context’s active document.

To get a realm from a target given target:
  1. If target matches the script.ContextTarget production:

    1. Let context be the result of trying to get a browsing context with target["context"].

    2. If target does not contain a field named "sandbox", or target["sandbox"] is an empty string:

      1. Let document be context’s active document.

      2. Let environment settings be the environment settings object whose relevant global object's associated Document is document.

      3. Let realm be environment settingsrealm execution context's Realm component.

    3. Otherwise: let realm be result of trying to get or create a sandbox realm given target["sandbox"] and context.

  2. Otherwise:

    1. Assert: target matches the script.RealmTarget production.

    2. Let realm id be the value of the realm field of target.

    3. Let realm be get a realm given realm id.

  3. Return success with data realm

This has the wrong error code

6.5.4. Commands

6.5.4.1. The script.addPreloadScript Command

The script.addPreloadScript command adds a preload script.

Command Type
script.AddPreloadScriptCommand = {
  method: "script.addPreloadScript",
  params: script.AddPreloadScriptParameters
}

script.AddPreloadScriptParameters = {
  functionDeclaration: text,
  ? arguments: [*script.ChannelValue],
  ? sandbox: text
}
Return Type
script.AddPreloadScriptResult = {
  script: script.PreloadScript
}
The remote end steps given session and command parameters are:
  1. Let function declaration be the functionDeclaration field of command parameters.

  2. Let arguments be the arguments field of command parameters if present, or an empty list otherwise.

  3. Let script be the string representation of a UUID.

  4. Let sandbox be the value of the "sandbox" field in command parameters, if present, or null otherwise.

  5. Let preload script map be session’s preload script map.

  6. Set preload script map[script] to a struct with function declaration function declaration, arguments arguments, and sandbox sandbox.

  7. Return a new map matching the script.AddPreloadScriptResult with the script field set to script.

6.5.4.2. The script.disown Command

The script.disown command disowns the given handles. This does not guarantee the handled object will be garbage collected, as there can be other handles or strong ECMAScript references.

Command Type
script.Disown = {
  method: "script.disown",
  params: script.DisownParameters
}

script.DisownParameters = {
  handles: [script.Handle]
  target: script.Target;
}
Return Type
EmptyResult
The remote end steps with command parameters are:
  1. Let realm be the result of trying to get a realm from a target given the value of the target field of command parameters.

  2. Let handles the value of the handles field of command parameters.

  3. For each handle id of handles:

    1. Let handle map be realm’s handle object map

    2. If handle map contains handle id, remove handle id from the handle map.

  4. Return success with data null.

6.5.4.3. The script.callFunction Command

The script.callFunction command calls a provided function with given arguments in a given realm.

RealmInfo can be either a realm or a browsing context.

Note: In case of an arrow function in functionDeclaration, the this argument doesn’t affect function’s this binding.

Command Type
script.CallFunction = {
  method: "script.callFunction",
  params: script.CallFunctionParameters
}

script.CallFunctionParameters = {
  functionDeclaration: text,
  awaitPromise: bool,
  target: script.Target,
  ? arguments: [*script.ArgumentValue],
  ? resultOwnership: script.ResultOwnership,
  ? serializationOptions: script.SerializationOptions,
  ? this: script.ArgumentValue,
}

script.ArgumentValue = (
  script.RemoteReference //
  script.LocalValue //
  script.ChannelValue
);

Result Type
script.EvaluateResult

TODO: Add timeout argument as described in the script.evaluate.

To deserialize arguments with given realm, serialized arguments list and session:

  1. Let deserialized arguments list be an empty list.

  2. For each serialized argument of serialized arguments list:

    1. Let deserialized argument be the result of trying to deserialize local value given serialized argument, realm and session.

    2. Append deserialized argument to the deserialized arguments list.

  3. Return success with data deserialized arguments list.

To evaluate function body given function declaration, environment settings, base URL, and options:

Note: the function declaration is parenthesized and evaluated.

  1. Let parenthesized function declaration be concatenate «"(", function declaration, ")

  2. Let function script be the result of create a classic script with parenthesized function declaration, environment settings, base URL, and options.

  3. Prepare to run script with environment settings.

  4. Let function body evaluation status be ScriptEvaluation(function script’s record).

  5. Clean up after running script with environment settings.

  6. Return (function script, function body evaluation status).

The remote end steps with session and command parameters are:

  1. Let realm be the result of trying to get a realm from a target given the value of the target field of command parameters.

  2. Let realm id be realm’s realm id.

  3. Let environment settings be the environment settings object whose realm execution context's Realm component is realm.

  4. Let command arguments be the value of the arguments field of command parameters.

  5. Let deserialized arguments be an empty list.

  6. If command arguments is not null, set deserialized arguments to the result of trying to deserialize arguments given realm, command arguments and session.

  7. Let this parameter be the value of the this field of command parameters.

  8. Let this object be null.

  9. If this parameter is not null, set this object to the result of trying to deserialize local value given this parameter, realm and session.

  10. Let function declaration be the value of the functionDeclaration field of command parameters.

  11. Let await promise be the value of the awaitPromise field of command parameters.

  12. Let serialization options be the value of the serializationOptions field of command parameters, if present, or otherwise a map matching the script.SerializationOptions production with the fields set to their default values.

  13. Let result ownership be the value of the resultOwnership field of command parameters, if present, or none otherwise.

  14. Let base URL be the API base URL of environment settings.

  15. Let options be the default classic script fetch options.

  16. Let (script, function body evaluation status) be the result of evaluate function body with function declaration, environment settings, base URL, and options.

  17. If function body evaluation status.[[Type]] is throw:

    1. Let exception details be the result of get exception details given realm, function body evaluation status, result ownership and session.

    2. Return a new map matching the script.EvaluateResultException production, with the exceptionDetails field set to exception details.

  18. Let function object be function body evaluation status.[[Value]].

  19. If IsCallable(function object) is false:

    1. Return an error with error code invalid argument

  20. Prepare to run script with environment settings.

  21. Set evaluation status to Call(function object, this object, deserialized arguments).

  22. If evaluation status.[[Type]] is normal, and await promise is true, and IsPromise(evaluation status.[[Value]]):

    1. Set evaluation status to Await(evaluation status.[[Value]]).

  23. Clean up after running script with environment settings.

  24. If evaluation status.[[Type]] is throw:

    1. Let exception details be the result of get exception details given realm, evaluation status, result ownership and session.

    2. Return a new map matching the script.EvaluateResultException production, with the exceptionDetails field set to exception details.

  25. Assert: evaluation status.[[Type]] is normal.

  26. Let result be the result of serialize as a remote value with evaluation status.[[Value]], serialization options, result ownership, a new map as serialization internal map, realm and session.

  27. Return a new map matching the script.EvaluateResultSuccess production, with the realm field set to realm id, and the result field set to result.

6.5.4.4. The script.evaluate Command

The script.evaluate command evaluates a provided script in a given realm. For convenience a browsing context can be provided in place of a realm, in which case the realm used is the realm of the browsing context’s active document.

The method returns the value of executing the provided script, unless it returns a promise and awaitPromise is true, in which case the resolved value of the promise is returned.

Command Type
script.Evaluate = {
  method: "script.evaluate",
  params: script.EvaluateParameters
}

script.EvaluateParameters = {
  expression: text,
  target: script.Target,
  awaitPromise: bool,
  ? resultOwnership: script.ResultOwnership,
  ? serializationOptions: script.SerializationOptions,
}
Result Type
script.EvaluateResult

TODO: Add timeout argument. It’s not totally clear how this ought to work; in Chrome it seems like the timeout doesn’t apply to the promise resolve step, but that likely isn’t what clients want.

The remote end steps given session and command parameters are:

  1. Let realm be the result of trying to get a realm from a target given the value of the target field of command parameters.

  2. Let realm id be realm’s realm id.

  3. Let environment settings be the environment settings object whose realm execution context's Realm component is realm.

  4. Let source be the value of the expression field of command parameters.

  5. Let await promise be the value of the awaitPromise field of command parameters.

  6. Let serialization options be the value of the serializationOptions field of command parameters, if present, or otherwise a map matching the script.SerializationOptions production with the fields set to their default values.

  7. Let result ownership be the value of the resultOwnership field of command parameters, if present, or none otherwise.

  8. Let options be the default classic script fetch options.

  9. Let base URL be the API base URL of environment settings.

  10. Let script be the result of create a classic script with source, environment settings, base URL, and options.

  11. Prepare to run script with environment settings.

  12. Set evaluation status to ScriptEvaluation(script’s record).

  13. If evaluation status.[[Type]] is normal, await promise is true, and IsPromise(evaluation status.[[Value]]):

    1. Set evaluation status to Await(evaluation status.[[Value]]).

  14. Clean up after running script with environment settings.

  15. If evaluation status.[[Type]] is throw:

    1. Let exception details be the result of get exception details with realm, evaluation status, result ownership and session.

    2. Return a new map matching the script.EvaluateResultException production, with the realm field set to realm id, and the exceptionDetails field set to exception details.

  16. Assert: evaluation status.[[Type]] is normal.

  17. Let result be the result of serialize as a remote value with evaluation status.[[Value]], serialization options, result ownership, a new map as serialization internal map, realm and session.

  18. Return a new map matching the script.EvaluateResultSuccess production, with the with the realm field set to realm id, and the result field set to result.

6.5.4.5. The script.getRealms Command

The script.getRealms command returns a list of all realms, optionally filtered to realms of a specific type, or to the realm associated with the document currently loaded in a specified browsing context.

Command Type
script.GetRealms = {
  method: "script.getRealms",
  params: script.GetRealmsParameters
}

script.GetRealmsParameters = {
  ? context: browsingContext.BrowsingContext,
  ? type: script.RealmType,
}
Result Type
script.GetRealmsResult = {
  realms: [*script.RealmInfo]
}
The remote end steps with session and command parameters are:
  1. Let environment settings be a list of all the environment settings objects that have their execution ready flag set.

  2. If command parameters contains context:

    1. Let context be the result of trying to get a browsing context with command parameters["context"].

    2. Let document be context’s active document.

    3. Let context environment settings be a list.

    4. For each settings of environment settings:

      1. If any of the following conditions hold:

        Append settings to context environment settings.

    5. Set environment settings to context environment settings.

  3. Let realms be a list.

  4. For each settings of environment settings:

    1. Let realm info be the result of get the realm info given settings

    2. If command parameters contains type and realm info["type"] is not equal to command parameters["type"] then continue.

    3. If realm info is not null, append realm info to realms.

  5. Let body be a map matching the script.GetRealmsResult production, with the realms field set to realms.

  6. Return success with data body.

Extend this to also allow realm parents e.g. for nested workers? Or get all ancestor workers.

We might want to have a more sophisticated filter system than just a literal match.

6.5.4.6. The script.removePreloadScript Command

The script.removePreloadScript command removes a preload script.

Command Type
script.RemovePreloadScriptCommand = {
  method: "script.removePreloadScript",
  params: script.RemovePreloadScriptParameters
}

script.RemovePreloadScriptParameters = {
  script: script.PreloadScript
}
Return Type
EmptyResult
The remote end steps given session and command parameters are:
  1. Let script be the value of the "script" field in command parameters.

  2. Let preload script map be session’s preload script map.

  3. If preload script map does not contain script, return error with error code no such script.

  4. Remove script from preload script map.

  5. Return null

6.5.5. Events

6.5.5.1. The script.message Event
Event Type
 script.Message = {
  method: "script.message",
  params: script.MessageParameters
}

script.MessageParameters = {
  channel: script.Channel,
  data: script.RemoteValue,
  source: script.Source,
}
The remote end event trigger is the emit a script message steps, given session, realm, channel properties, and message:
  1. Let environment settings be the environment settings object whose realm execution context's Realm component is realm.

  2. Let related browsing contexts be the result of get related browsing contexts given environment settings.

  3. If event is enabled given session, "script.message" and related browsing contexts:

    1. If channel properties contains "serializationOptions", let serialization options be the value of the serializationOptions field of channel properties. Otherwise let serialization options be a map matching the script.SerializationOptions production with the fields set to their default values.

    2. Let if channel properties contains "ownership", let ownership type be channel properties["ownership"]. Otherwise let ownership type be "none".

    3. Let data be the result of serialize as a remote value given message, serialization options, ownership type, a new map as serialization internal map and realm.

    4. Let source be the get the source with realm.

    5. Let params be a map matching the script.MessageParameters production, with the channel field set to channel properties["channel"], the data field set to data, and the source field set to source.

    6. Let body be a map matching the script.Message production, with the params field set to params.

    7. Emit an event with session and body.

6.5.5.2. The script.realmCreated Event
Event Type
 script.RealmCreated = {
  method: "script.realmCreated",
  params: script.RealmInfo
}
The remote end event trigger is:

When any of the set up a window environment settings object, set up a worker environment settings object or set up a worklet environment settings object algorithms are invoked, immediately prior to returning the settings object:

  1. Let environment settings be the newly created environment settings object.

  2. Let realm info be the result of get the realm info given environment settings.

  3. If realm info is null, return.

  4. Let related browsing contexts be the result of get related browsing contexts given environment settings.

  5. Let body be a map matching the script.RealmCreated production, with the params field set to realm info.

  6. For each session in the set of sessions for which an event is enabled given "script.realmCreated" and related browsing contexts:

    1. Emit an event with session and body.

The remote end subscribe steps with subscribe priority 2, given session, contexts and include global are:

  1. Let environment settings be a list of all the environment settings objects that have their execution ready flag set.

  2. For each settings of environment settings:

    1. Let related browsing contexts be a new set.

    2. If the associated Document of settingsrelevant global object is a Document:

      1. Let context be settings’s relevant global object's associated Document’s browsing context's top-level browsing context.

      2. If context is not in contexts, continue.

      3. Append context to related browsing contexts.

      Otherwise, if include global is false, continue.

    3. Let realm info be the result of get the realm info given settings

    4. Let body be a map matching the script.RealmCreated production, with the params field set to realm info.

    5. If event is enabled given session, "script.realmCreated" and related browsing contexts:

      1. Emit an event with session and body.

Should the order here be better defined?

6.5.5.3. The script.realmDestroyed Event
Event Type
script.RealmDestroyed = {
  method: "script.realmDestoyed",
  params: script.RealmDestroyedParameters
}

script.RealmDestroyedParameters = {
  realm: script.Realm
}

The remote end event trigger is:
Define the following unloading document cleanup steps with document:
  1. Let related browsing contexts be an empty set.

  2. Append document’s browsing context to related browsing contexts.

  3. For each worklet global scope in document’s worklet global scopes:

    1. Let realm be worklet global scope’s relevant Realm.

    2. Let realm id be the realm id for realm.

    3. Let params be a map matching the script.RealmDestroyedParameters production, with the realm field set of realm id.

    4. Let body be a map matching the script.RealmDestroyed production, with the params field set to params.

    5. For each session in the set of sessions for which an event is enabled given "script.realmDestroyed" and related browsing contexts:

      1. Emit an event with session and body.

  4. Let environment settings be the environment settings object whose relevant global object's associated Document is document.

  5. Let realm be environment settingsrealm execution context's Realm component.

  6. Let realm id be the realm id for realm.

  7. Let params be a map matching the script.RealmDestroyedParameters production, with the realm field set to realm id.

  8. Let body be a map matching the script.RealmDestroyed production, with the params field set to params.

  9. For each session in the set of sessions for which an event is enabled given "script.realmDestroyed" and related browsing contexts:

    1. Emit an event with session and body.

Whenever a worker event loop event loop is destroyed, either because the worker comes to the end of its lifecycle, or prematurely via the terminate a worker algorithm:

  1. Let environment settings be the environment settings object for which event loop is the responsible event loop.

  2. Let related browsing contexts be the result of get related browsing contexts given environment settings.

  3. Let realm be environment settings’s environment settings object’s Realm.

  4. Let realm id be the realm id for realm.

  5. Let params be a map matching the script.RealmDestroyedParameters production, with the realm field set of realm id.

  6. Let body be a map matching the script.RealmDestroyed production, with the params field set to params.

6.6. The log Module

The log module contains functionality and events related to logging.

A BiDi Session has a log event buffer which is a map from browsing context id to a list of log events for that context that have not been emitted. User agents may impose a maximum size on this buffer, subject to the condition that if events A and B happen in the same context with A occuring before B, and both are added to the buffer, the entry for B must not be removed before the entry for A.

To buffer a log event given session, contexts and event:

  1. Let buffer be session’s log event buffer.

  2. Let context ids be a new list.

  3. For each context of contexts:

    1. Append the browsing context id for context to context ids.

  4. For each context id in context ids:

    1. Let other contexts be an empty list

    2. For each other id in context ids:

    3. If other id is not equal to context id, append other id to other contexts.

    4. If buffer does not contain context id, let buffer[context id] be a new list.

    5. Append (event, other contexts) to buffer[context id].

Note: we store the other contexts here so that each event is only emitted once. In practice this is only relevant for workers that can be associated with multiple browsing contexts.

Do we want to key this on browsing context or top-level browsing context? The difference is in what happens if an event occurs in a frame and that frame is then navigated before the local end subscribes to log events for the top level context.

6.6.1. Definition

Local end definition

LogEvent = (
  log.EntryAdded
)

6.6.2. Types

6.6.2.1. log.LogEntry

Local end definition

log.Level = "debug" / "info" / "warn" / "error"

log.Entry = (
  log.GenericLogEntry //
  log.ConsoleLogEntry //
  log.JavascriptLogEntry
)

log.BaseLogEntry = {
  level: log.Level,
  source: script.Source,
  text: text / null,
  timestamp: js-uint,
  ? stackTrace: script.StackTrace,
}

log.GenericLogEntry = {
  log.BaseLogEntry,
  type: text,
}

log.ConsoleLogEntry = {
  log.BaseLogEntry,
  type: "console",
  method: text,
  args: [*script.RemoteValue],
}

log.JavascriptLogEntry = {
  log.BaseLogEntry,
  type: "javascript",
}

Each log event is represented by a log.Entry object. This has a type property which represents the type of log entry added, a level property representing severity, a source property representing the origin of the log entry, a text property with the log message string itself, and a timestamp property corresponding to the time the log entry was generated. Specific variants of the log.Entry are used to represent logs from different sources, and provide additional fields specific to the entry type.

6.6.3. Events

6.6.3.1. The log.entryAdded Event
Event Type
 log.EntryAdded = {
  method: "log.entryAdded",
  params: log.Entry,
}

The remote end event trigger is:

Define the following console steps with method, args, and options:

  1. For each session in active BiDI sessions:

    1. If method is "error" or "assert", let level be "error". If method is "debug" or "trace" let level be "debug". If method is "warn", let level be "warn". Otherwise let level be "info".

    2. Let timestamp be a time value representing the current date and time in UTC.

    3. Let text be an empty string.

    4. If Type(args[0]) is String, and args[0] contains a formatting specifier, let formatted args be Formatter(args). Otherwise let formatted args be args.

      Note: The formatter operation is underdefined in the console specification, formatting can be inconsistent between different implementations.

    5. For each arg in formatted args:

      1. If arg is not the first entry in args, append a U+0020 SPACE to text.

      2. If arg is a primitive ECMAScript value, append ToString(arg) to text. Otherwise append an implementation-defined string to text.

    6. Let realm be the realm id of the current Realm Record.

    7. Let serialized args be a new list.

    8. Let serialization options be a map matching the script.SerializationOptions production with the fields set to their default values.

    9. For each arg of args:

      1. Let serialized arg be the result of serialize as a remote value with arg as value, serialization options, none as ownership type, a new map as serialization internal map, realm and session.

      2. Add serialized arg to serialized args.

    10. Let source be the result of get the source given current Realm Record.

    11. If method is "assert", "error", "trace", or "warn", let stack be the current stack trace. Otherwise let stack be null.

    12. Let entry be a map matching the log.ConsoleLogEntry production, with the the level field set to level, the text field set to text, the timestamp field set to timestamp, the stackTrace field set to stack if stack is not null, or omitted otherwise, the method field set to method, the source field set to source and the args field set to serialized args.

    13. Let body be a map matching the log.EntryAdded production, with the params field set to entry.

    14. Let settings be the current settings object

    15. Let related browsing contexts be the result of get related browsing contexts given settings.

    16. If event is enabled with session, "log.entryAdded" and related browsing contexts, emit an event with session and body.

      Otherwise, buffer a log event with session, related browsing contexts, and body.

Define the following error reporting steps with arguments script, line number, column number, message and handled:

  1. If handled is true return.

  2. Let settings be script’s settings object.

  3. Let stack be the stack trace for an exception with the exception corresponding to the error being reported.

  4. Let source be the result of get the source given current Realm Record.

  5. Let entry be a map matching the log.JavascriptLogEntry production, with level set to "error", text set to message, source set to source, and the timestamp field set to timestamp.

  6. Let related browsing contexts be the result of get related browsing contexts given settings.

  7. For each session in active BiDi sessions:

    1. If event is enabled with session, "log.entryAdded" and related browsing contexts, emit an event with session and body.

      Otherwise, buffer a log event with session, related browsing contexts, and body.

Lots more things require logging. CDP has LogEntryAdded types xml, javascript, network, storage, appcache, rendering, security, deprecation, worker, violation, intervention, recommendation, other. These are in addition to the js exception and console API types that are represented by different methods.

Allow implementation-defined log types

The remote end subscribe steps, with subscribe priority 10, given session, contexts and include global are:

  1. For each context idevents in session’s log event buffer:

    1. Let maybe context be the result of getting a browsing context given context id.

    2. If maybe context is an error, remove context id from log event buffer and continue.

    3. Let context be maybe context’s data

    4. Let top level context be context’s top-level browsing context.

    5. If include global is true and top level context is not in contexts, or if include global is false and top level context is in contexts:

      1. For each (event, other contexts) in events:

        1. Emit an event with session and event.

        2. For each other context id in other contexts:

          1. If log event buffer contains other context id, remove event from log event buffer[other context id].

6.7. Input

The input module contains functionality for simulated user input.

6.7.1. Definition

remote end definition

InputCommand = (
  input.PerformActions //
  input.ReleaseActions
)

6.7.2. Types

6.7.2.1. input.ElementOrigin

The input.ElementOrigin type represents an Element that will be used as a coordinate origin.

input.ElementOrigin = {
  type: "element",
  element: script.SharedReference
}
The is input.ElementOrigin steps given object are:
  1. If object is a map matching the input.ElementOrigin production, return true.

  2. Return false.

To get Element from input.ElementOrigin steps given session:
  1. Return the following steps, given origin and context:

    1. Assert: origin matches input.ElementOrigin.

    2. Let document be context’s active document.

    3. Let reference be origin["element"]

    4. Let environment settings be the environment settings object whose relevant global object's associated Document is document.

    5. Let realm be environment settingsrealm execution context's Realm component.

    6. Let element be the result of trying to deserialize remote reference with reference, realm, and session.

    7. If element doesn’t implement Element return error with error code no such element.

    8. Return success with data element.

6.7.3. Commands

6.7.3.1. The input.performActions Command

The input.performActions command performs a specified sequence of user input actions.

Command Type
input.PerformActions = {
  method: "input.performActions",
  params: input.PerformActionsParameters
};

input.PerformActionsParameters = {
  context: browsingContext.BrowsingContext,
  actions: [*input.SourceActions]
}

input.SourceActions = (
  input.NoneSourceActions //
  input.KeySourceActions //
  input.PointerSourceActions //
  input.WheelSourceActions
)

input.NoneSourceActions = {
  type: "none",
  id: text,
  actions: [*input.NoneSourceAction]
}

input.NoneSourceAction = input.PauseAction

input.KeySourceActions = {
  type: "key",
  id: text,
  actions: [*input.KeySourceAction]
}

input.KeySourceAction = (
  input.PauseAction //
  input.KeyDownAction //
  input.KeyUpAction
)

input.PointerSourceActions = {
  type: "pointer",
  id: text,
  ? parameters: input.PointerParameters,
  actions: [*input.PointerSourceAction]
}

input.PointerType = "mouse" / "pen" / "touch"

input.PointerParameters = {
  ? pointerType: input.PointerType .default "mouse"
}

input.PointerSourceAction = (
  input.PauseAction //
  input.PointerDownAction //
  input.PointerUpAction //
  input.PointerMoveAction
)

input.WheelSourceActions = {
  type: "wheel",
  id: text,
  actions: [*input.WheelSourceAction]
}

input.WheelSourceAction = (
  input.PauseAction //
  input.WheelScrollAction
)

input.PauseAction = {
  type: "pause",
  ? duration: js-uint
}

input.KeyDownAction = {
  type: "keyDown",
  value: text
}

input.KeyUpAction = {
  type: "keyUp",
  value: text
}

input.PointerUpAction = {
  type: "pointerUp",
  button: js-uint,
  input.PointerCommonProperties
}

input.PointerDownAction = {
  type: "pointerDown",
  button: js-uint,
  input.PointerCommonProperties
}

input.PointerMoveAction = {
  type: "pointerMove",
  x: js-int,
  y: js-int,
  ? duration: js-uint,
  ? origin: input.Origin,
  input.PointerCommonProperties
}

input.WheelScrollAction = {
  type: "scroll",
  x: js-int,
  y: js-int,
  deltaX: js-int,
  deltaY: js-int,
  ? duration: js-uint,
  ? origin: input.Origin .default "viewport",
}

input.PointerCommonProperties = (
  ? width: js-uint .default 1,
  ? height: js-uint .default 1,
  ? pressure: float .default 0.0,
  ? tangentialPressure: float .default 0.0,
  ? twist: 0..359 .default 0,
  (input.TiltProperties // input.AngleProperties)
)

input.AngleProperties = (
  ? altitudeAngle: float .default 0.0,
  ? azimuthAngle: float .default 0.0,
)

input.TiltProperties = (
  ? tiltX: -90..90 .default 0,
  ? tiltY: -90..90 .default 0,
)

input.Origin = "viewport" / "pointer" / input.ElementOrigin
Return Type
EmptyResult

The remote end steps with session and command parameters are:

  1. Let context id be the value of the context field of command parameters.

  2. Let context be the result of trying to get a browsing context with context id.

  3. Let input state be get the input state with session and context’s top-level browsing context.

  4. Let actions options be a new actions options with the is element origin steps set to is input.ElementOrigin, and the get element origin steps set to the result of get Element from input.ElementOrigin steps given session.

  5. Let actions by tick be the result of trying to extract an action sequence with input state, command parameters, and actions options.

  6. Try to dispatch actions with input state, actions by tick, context, and actions options.

  7. Return success with data null.

6.7.3.2. The input.releaseActions Command

The input.releaseActions command resets the input state associated with the current session.

Command Type
input.ReleaseActions = {
  method: "input.releaseActions",
  params: input.ReleaseActionsParameters
};

input.ReleaseActionsParameters = {
  context: browsingContext.BrowsingContext,
}
Return Type
EmptyResult

The remote end steps given session, and command parameters are:

  1. Let context id be the value of the context field of command parameters.

  2. Let context be the result of trying to get a browsing context with context id.

  3. Let top-level context be context’s top-level browsing context.

  4. Let input state be get the input state with session and top-level context.

  5. Let actions options be a new actions options with the is element origin steps set to is input.ElementOrigin, and the get element origin steps set to get Element from input.ElementOrigin steps given session.

  6. Let undo actions be input state’s input cancel list in reverse order.

  7. Try to dispatch tick actions with undo actions, 0, context, and actions options.

  8. Reset the input state with session and top-level context.

  9. Return success with data null.

7. Patches to Other Specifications

This specification requires some changes to external specifications to provide the necessary integration points. It is assumed that these patches will be committed to the other specifications as part of the standards process.

7.1. HTML

The a browsing context is discarded algorithm is modified to read as follows:

To discard a browsing context browsingContext, run these steps:
  1. If this is not a recursive invocation of this algorithm, call any browsing context tree discarded steps defined in other applicable specifications with browsingContext.

  2. Discard all Document objects for all the entries in browsingContext’s session history.

  3. If browsingContext is a top-level browsing context, then remove a browsing context browsingContext.

The actual patch might be better to split the algorithm into an outer algorithm that is called by external callers and an inner algorithm that’s used for recursive calls. That’s quite hard to express as a patch to the specification since it requires changing multiple parts.

The report an error algorithm is modified with an additional step at the end:

  1. Call any error reporting steps defined in external specifications with script, line, col, message, and true if the error is handled, or false otherwise.

7.2. Console

Other specifications can define console steps. When any method of the console interface is called, with method name method and argument args:

  1. If that method does not call the Printer operation, call any console steps defined in external specification with arguments method, args, and undefined.

    Otherwise, at the point when the Printer operation is called with arguments name, printerArgs and options (which is undefined if the argument is not provided), call any console steps defined in external specification with arguments name, printerArgs, and options.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[CONSOLE]
Dominic Farolino; Robert Kowalski; Terin Stock. Console Standard. Living Standard. URL: https://console.spec.whatwg.org/
[CSS-PAINT-API-1]
Ian Kilpatrick; Dean Jackson. CSS Painting API Level 1. URL: https://drafts.css-houdini.org/css-paint-api-1/
[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[ECMAScript]
ECMAScript Language Specification. URL: https://tc39.es/ecma262/multipage/
[ENCODING]
Anne van Kesteren. Encoding Standard. Living Standard. URL: https://encoding.spec.whatwg.org/
[FETCH]
Anne van Kesteren. Fetch Standard. Living Standard. URL: https://fetch.spec.whatwg.org/
[HR-TIME-3]
Yoav Weiss. High Resolution Time. URL: https://w3c.github.io/hr-time/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[MEDIAQUERIES-5]
Dean Jackson; et al. Media Queries Level 5. URL: https://drafts.csswg.org/mediaqueries-5/
[MIMESNIFF]
Gordon P. Hemsley. MIME Sniffing Standard. Living Standard. URL: https://mimesniff.spec.whatwg.org/
[RESOURCE-TIMING-2]
Yoav Weiss; Noam Rosenthal. Resource Timing. URL: https://w3c.github.io/resource-timing/
[RFC4122]
P. Leach; M. Mealling; R. Salz. A Universally Unique IDentifier (UUID) URN Namespace. July 2005. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc4122
[RFC4648]
S. Josefsson. The Base16, Base32, and Base64 Data Encodings. October 2006. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc4648
[RFC6455]
I. Fette; A. Melnikov. The WebSocket Protocol. December 2011. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc6455
[RFC8610]
H. Birkholz; C. Vigano; C. Bormann. Concise Data Definition Language (CDDL): A Notational Convention to Express Concise Binary Object Representation (CBOR) and JSON Data Structures. June 2019. Proposed Standard. URL: https://www.rfc-editor.org/rfc/rfc8610
[SERVICE-WORKERS]
Jake Archibald; Marijn Kruisselbrink. Service Workers. URL: https://w3c.github.io/ServiceWorker/
[URL]
Anne van Kesteren. URL Standard. Living Standard. URL: https://url.spec.whatwg.org/
[WEBAUDIO]
Paul Adenot; Hongchan Choi. Web Audio API. URL: https://webaudio.github.io/web-audio-api/
[WEBDRIVER]
Simon Stewart; David Burns. WebDriver. URL: https://w3c.github.io/webdriver/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

Informative References

[COOKIES]
A. Barth. HTTP State Management Mechanism. April 2011. Proposed Standard. URL: https://httpwg.org/specs/rfc6265.html
[HTTP11]
R. Fielding, Ed.; M. Nottingham, Ed.; J. Reschke, Ed.. HTTP/1.1. June 2022. Internet Standard. URL: https://www.rfc-editor.org/rfc/rfc9112
[JSON-RPC]
JSON-RPC Working Group. JSON-RPC 2.0 Specification. 4 January 2013. URL: https://www.jsonrpc.org/specification
[SAME-SITE-COOKIES]
Mike West; Mark Goodwin. Same-Site Cookies. URL: https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-cookie-same-site

Issues Index

Surely there’s a better mechanism for doing this "wait for an event" thing.
Should we have something like microtasks to ensure this runs before any other tasks on the event loop?
Should this be an appendix?
Do we support > 1 connection for a single session?
Nothing seems to define what status code is used for UTF-8 errors.
Need to hook in to the session ending to allow the UA to close the listener if it wants.
Define creation of sandbox realm. This is going to return a SandboxWindowProxy wrapping window.
Define in detail how SandboxProxy works
Define how this works.
this is rather imprecise language, but hopefully it’s clear that the intent is that we send the response to the command before starting shutdown of the connections.
This needs to be generalized to work with realms too
This needs to be generalised to work with realms too
property specify how the ignore cache flag works. This needs to consider whether only the first load of a resource bypasses the cache (i.e. whether this is like initially clearing the cache and proceeding like normal), or whether resources not directly loaded by the HTML parser (e.g. loads initiated by scripts or stylesheets) also bypass the cache.
Are we surfacing enough information about what failed and why with an error here? What error code do we want? Is there going to be a problem where local ends parse the implementation-defined strings to figure out what actually went wrong?
This ought to be integrated into the update rendering algorithm in some more explicit way.
There is an open discussion about the behavior when closing the last top-level browsing context. We could expect to close the browser, close the session or leave this up to the implementation. [Issue #w3c/webdriver-bidi#170]
This ought to be integrated into the update rendering algorithm in some more explicit way.
the way this hooks into HTML feels very fragile. See https://github.com/whatwg/html/issues/6194
It’s unclear if we ought to only fire this event for browsing contexts that have active documents; navigation can also cause contexts to become inaccessible but not yet get discarded because bfcache.
Consider including the `sharedId` of the document node that initiated the request in addition to the context.
TODO distinguish between `List` and `Array`.
This has the wrong error code
This doesn’t handle lone surrogates
handle "stale object reference" case.
Add WASM types?
Should WindowProxy get attributes in a similar style to Node?
Describe `script.IteratorRemoteValue` serialization.
Describe `script.GeneratorRemoteValue` serialization.
Describe `script.ProxyRemoteValue` serialization.
handle String / Number / etc. wrapper objects specially?
reconsider mirror objects' lifecycle.
children and child nodes are different things. Either childNodeCount should reference to childNodes, or it should be renamed to childrenCount.
nodeValue can be null. Omit the field in such a case, or adjust type to allow null value.
this assumes for-in works on iterators
This has the wrong error code
TODO: Add timeout argument as described in the script.evaluate.
Extend this to also allow realm parents e.g. for nested workers? Or get all ancestor workers.
We might want to have a more sophisticated filter system than just a literal match.
Should the order here be better defined?
Do we want to key this on browsing context or top-level browsing context? The difference is in what happens if an event occurs in a frame and that frame is then navigated before the local end subscribes to log events for the top level context.
Lots more things require logging. CDP has LogEntryAdded types xml, javascript, network, storage, appcache, rendering, security, deprecation, worker, violation, intervention, recommendation, other. These are in addition to the js exception and console API types that are represented by different methods.
Allow implementation-defined log types
The actual patch might be better to split the algorithm into an outer algorithm that is called by external callers and an inner algorithm that’s used for recursive calls. That’s quite hard to express as a patch to the specification since it requires changing multiple parts.