From 1c9a77f3a12657a0b9401e53e960b4d5493b5a77 Mon Sep 17 00:00:00 2001 From: James Graham Date: Mon, 13 Mar 2023 10:51:24 +0000 Subject: [PATCH] Add network request interception Add a feature for intercepting network requests. Network request incercepts are created using the network.addIntercept command. This takes a URL pattern describing which resources to match, and a list of phases, which can be "beforeRequestSent", "responseStarted", or "authRequired". It returns a handle that can later be used with network.removeIntercept to unregister the intercept. When a request is intercepted, the corresponding network events get their `isBLocked` property set to true, and the client has to respond with a command in order to unblock the request. Blocking only occurs if a client is also subscribed to the relevant network events. Each request inteception phase has a corresponding event, and one or more commands to continue the request from that point. At the beforeRequestSent phase, the client may respond with continueRequest, which allows altering the request properties, but continues processing the request via the normal network stack. It may also respond with network.provideResponse, which allows providing a complete response body and prevents all further processing. At the afterResponse stage, the client may respond with network.continueResponse, which allows altering the response status and headers, or network.provideResponse, which overrides the network response with the client-provided response. At either phase the client may response with network.failRequest to cause the fetch to end with a network error. At the authRequired phase, the client must respond with network.continueWithAuth, to handle the request for credentials. Credentials can also be provided in the responseStarted phase (when the response has an approproate status code and authentication header), in which case those credentials will be used and a network.authRequired event will not be sent. --- index.bs | 1162 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 999 insertions(+), 163 deletions(-) diff --git a/index.bs b/index.bs index 33604f3a4..2731c575d 100644 --- a/index.bs +++ b/index.bs @@ -370,9 +370,9 @@ A BiDi session is a [=/session=] which has the [=BiDi flag=] set to true.
-The set of active BiDi sessions is given by: +The list of active BiDi sessions is given by: -1. Let |BiDi sessions| be a new [=/set=]. +1. Let |BiDi sessions| be a new [=/list=]. 1. For each |session| in [=active sessions=]: @@ -458,33 +458,30 @@ modules=]. WebDriver BiDi extends the set of [=error codes=] from [[WEBDRIVER|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 intercept +
Tried to remove an unknown [=network intercept=]. +
no such node
Tried to deserialize an unknown SharedReference. -
-
+
no such script +
Tried to remove an unknown [=preload script=]. +
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 intercept" /
              "no such node" /
              "no such script" /
              "session not created" /
@@ -612,19 +609,6 @@ To obtain a set of event names given an |name|:
 
 
-
-To emit events given |body| and |related browsing contexts|: - -1. [=Assert=]: |body| has [=map/size=] 2 and [=contains=] "method" - and "params". - -1. 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|. - -
- # Transport # {#transport} Message transport is provided using the WebSocket protocol. @@ -3221,6 +3205,13 @@ relating to network requests.
 
 NetworkCommand = (
+  network.AddIntercept //
+  network.ContinueRequest //
+  network.ContinueResponse //
+  network.ContinueWithAuth //
+  network.FailRequest //
+  network.ProvideResponse //
+  network.RemoveIntercept //
 )
 
 
@@ -3233,10 +3224,11 @@ NetworkResult = ( ) NetworkEvent = ( + network.AuthRequired // network.BeforeRequestSent // network.FetchError // - network.ResponseStarted // - network.ResponseCompleted + network.ResponseCompleted // + network.ResponseStarted ) @@ -3245,17 +3237,143 @@ 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. +### Network Intercepts ### {#network-intercepts} + +A network intercept is a mechanism to allow remote ends to intercept +and modify network requests and responses. + +A [=BiDi session=] has an intercept map which is initially an +empty [=/map=]. It's used to track the active network intercepts. + +A [=BiDi session=] has an blocked request map which is initially an +empty [=/map=]. It's used to track the requests which are actively being blocked. + +
+ +To get the network intercepts given |session|, |event|, and |request|: + +1. Let |session intercepts| be |session|'s [=intercept map=]. + +1. Let |intercepts| be an empty list. + +1. Run the steps under the first matching condition: +
+
|event| is "network.beforeRequestSent" +
Set |phase| to the beforeRequestSent +
|event| is "network.responseStarted" +
Set |phase| to "responseStarted". +
|event| is "network.authRequired" +
Set |phase| to "authRequired"». +
|event| is "network.responseCompleted" +
Return |intercepts|. +
+ +1. Let |url| be the result of running the [=URL serializer=] with |request|'s + [=request/URL=]. + +1. For each |intercept| → |intercept properties| of |session intercepts|: + + 1. If |intercept properties|[phases] [=set/contains=] |phase|: + + 1. For each |url pattern| in |intercept properties|[url + patterns]: + + 1. If [=URL pattern matches=] with |url pattern| and |url|: + + 1. [list/Append=] |intercept| to |intercepts| and break. + +1. Return |intercepts|. + +
+ +
+ +To decide if URL pattern matches given |url pattern| and |url|: + +1. TODO: write an algorithm to match |url| against |url pattern|. + +
+ ### Types ### {#module-network-types} +#### The network.AuthChallenge Type #### {#type-network-AuthChallenge} + +
+network.AuthChallenge = {
+  scheme: text,
+  realm: text,
+}
+
+ +
+ +To extract challenges given |response|: + +Issue: Should we include parameters other than realm? + +1. If |response|'s [=response/status=] is 401, let |header name| be + `WWW-Authenticate`. Otherwise if |response|'s [=response/status=] is 407, let + |header name| be `Proxy-Authenticate`. Otherwise return null. + +1. Let |challenges| be a new [=/list=]. + +1. For each (|name|, |value|) in |response|'s [=response/header list=]: + + Issue: as in Fetch it's unclear if this is the right way to handle multiple + headers, parsing issues, etc. + + 1. If |name| is a [=byte-case-insensitive=] match for |header name|: + + 1. Let |header challenges| be the result of parsing |value| into a list of + challenges, each consisting of a scheme and a list of parameters, each of + which is a tuple (name, value), according to the rules of [[!RFC9110]]. + + 1. For each |header challenge| in |header challenges|: + + 1. Let |scheme| be |header challenge|'s scheme. + + 1. Let |realm| be "". + + 1. For each (|param name|, |param value|) in |header challenge|'s + parameters: + 1. If |param name| equals `realm` let |realm| be + [=UTF-8 decode=] |param value|. + + 1. Let |challenge| be a new [=/map=] matching the + network.AuthChallenge production, with the + scheme field set to |scheme| and the realm + field set to |realm|. + + 1. [=list/Append=] |challenge| to |challenges|. + +1. Return |challenges|. + +
+ +#### The network.AuthCredentials Type #### {#type-network-AuthCredentials} + +
+network.AuthCredentials = {
+  type: "password",
+  username: text,
+  password: text,
+}
+
+ +The network.AuthCredentials type represents the response to a +request for authorization credentials. + #### The network.BaseParameters Type #### {#type-network-BaseParameters}
 network.BaseParameters = {
     context: BrowsingContext / null,
+    isBlocked: bool,
     navigation: Navigation / null,
     redirectCount: js-uint,
     request: network.RequestData,
     timestamp: js-uint,
+    ?intercepts: [*network.Intercept]
 }
 
@@ -3266,7 +3384,10 @@ Issue: 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|: +To process a network event given |session|, |event|, and |request|: + +1. Let |intercepts| be the result of [=get the network intercepts=] with + |session|, |event|, and |request|. 1. Let |request data| be the result of [=get the request data=] with |request|. @@ -3275,11 +3396,6 @@ To get the base network event data given |request| and |redirect coun 1. Let |context id| be null. -1. If |request|'s [=request/client=] is not null, let |related browsing contexts| - be the result of [=get related browsing contexts=] with |request|'s - [=request/client=]. Otherwise let |related browsing contexts| be an empty - set. - 1. If |request|'s [=request/window=] is an [=environment settings object=]: 1. Let |environment settings| be |request|'s [=request/window=] @@ -3288,15 +3404,55 @@ To get the base network event data given |request| and |redirect coun settings|' [=environment settings object/global object=], let |context id| be the [=browsing context id=] for that context. +1. Let |redirect count| be |request|'s [=redirect count=]. + 1. Let |timestamp| be a [=time value=] representing the current date and time in UTC. +1. If |intercepts| is not [=list/empty=], let |is blocked| be true, otherwise + let |is blocked| be false. + 1. 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|. + |timestamp|, the redirectCount field set to |redirect count|, the + isBlocked field set to |is blocked|, and intercepts + field set to |intercepts| if |is blocked| is true, or omitted otherwise. + +1. Return |params| + +
-1. Return (|related browsing contexts|, |params|) +#### The network.Body Type #### {#type-network-Body} + +
+network.Body = network.StringBody / network.Base64Body;
+
+network.StringBody = {
+  type: "string",
+  value: text,
+}
+
+network.Base64Body = {
+  type: "base64",
+  value: text
+}
+
+ +The network.Body type represents a request or response body, +possibly encoded. + +
+To get the body given |protocol body|: + +1. If |protocol body| matches the network.StringBody production, + let |body| be [=UTF-8 encode=] |protocol body|["value"]. + +1. Otherwise if |protocol body| matches the + network.Base64Bodyproduction. Let |body| be [=forgiving-base64 + decode=] |protocol body|["value"]. + +1. Return |body|
@@ -3304,11 +3460,11 @@ To get the base network event data given |request| and |redirect coun [=Remote end definition=] and [=local end definition=] -
+
 network.Cookie = {
     name: text,
     ? value: text,
-    ? binaryValue: [ uint ]
+    ? binaryValue: [ *(0..255) ]
     domain: text,
     path: text,
     ? expires: js-uint,
@@ -3480,12 +3636,19 @@ TODO: Add service worker fields
 
 [=Remote end definition=] and [=local end definition=]
 
-
+
 network.Header = {
-    name: text,
-    ? value: text,
-    ? binaryValue: [ uint ]
-};
+  name: text,
+  (network.StringHeaderValue // network.BinaryHeaderValue)
+}
+
+network.StringHeaderValue = {
+    value: text
+}
+
+network.BinaryHeaderValue = {
+   binaryValue: [ *(0..255) ]
+}
 
The network.Header type represents a single request header. @@ -3524,6 +3687,27 @@ To get a header given |name bytes| and |value bytes|: +
+To serialize a header given |header|: + +1. Let |name| be the result of [=UTF-8 encode=] with + |header|["name"]. + +1. If |header| [=map/contains=] "value", let |value| be [=UTF-8 + encode=] |header|["value"]. + +1. If |header| [=map/contains=] "binaryValue": + + 1. Let value be an empty [=byte sequence=]. + + 1. For each |byte| in |header|["binaryValue"]: + + 1. Append |byte| to |value| + +1. Return a [=/header=] (|name|, |value|). + +
+ #### The network.Initiator Type #### {#type-network-Initiator} [=Remote end definition=] and [=local end definition=] @@ -3581,6 +3765,16 @@ To get the initiator given |request|: +#### The network.Intercept Type #### {#type-network-Intercept} + +[=Remote end definition=] and [=local end definition=] + +
+network.Intercept = text
+
+ +The network.Intercept type represents the id of a [=network intercept=]. + #### The network.Request Type #### {#type-network-Request} [=Remote end definition=] and [=local end definition=] @@ -3709,7 +3903,8 @@ network.ResponseData = { bytesReceived: js-uint, headersSize: js-uint / null, bodySize: js-uint / null, - content: network.ResponseContent + content: network.ResponseContent, + ?authChallenge: network.AuthChallenge, };
@@ -3776,10 +3971,6 @@ To get the response data given |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. -1. For each (|name|, |value|) in |response|'s [=response/headers list=]: - - 1. Append the result of [=get a header=] with |name| and |value| to |headers|. - 1. Let |bytes received| be the total number of bytes transmitted as part of the HTTP response associated with |response|. @@ -3790,6 +3981,8 @@ To get the response data given |response|: 1. Let |content| be the result of [=get the response content info=] with |response|. +1. Let |auth challenges| be the result of [=extract challenges=] with |response|. + 1. 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 @@ -3798,168 +3991,786 @@ To get the response data given |response|: 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|. + bodySize field set to |body size|, content + field set to |content|, and the authChallenges field set to + |auth challenges| if it's not null, or omitted otherwise. -### Events ### {#module-network-event} +### Commands ### {#module-network-commands} + +#### The network.addIntercept Command #### {#command-network-addIntercept} + +The network.addIntercept command adds a +[=network intercept=]. -#### The network.beforeRequestSent Event #### {#event-network-beforeSendRequest}
-
Event Type
+
Command Type
-
-        network.BeforeRequestSent = {
-         method: "network.beforeRequestSent",
-         params: network.BeforeRequestSentParameters
-       }
+      
+      network.AddIntercept = {
+        method: "network.addIntercept",
+        params: network.AddInterceptParameters
+      }
 
-       network.BeforeRequestSentParameters = {
-         network.BaseParameters,
-         initiator: network.Initiator,
-       }
+      network.AddInterceptParameters = {
+        phases: [*network.InterceptPhase],
+        ?urlPatterns: [*text],
+      }
+
+      network.InterceptPhase = ("beforeRequestSent" /
+                                "responseStarted" /
+                                "authRequired")
       
+
Return Type
+
+
+      network.AddInterceptResult = {
+        intercept: network.Intercept
+      }
+    
+
-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. - -1. Let |redirect count| be |request|'s [=redirect count=]. - -1. Add |redirect count| to [=before request sent map=][|request|]. - -1. Let (|related browsing contexts|, |params|) be the result of [=get the base - network event data=] with |request|, and |redirect count|. +
+The [=remote end steps=] given |session| and |command parameters| are: -1. Let |initiator| be the result of [=get the initiator=] with |request|. +1. Let |intercept| be the string representation of a [[!RFC4122|UUID]]. -1. Set the initiator field of |params| to |initiator|. +1. Let |url patterns| be the urlPatterns field of |command + parameters| if present, or null otherwise. -1. Assert: |params| matches the network.BeforeRequestSentParameters - production. +1. Let |intercept map| be |session|'s [=intercept map=]. -1. Let |body| be a map matching the network.BeforeRequestSent - production, with the params field set to |params|. +1. Set |intercept map|[|intercept|] to a struct with url patterns + |url patterns| and phases |command parameters|["phases"]. -1. [=Emit events=] with |body| and |related browsing contexts|. +1. Return a new [=/map=] matching the + network.AddNetworkInterceptResult with the + intercept field set to |intercept|.
-#### The network.fetchError Event #### {#event-network-fetchError} +#### The network.continueRequest Command #### {#command-network-continueRequest} + +The network.continueRequest command continues a request +that's blocked by a [=network intercept=].
-
Event Type
+
Command Type
-
-        network.FetchError = {
-         method: "network.fetchError",
-         params: network.FetchErrorParameters
-       }
+      
+      network.ContinueRequest = {
+        method: "network.continueRequest",
+        params: network.ContinueRequestParameters
+      }
 
-       network.FetchErrorParameters = {
-         network.BaseParameters,
-         errorText: text,
-       }
+      network.ContinueRequestParameters = {
+        request: network.Request,
+        ?body: network.Body,
+        ?headers: [*network.Header],
+        ?method: text,
+        ?url: text,
+      }
       
+
Return Type
+
+
+      EmptyResult
+    
+
-This event is emitted when a network request ends in an error. +
+The [=remote end steps=] given |session| and |command parameters| are: -
+1. Let |blocked requests| be |session|'s [=blocked request map=]. -The [=remote end event trigger=] is the WebDriver BiDi fetch -error steps given |request|: +1. Let |request id| be |command parameters|["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|. +1. If |blocked requests| does not [=map/contain=] |request id| then return + [=error=] with [=error code=] [=invalid argument=]. - 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. + Issue: consider a "no such request" error. -1. Let (|related browsing contexts|, |params|) be the result of [=get the - base network event data=] given |request|. +1. Let (|request|, |phase|, response) be |blocked requests|[|request id|]. -1. TODO: Set the errorText field of |params|. +1. If |phase| is not "beforeRequestSent", then return [=error=] + with [=error code=] [=invalid argument=]. -1. Assert: |params| matches the network.FetchErrorParameters - production. + Issue: consider a "request already sent" error. -1. Let |body| be a map matching the network.FetchError - production, with the params field set to |params|. + -1. [=Emit events=] with |body| and |related browsing contexts|. +1. If |command parameters| [=map/contains=] "url": -
+ 1. Let |url record| be the result of applying the [=URL parser=] to |command + parameters|["url"], with [=base URL=] null. -#### The network.responseCompleted Event #### {#event-network-responseCompleted} + 1. If |url record| is failure, return [=error=] with [=error code=] [=invalid + argument=]. -
-
Event Type
-
-
-        network.ResponseCompleted = {
-         method: "network.responseCompleted",
-         params: network.ResponseCompletedParameters
-       }
+     TODO: Should we also resume here?
 
-       network.ResponseCompletedParameters = {
-         network.BaseParameters,
-         response: network.ResponseData,
-       }
-      
-
-
+ 1. Let |request|'s [=request/url=] be |url record|. -This event is emitted after the full response body is received. +1. If|command parameters| [=map/contains=] "method": -
-The [=remote end event trigger=] is the WebDriver BiDi response -completed steps given |request| and |response|: + 1. Let |request|'s [=request/method=] be |command + parameters|["method"]. -1. Let |redirect count| be |request|'s [=redirect count=]. +1. If |command parameters| [=map/contains=] "headers": -1. Assert: [=before request sent map=][|request|] contains |redirect count|. + 1. Let |headers| be an empty [=/header list=]. - Note: This implies that every caller needs to ensure that the [=WebDriver BiDi - before request sent=] steps are invoked with |request| before these steps. + 1. For |header| in |command parameters|["headers"]: -1. Let (|related browsing contexts|, |params|) be the result of [=get the - base network event data=] given |request| and |redirect count|. + 1. Append [=serialize a header=] with |header| to |headers|. -1. Let |response data| be the result of [=get the response data=] with |response|. + 1. Set |request|'s [=request/headers list=] to |headers|. -1. Set the response field of |params| to |response data|. +1. If |command parameters| [=map/contains=] "body": -1. Assert: |params| matches the network.ResponseCompletedParameters - production. + 1. Let |body| be [=get the body=] with |command + parameters|["body"]. -1. Let |body| be a map matching the network.ResponseCompleted - production, with the params field set to |params|. + 1. Set |request|'s [=request/body=] to |body|. + +1. [=Resume=] with "continue request", |request id|, and (null, + "incomplete"). -1. [=Emit events=] with |body| and |related browsing contexts|. +1. Return [=success=] with [=data=] null.
-#### The network.responseStarted Event #### {#event-network-responseStarted} +#### The network.continueResponse Command #### {#command-network-continueResponse} + +The network.continueResponse command continues a +response that's blocked by a [=network intercept=]. If called in the +beforeRequestSent phase, this allows the request to be fulfilled +without being sent. If called in the responseStarted phase, this +allows the fetch response to be modified.
-
Event Type
+
Command Type
-
-        network.ResponseStarted = {
+      
+      network.ContinueResponse = {
+        method: "network.continueResponse",
+        params: network.ContinueResponseParameters
+      }
+
+      network.ContinueResponseParameters = {
+        request: network.Request,
+        ?credentials: network.AuthCredentials,
+        ?headers: [*network.Header],
+        ?reasonPhrase: text,
+        ?statusCode: js-uint,
+      }
+      
+
+
Return Type
+
+
+      EmptyResult
+    
+
+
+ +
+To update the response given |session|, |command| and |command parameters|: + +1. Let |blocked requests| be |session|'s [=blocked request map=]. + +1. Let |request id| be |command parameters|["request"]. + +1. If |blocked requests| does not [=map/contain=] |request id| then return + [=error=] with [=error code=] [=invalid argument=]. + + Issue: consider a "no such request" error. + +1. Let (request, |phase|, |response|) be |blocked requests|[|request id|]. + +1. If |phase| is "beforeRequestSent" and |command| is + "continueResponse", return [=error=] with [=error code=] + "invalid argument". + + TODO: Consider a different error + +1. If |response| is null: + + + 1. Assert: |phase| is "beforeRequestSent". + + 1. Set |response| to a new [=/response=]. + +1. If|command parameters| [=map/contains=] "statusCode": + + 1. Set |responses|'s [=response/status=] be |command + parameters|["statusCode"]. + +1. If|command parameters| [=map/contains=] "reasonPhrase": + + 1. Set |responses|'s [=response/status message=] be [=UTF-8 encode=] with + |command parameters|["reasonPhrase"]. + +1. If |command parameters| [=map/contains=] "headers": + + 1. Let |headers| be an empty [=/header list=]. + + 1. For |header| in |command parameters|["headers"]: + + 1. Append [=serialize a header=] with |header| to |headers|. + + 1. Set |response|'s [=response/header list=] to |headers|. + +1. If |command parameters| [=map/contains=] "credentials": + + Issue: This doesn't have a way to cancel the auth. + + 1. Let |credentials| be |command parameters|["credentials"]. + + 1. Asssert: |credentials|{"type"] is "password". + + 1. Set |response|'s authentication credentials to + (|credentials|["username"], |credentials|["password"]) + +1. Return |response| + +
+ +
+The [=remote end steps=] given |session| and |command parameters| are: + +1. Let |request id| be |command parameters|["request"]. + +1. Let |response| be the result of [=trying=] to [=update the response=] with + |session|, "continueResponse" and |command parameters|. + +1. [=Resume=] with "continue request", |request id|, and + (|response|, "incomplete"). + +1. Return [=success=] with [=data=] null. + +
+ +#### The network.continueWithAuth Command #### {#command-network-continueWithAuth} + +The network.continueWithAuth command continues a +response that's blocked by a [=network intercept=] at the +authRequired phase. + +
+
Command Type
+
+
+      network.ContinueWithAuth = {
+        method: "network.continueWithAuth",
+        params: network.ContinueWithAuthParameters
+      }
+
+      network.ContinueWithAuthParameters = {
+        request: network.Request,
+        (network.ContinueWithAuthCredentials // network.ContinueWithAuthNoCredentials)
+      }
+
+      network.ContinueWithAuthCredentials = {
+        action: "provideCredentials", 
+        credentials: network.AuthCredentials
+      }
+
+      network.ContinueWithAuthNoCredentials = {
+        action: "default" / "cancel"
+      }
+      
+
+
Return Type
+
+
+      EmptyResult
+    
+
+
+ +
+The [=remote end steps=] given |session| and |command parameters| are: + +1. Let |blocked requests| be |session|'s [=blocked request map=]. + +1. Let |request id| be |command parameters|["request"]. + +1. If |blocked requests| does not [=map/contain=] |request id| then return + [=error=] with [=error code=] [=invalid argument=]. + + Issue: consider a "no such request" error. + +1. Let (request, |phase|, |response|) be |blocked requests|[|request id|]. + +1. If |phase| is not "authRequired", then return [=error=] + with [=error code=] [=invalid argument=]. + +1. If |command parameters| "action is "cancel", set + |response|'s authentication credentials to + "cancelled". + +1. If |command parameters| "action" is + "provideCredentials": + + 1. Let |credentials| be |command parameters|["credentials"]. + + 1. Asssert: |credentials|{"type"] is "password". + + 1. Set |response|'s authentication credentials to + (|credentials|["username"], |credentials|["password"]) + +1. [=Resume=] with "continue request", |request id|, and + (|response|, "incomplete"). + +1. Return [=success=] with [=data=] null. + +
+ +#### The network.failRequest Command #### {#command-network-failRequest} + +The network.failRequest command fails a +fetch that's blocked by a [=network intercept=]. + +
+
Command Type
+
+
+      network.FailRequest = {
+        method: "network.failRequest",
+        params: network.FailRequestParameters
+      }
+
+      network.FailRequestParameters = {
+        request: network.Request,
+      }
+  
+
+ +
+The [=remote end steps=] given |session| and |command parameters| are: + +1. Let |blocked requests| be |session|'s [=blocked request map=]. + +1. Let |request id| be |command parameters|["request"]. + +1. If |blocked requests| does not [=map/contain=] |request id| then return + [=error=] with [=error code=] [=invalid argument=]. + + Issue: consider a "no such request" error. + +1. Let (request, |phase|, response) be + |blocked requests|[|request id|]. + +1. If |phase| is "authRequired", then return [=error=] + with [=error code=] [=invalid argument=]. + +1. Let |response| be a new [=network error=]. + +1. TODO: Allow setting the precise kind of error + +1. [=Resume=] with "continue request", |request id|, and + (|response|, "complete"). + +1. Return [=success=] with data null. + +
+ +#### The network.provideResponse Command #### {#command-network-provideResponse} + +The network.provideResponse command continues a +response that's blocked by a [=network intercept=], by providing a complete +response and bypassing any further network request processing. + +
+
Command Type
+
+
+      network.ProvideResponse = {
+        method: "network.provideResponse",
+        params: network.ProvideResponseParameters
+      }
+
+      network.ProvideResponseParameters = {
+        request: network.Request,
+        ?body: network.Body,
+        ?headers: [*network.Header],
+        ?reasonPhrase: text,
+        ?statusCode: js-uint,
+      }
+      
+
+
Return Type
+
+
+      EmptyResult
+    
+
+
+ +
+The [=remote end steps=] given |session| and |command parameters| are: + +1. Let |request id| be |command parameters|["request"]. + +1. Let |response| be the result of [=trying=] to [=update the response=] with + |session|, "provideResponse", and |command parameters|. + +1. If |command parameters| [=map/contains=] "body": + + 1. Let |body| be [=get the body=] with |command parameters["body"]. + + 1. Set |response|'s [=response/body=] to |body| [=as a body=]. + +1. [=Resume=] with "continue request", |request id|, and + (|response|,"complete"). + +1. Return [=success=] with [=data=] null. + +
+ + +#### The network.removeIntercept Command #### {#command-network-removeIntercept} + +The network.removeIntercept command removes a +[=network intercept=]. + +
+
Command Type
+
+
+      network.RemoveIntercept = {
+        method: "network.removeIntercept",
+        params: network.RemoveInterceptParameters
+      }
+
+      network.RemoveInterceptParameters = {
+        intercept: network.Intercept
+      }
+    
+
+
Return Type
+
+
+      EmptyResult
+    
+
+
+ +
+The [=remote end steps=] given |session| and |command parameters| are: + +1. Let |intercept| be the value of the "intercept" field in |command + parameters|. + +1. Let |intercept map| be |session|'s [=intercept map=]. + +1. If |intercept map| does not contain |intercept|, return + [=error=] with [=error code=] [=no such intercept=]. + +1. Remove |intercept| from |intercept map|. + +1. Return null + +
+ + +### Events ### {#module-network-event} + +#### The network.authRequired Event #### {#event-network-authRequired} + +
+
Event Type
+
+
+      network.AuthRequired = {
+        method: "network.authRequired",
+        params: network.AuthRequiredParameters
+      }
+
+      network.AuthRequiredParameters = {
+        network.BaseParameters,
+        response: network.ResponseData
+      }
+      
+
+
Return Type
+
+
+      EmptyResult
+    
+
+
+ +This event is emitted when the user agent is going to prompt for authorization +credentials. + +
+The [=remote end event trigger=] is the WebDriver BiDi auth required +steps given |request| and |response|: + +1. Let |redirect count| be |request|'s [=redirect count=]. + +1. 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. + +1. If |request|'s [=request/client=] is not null, let |related browsing contexts| + be the result of [=get related browsing contexts=] with |request|'s + [=request/client=]. Otherwise let |related browsing contexts| be an empty + set. + +1. Let |response status| be "incomplete". + +1. For each |session| in the [=set of sessions for which an event is enabled=] + given "network.authRequired" and |related browsing contexts|: + + 1. Let |params| be the result of [=process a network event=] with |session| + "network.authRequired", and |request|. + + 1. Let |response data| be the result of [=get the response data=] with |response|. + + 1. Assert: |response data| [=map/contains=] "authChallenge". + + 1. Set the response field of |params| to |response data|. + + 1. Assert: |params| matches the network.AuthRequiredParameters + production. + + 1. Let |body| be a map matching the network.AuthRequired + production, with the params field set to |params|. + + 1. [=Emit an event=] with |session| and |body|. + + 1. If |params|["isBlocked"] is true: + + 1. Let |blocked requests| be |session|'s [=blocked request map=]. + + 1. Let |request id| be |request|'s [=request id=]. + + 1. Set |blocked requests|[|request id|] to (|request|, + "authRequired", |response|). + + + 1. [=Await=] with «"continue request"», and |request id|. + +
+ +#### The network.beforeRequestSent Event #### {#event-network-beforeSendRequest} +
+
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. + +1. Let |redirect count| be |request|'s [=redirect count=]. + +1. Add |redirect count| to [=before request sent map=][|request|]. + +1. If |request|'s [=request/client=] is not null, let |related browsing contexts| + be the result of [=get related browsing contexts=] with |request|'s + [=request/client=]. Otherwise let |related browsing contexts| be an empty + set. + +1. Let |response| be null. + +1. Let |response status| be "incomplete". + +1. For each |session| in the [=set of sessions for which an event is enabled=] + given "network.beforeRequestSent" and |related browsing contexts|: + + 1. Let |params| be the result of [=process a network event=] with |session|, + "network.beforeRequestSent", and |request|. + + 1. If |params| is null then continue. + + 1. Let |initiator| be the result of [=get the initiator=] with |request|. + + 1. Set the initiator field of |params| to |initiator|. + + 1. Assert: |params| matches the network.BeforeRequestSentParameters + production. + + 1. Let |body| be a map matching the network.BeforeRequestSent + production, with the params field set to |params|. + + 1. [=Emit an event=] with |session| and |body|. + + 1. If |params|["isBlocked"] is true, then: + + 1. Let |blocked requests| be |session|'s [=blocked request map=]. + + 1. Let |request id| be |request|'s [=request id=]. + + 1. Set |blocked requests|[|request id|] to (|request|, + "beforeRequestSent", null). + + 1. Let (|response|, |status|) be [=await=] with «"continue + request"», and |request|'s [=request id=]. + + 1. If |status| is "complete" set |response status| to |status|. + + 1. [=map/Remove=] |blocked requests|[|request id|]. + + Note: While waiting, no further processing of the request occurs. + +1. Return (|response|, |response status|). + +
+ +#### The network.fetchError Event #### {#event-network-fetchError} + +
+
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. + +1. If |request|'s [=request/client=] is not null, let |related browsing contexts| + be the result of [=get related browsing contexts=] with |request|'s + [=request/client=]. Otherwise let |related browsing contexts| be an empty + set. + +1. For each |session| in the [=set of sessions for which an event is enabled=] + given "network.fetchError" and |related browsing contexts|: + + 1. Let |params| be the result of [=process a network event=] with |session| + "network.fetchError", and |request|. + + + 1. TODO: Set the errorText field of |params|. + + 1. Assert: |params| matches the network.FetchErrorParameters + production. + + 1. Let |body| be a map matching the network.FetchError + production, with the params field set to |params|. + + 1. [=Emit an event=] with |session| and |body|. + +
+ +#### The network.responseCompleted Event #### {#event-network-responseCompleted} + +
+
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=]. + +1. 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. + +1. If |request|'s [=request/client=] is not null, let |related browsing contexts| + be the result of [=get related browsing contexts=] with |request|'s + [=request/client=]. Otherwise let |related browsing contexts| be an empty + set. + +1. For each |session| in the [=set of sessions for which an event is enabled=] + given "network.responseCompleted" and |related browsing contexts|: + + 1. Let |params| be the result of [=process a network event=] with |session| + "network.responseCompleted", and |request|. + + 1. Assert: |params|["isBlocked"] is false. + + 1. Let |response data| be the result of [=get the response data=] with |response|. + + 1. Set the response field of |params| to |response data|. + + 1. Assert: |params| matches the network.ResponseCompletedParameters + production. + + 1. Let |body| be a map matching the network.ResponseCompleted + production, with the params field set to |params|. + + 1. [=Emit an event=] with |session| and |body|. + +
+ +#### The network.responseStarted Event #### {#event-network-responseStarted} + +
+
Event Type
+
+
+        network.ResponseStarted = {
          method: "network.responseStarted",
          params: network.ResponseStartedParameters
        }
@@ -3986,20 +4797,46 @@ started steps given |request| and |response|:
    Note: This implies that every caller needs to ensure that the [=WebDriver BiDi
    before request sent=] steps are invoked with |request| before these steps.
 
-1. Let (|related browsing contexts|, |params|) be the result of [=get the base
-   network event data=] with |request| and |redirect count|.
+1. If |request|'s [=request/client=] is not null, let |related browsing contexts|
+   be the result of [=get related browsing contexts=] with |request|'s
+   [=request/client=]. Otherwise let |related browsing contexts| be an empty
+   set.
+
+1. Let |response status| be "incomplete".
 
-1. Let |response data| be the result of [=get the response data=] with |response|.
+1. For each |session| in the [=set of sessions for which an event is enabled=]
+   given "network.responseStarted" and |related browsing contexts|:
 
-1. Set the response field of |params| to |response data|.
+  1. Let |params| be the result of [=process a network event=] with |session|
+     "network.responseStarted", and |request|.
 
-1. Assert: |params| matches the network.ResponseStartedParameters
-   production.
+  1. Let |response data| be the result of [=get the response data=] with |response|.
 
-1. Let |body| be a map matching the network.ResponseStarted
-   production, with the params field set to |params|.
+  1. Set the response field of |params| to |response data|.
+
+  1. Assert: |params| matches the network.ResponseStartedParameters
+     production.
+
+  1. Let |body| be a map matching the network.ResponseStarted
+     production, with the params field set to |params|.
+
+  1. [=Emit an event=] with |session| and |body|.
+
+  1. If |params|["isBlocked"] is true:
+
+    1. Let |blocked requests| be |session|'s [=blocked request map=].
+
+    1. Let |request id| be |request|'s [=request id=].
+
+    1. Set |blocked requests|[|request id|] to (|request|,
+       "beforeRequestSent", |response|).
+
+    1. Let (|response|, |status|) be [=await=] with «"continue request"», and
+       |request id|.
+
+    1. If |status| is "complete", set |response status| to |status|.
 
-1. [=Emit events=] with |body| and |related browsing contexts|.
+1. Return (|response|, |response status|).
 
 
@@ -4745,8 +5582,7 @@ script.WorkletRealmInfo = {
Note: there's a 1:1 relationship between the script.RealmInfo - variants and values of - script.RealmType. + variants and values of script.RealmType. The script.RealmInfo type represents the properties of a realm.