forked from mirrors/gecko-dev
Depends on D185997 Differential Revision: https://phabricator.services.mozilla.com/D185456
746 lines
20 KiB
JavaScript
746 lines
20 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
import { Module } from "chrome://remote/content/shared/messagehandler/Module.sys.mjs";
|
|
|
|
const lazy = {};
|
|
|
|
ChromeUtils.defineESModuleGetters(lazy, {
|
|
assert: "chrome://remote/content/shared/webdriver/Assert.sys.mjs",
|
|
error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs",
|
|
generateUUID: "chrome://remote/content/shared/UUID.sys.mjs",
|
|
matchURLPattern:
|
|
"chrome://remote/content/shared/webdriver/URLPattern.sys.mjs",
|
|
notifyNavigationStarted:
|
|
"chrome://remote/content/shared/NavigationManager.sys.mjs",
|
|
NetworkListener:
|
|
"chrome://remote/content/shared/listeners/NetworkListener.sys.mjs",
|
|
parseURLPattern:
|
|
"chrome://remote/content/shared/webdriver/URLPattern.sys.mjs",
|
|
TabManager: "chrome://remote/content/shared/TabManager.sys.mjs",
|
|
WindowGlobalMessageHandler:
|
|
"chrome://remote/content/shared/messagehandler/WindowGlobalMessageHandler.sys.mjs",
|
|
});
|
|
|
|
/**
|
|
* @typedef {object} BaseParameters
|
|
* @property {string=} context
|
|
* @property {Array<string>?} intercepts
|
|
* @property {boolean} isBlocked
|
|
* @property {Navigation=} navigation
|
|
* @property {number} redirectCount
|
|
* @property {RequestData} request
|
|
* @property {number} timestamp
|
|
*/
|
|
|
|
/**
|
|
* @typedef {object} BlockedRequest
|
|
* @property {NetworkEventRecord} networkEventRecord
|
|
* @property {InterceptPhase} phase
|
|
*/
|
|
|
|
/**
|
|
* Enum of possible BytesValue types.
|
|
*
|
|
* @readonly
|
|
* @enum {BytesValueType}
|
|
*/
|
|
const BytesValueType = {
|
|
Base64: "base64",
|
|
String: "string",
|
|
};
|
|
|
|
/**
|
|
* @typedef {object} BytesValue
|
|
* @property {BytesValueType} type
|
|
* @property {string} value
|
|
*/
|
|
|
|
/**
|
|
* @typedef {object} Cookie
|
|
* @property {string} domain
|
|
* @property {number=} expires
|
|
* @property {boolean} httpOnly
|
|
* @property {string} name
|
|
* @property {string} path
|
|
* @property {('lax' | 'none' | 'strict')} sameSite
|
|
* @property {boolean} secure
|
|
* @property {number} size
|
|
* @property {BytesValue} value
|
|
*/
|
|
|
|
/**
|
|
* @typedef {object} FetchTimingInfo
|
|
* @property {number} timeOrigin
|
|
* @property {number} requestTime
|
|
* @property {number} redirectStart
|
|
* @property {number} redirectEnd
|
|
* @property {number} fetchStart
|
|
* @property {number} dnsStart
|
|
* @property {number} dnsEnd
|
|
* @property {number} connectStart
|
|
* @property {number} connectEnd
|
|
* @property {number} tlsStart
|
|
* @property {number} requestStart
|
|
* @property {number} responseStart
|
|
* @property {number} responseEnd
|
|
*/
|
|
|
|
/**
|
|
* @typedef {object} Header
|
|
* @property {string} name
|
|
* @property {BytesValue} value
|
|
*/
|
|
|
|
/**
|
|
* @typedef {string} InitiatorType
|
|
*/
|
|
|
|
/**
|
|
* Enum of possible initiator types.
|
|
*
|
|
* @readonly
|
|
* @enum {InitiatorType}
|
|
*/
|
|
const InitiatorType = {
|
|
Other: "other",
|
|
Parser: "parser",
|
|
Preflight: "preflight",
|
|
Script: "script",
|
|
};
|
|
|
|
/**
|
|
* @typedef {object} Initiator
|
|
* @property {InitiatorType} type
|
|
* @property {number=} columnNumber
|
|
* @property {number=} lineNumber
|
|
* @property {string=} request
|
|
* @property {StackTrace=} stackTrace
|
|
*/
|
|
|
|
/**
|
|
* Enum of intercept phases.
|
|
*
|
|
* @readonly
|
|
* @enum {InterceptPhase}
|
|
*/
|
|
const InterceptPhase = {
|
|
AuthRequired: "authRequired",
|
|
BeforeRequestSent: "beforeRequestSent",
|
|
ResponseStarted: "responseStarted",
|
|
};
|
|
|
|
/**
|
|
* @typedef {object} InterceptProperties
|
|
* @property {Array<InterceptPhase>} phases
|
|
* @property {Array<URLPattern>} urlPatterns
|
|
*/
|
|
|
|
/**
|
|
* @typedef {object} RequestData
|
|
* @property {number|null} bodySize
|
|
* Defaults to null.
|
|
* @property {Array<Cookie>} cookies
|
|
* @property {Array<Header>} headers
|
|
* @property {number} headersSize
|
|
* @property {string} method
|
|
* @property {string} request
|
|
* @property {FetchTimingInfo} timings
|
|
* @property {string} url
|
|
*/
|
|
|
|
/**
|
|
* @typedef {object} BeforeRequestSentParametersProperties
|
|
* @property {Initiator} initiator
|
|
*/
|
|
|
|
/* eslint-disable jsdoc/valid-types */
|
|
/**
|
|
* Parameters for the BeforeRequestSent event
|
|
*
|
|
* @typedef {BaseParameters & BeforeRequestSentParametersProperties} BeforeRequestSentParameters
|
|
*/
|
|
/* eslint-enable jsdoc/valid-types */
|
|
|
|
/**
|
|
* @typedef {object} ResponseContent
|
|
* @property {number|null} size
|
|
* Defaults to null.
|
|
*/
|
|
|
|
/**
|
|
* @typedef {object} ResponseData
|
|
* @property {string} url
|
|
* @property {string} protocol
|
|
* @property {number} status
|
|
* @property {string} statusText
|
|
* @property {boolean} fromCache
|
|
* @property {Array<Header>} headers
|
|
* @property {string} mimeType
|
|
* @property {number} bytesReceived
|
|
* @property {number|null} headersSize
|
|
* Defaults to null.
|
|
* @property {number|null} bodySize
|
|
* Defaults to null.
|
|
* @property {ResponseContent} content
|
|
*/
|
|
|
|
/**
|
|
* @typedef {object} ResponseStartedParametersProperties
|
|
* @property {ResponseData} response
|
|
*/
|
|
|
|
/* eslint-disable jsdoc/valid-types */
|
|
/**
|
|
* Parameters for the ResponseStarted event
|
|
*
|
|
* @typedef {BaseParameters & ResponseStartedParametersProperties} ResponseStartedParameters
|
|
*/
|
|
/* eslint-enable jsdoc/valid-types */
|
|
|
|
/**
|
|
* @typedef {object} ResponseCompletedParametersProperties
|
|
* @property {ResponseData} response
|
|
*/
|
|
|
|
/**
|
|
* @typedef {object} URLPatternPattern
|
|
* @property {'pattern'} type
|
|
* @property {string=} protocol
|
|
* @property {string=} hostname
|
|
* @property {string=} port
|
|
* @property {string=} pathname
|
|
* @property {string=} search
|
|
*/
|
|
|
|
/**
|
|
* @typedef {object} URLPatternString
|
|
* @property {'string'} type
|
|
* @property {string} pattern
|
|
*/
|
|
|
|
/**
|
|
* @typedef {(URLPatternPattern|URLPatternString)} URLPattern
|
|
*/
|
|
|
|
/* eslint-disable jsdoc/valid-types */
|
|
/**
|
|
* Parameters for the ResponseCompleted event
|
|
*
|
|
* @typedef {BaseParameters & ResponseCompletedParametersProperties} ResponseCompletedParameters
|
|
*/
|
|
/* eslint-enable jsdoc/valid-types */
|
|
|
|
class NetworkModule extends Module {
|
|
#blockedRequests;
|
|
#interceptMap;
|
|
#networkListener;
|
|
#subscribedEvents;
|
|
|
|
constructor(messageHandler) {
|
|
super(messageHandler);
|
|
|
|
// Map of request id to BlockedRequest
|
|
this.#blockedRequests = new Map();
|
|
|
|
// Map of intercept id to InterceptProperties
|
|
this.#interceptMap = new Map();
|
|
|
|
// Set of event names which have active subscriptions
|
|
this.#subscribedEvents = new Set();
|
|
|
|
this.#networkListener = new lazy.NetworkListener();
|
|
this.#networkListener.on("before-request-sent", this.#onBeforeRequestSent);
|
|
this.#networkListener.on("response-completed", this.#onResponseEvent);
|
|
this.#networkListener.on("response-started", this.#onResponseEvent);
|
|
}
|
|
|
|
destroy() {
|
|
this.#networkListener.off("before-request-sent", this.#onBeforeRequestSent);
|
|
this.#networkListener.off("response-completed", this.#onResponseEvent);
|
|
this.#networkListener.off("response-started", this.#onResponseEvent);
|
|
this.#networkListener.destroy();
|
|
|
|
this.#blockedRequests = null;
|
|
this.#interceptMap = null;
|
|
this.#subscribedEvents = null;
|
|
}
|
|
|
|
/**
|
|
* Adds a network intercept, which allows to intercept and modify network
|
|
* requests and responses.
|
|
*
|
|
* The network intercept will be created for the provided phases
|
|
* (InterceptPhase) and for specific url patterns. When a network event
|
|
* corresponding to an intercept phase has a URL which matches any url pattern
|
|
* of any intercept, the request will be suspended.
|
|
*
|
|
* @param {object=} options
|
|
* @param {Array<InterceptPhase>} options.phases
|
|
* The phases where this intercept should be checked.
|
|
* @param {Array<URLPattern>=} options.urlPatterns
|
|
* The URL patterns for this intercept. Optional, defaults to empty array.
|
|
*
|
|
* @returns {object}
|
|
* An object with the following property:
|
|
* - intercept {string} The unique id of the network intercept.
|
|
*
|
|
* @throws {InvalidArgumentError}
|
|
* Raised if an argument is of an invalid type or value.
|
|
*/
|
|
addIntercept(options = {}) {
|
|
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1845345.
|
|
this.assertExperimentalCommandsEnabled("network.addIntercept");
|
|
|
|
const { phases, urlPatterns = [] } = options;
|
|
|
|
lazy.assert.array(
|
|
phases,
|
|
`Expected "phases" to be an array, got ${phases}`
|
|
);
|
|
|
|
if (!options.phases.length) {
|
|
throw new lazy.error.InvalidArgumentError(
|
|
`Expected "phases" to contain at least one phase, got an empty array`
|
|
);
|
|
}
|
|
|
|
const supportedInterceptPhases = Object.values(InterceptPhase);
|
|
for (const phase of phases) {
|
|
if (!supportedInterceptPhases.includes(phase)) {
|
|
throw new lazy.error.InvalidArgumentError(
|
|
`Expected "phases" values to be one of ${supportedInterceptPhases}, got ${phase}`
|
|
);
|
|
}
|
|
}
|
|
|
|
lazy.assert.array(
|
|
urlPatterns,
|
|
`Expected "urlPatterns" to be an array, got ${urlPatterns}`
|
|
);
|
|
|
|
const parsedPatterns = urlPatterns.map(urlPattern =>
|
|
lazy.parseURLPattern(urlPattern)
|
|
);
|
|
|
|
const interceptId = lazy.generateUUID();
|
|
this.#interceptMap.set(interceptId, {
|
|
phases,
|
|
urlPatterns: parsedPatterns,
|
|
});
|
|
|
|
return {
|
|
intercept: interceptId,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Removes an existing network intercept.
|
|
*
|
|
* @param {object=} options
|
|
* @param {string} options.intercept
|
|
* The id of the intercept to remove.
|
|
*
|
|
* @throws {InvalidArgumentError}
|
|
* Raised if an argument is of an invalid type or value.
|
|
* @throws {NoSuchInterceptError}
|
|
* Raised if the intercept id could not be found in the internal intercept
|
|
* map.
|
|
*/
|
|
removeIntercept(options = {}) {
|
|
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1845345.
|
|
this.assertExperimentalCommandsEnabled("network.removeIntercept");
|
|
const { intercept } = options;
|
|
|
|
lazy.assert.string(
|
|
intercept,
|
|
`Expected "intercept" to be a string, got ${intercept}`
|
|
);
|
|
|
|
if (!this.#interceptMap.has(intercept)) {
|
|
throw new lazy.error.NoSuchInterceptError(
|
|
`Network intercept with id ${intercept} not found`
|
|
);
|
|
}
|
|
|
|
this.#interceptMap.delete(intercept);
|
|
}
|
|
|
|
#getContextInfo(browsingContext) {
|
|
return {
|
|
contextId: browsingContext.id,
|
|
type: lazy.WindowGlobalMessageHandler.type,
|
|
};
|
|
}
|
|
|
|
#getNetworkIntercepts(event, requestData) {
|
|
const intercepts = [];
|
|
|
|
let phase;
|
|
switch (event) {
|
|
case "network.beforeRequestSent":
|
|
phase = InterceptPhase.BeforeRequestSent;
|
|
break;
|
|
case "network.responseStarted":
|
|
phase = InterceptPhase.ResponseStarted;
|
|
break;
|
|
case "network.authRequired":
|
|
phase = InterceptPhase.AuthRequired;
|
|
break;
|
|
case "network.responseCompleted":
|
|
// The network.responseCompleted event does not match any interception
|
|
// phase. Return immediately.
|
|
return intercepts;
|
|
}
|
|
|
|
const url = requestData.url;
|
|
for (const [interceptId, intercept] of this.#interceptMap) {
|
|
if (intercept.phases.includes(phase)) {
|
|
const urlPatterns = intercept.urlPatterns;
|
|
if (
|
|
!urlPatterns.length ||
|
|
urlPatterns.some(pattern => lazy.matchURLPattern(pattern, url))
|
|
) {
|
|
intercepts.push(interceptId);
|
|
}
|
|
}
|
|
}
|
|
|
|
return intercepts;
|
|
}
|
|
|
|
#getNavigationId(eventName, isNavigationRequest, browsingContext, url) {
|
|
if (!isNavigationRequest) {
|
|
// Not a navigation request return null.
|
|
return null;
|
|
}
|
|
|
|
let navigation =
|
|
this.messageHandler.navigationManager.getNavigationForBrowsingContext(
|
|
browsingContext
|
|
);
|
|
|
|
// `onBeforeRequestSent` might be too early for the NavigationManager.
|
|
// If there is no ongoing navigation, create one ourselves.
|
|
// TODO: Bug 1835704 to detect navigations earlier and avoid this.
|
|
if (
|
|
eventName === "network.beforeRequestSent" &&
|
|
(!navigation || navigation.finished)
|
|
) {
|
|
navigation = lazy.notifyNavigationStarted({
|
|
contextDetails: { context: browsingContext },
|
|
url,
|
|
});
|
|
}
|
|
|
|
return navigation ? navigation.navigationId : null;
|
|
}
|
|
|
|
#onBeforeRequestSent = (name, data) => {
|
|
const {
|
|
contextId,
|
|
isNavigationRequest,
|
|
redirectCount,
|
|
requestChannel,
|
|
requestData,
|
|
timestamp,
|
|
} = data;
|
|
|
|
const protocolEventName = "network.beforeRequestSent";
|
|
const isListening = this.messageHandler.eventsDispatcher.hasListener(
|
|
protocolEventName,
|
|
{ contextId }
|
|
);
|
|
if (!isListening) {
|
|
// If there are no listeners subscribed to this event and this context,
|
|
// bail out.
|
|
return;
|
|
}
|
|
|
|
const baseParameters = this.#processNetworkEvent(protocolEventName, {
|
|
contextId,
|
|
isNavigationRequest,
|
|
redirectCount,
|
|
requestData,
|
|
timestamp,
|
|
});
|
|
|
|
// Bug 1805479: Handle the initiator, including stacktrace details.
|
|
const initiator = {
|
|
type: InitiatorType.Other,
|
|
};
|
|
|
|
const beforeRequestSentEvent = this.#serializeNetworkEvent({
|
|
...baseParameters,
|
|
initiator,
|
|
});
|
|
|
|
const browsingContext = lazy.TabManager.getBrowsingContextById(contextId);
|
|
this.emitEvent(
|
|
protocolEventName,
|
|
beforeRequestSentEvent,
|
|
this.#getContextInfo(browsingContext)
|
|
);
|
|
|
|
if (beforeRequestSentEvent.isBlocked) {
|
|
// TODO: Requests suspended in beforeRequestSent still reach the server at
|
|
// the moment. https://bugzilla.mozilla.org/show_bug.cgi?id=1849686
|
|
requestChannel.suspend();
|
|
|
|
this.#blockedRequests.set(beforeRequestSentEvent.request.request, {
|
|
request: requestChannel,
|
|
phase: InterceptPhase.BeforeRequestSent,
|
|
});
|
|
|
|
// TODO: Once we implement network.continueRequest, we should create a
|
|
// promise here which will wait until the request is resumed and removes
|
|
// the request from the blockedRequests. See Bug 1850680.
|
|
}
|
|
};
|
|
|
|
#onResponseEvent = (name, data) => {
|
|
const {
|
|
contextId,
|
|
isNavigationRequest,
|
|
redirectCount,
|
|
requestChannel,
|
|
requestData,
|
|
responseChannel,
|
|
responseData,
|
|
timestamp,
|
|
} = data;
|
|
|
|
const protocolEventName =
|
|
name === "response-started"
|
|
? "network.responseStarted"
|
|
: "network.responseCompleted";
|
|
const isListening = this.messageHandler.eventsDispatcher.hasListener(
|
|
protocolEventName,
|
|
{ contextId }
|
|
);
|
|
if (!isListening) {
|
|
// If there are no listeners subscribed to this event and this context,
|
|
// bail out.
|
|
return;
|
|
}
|
|
|
|
const baseParameters = this.#processNetworkEvent(protocolEventName, {
|
|
contextId,
|
|
isNavigationRequest,
|
|
redirectCount,
|
|
requestData,
|
|
timestamp,
|
|
});
|
|
|
|
const responseEvent = this.#serializeNetworkEvent({
|
|
...baseParameters,
|
|
response: responseData,
|
|
});
|
|
|
|
const browsingContext = lazy.TabManager.getBrowsingContextById(contextId);
|
|
this.emitEvent(
|
|
protocolEventName,
|
|
responseEvent,
|
|
this.#getContextInfo(browsingContext)
|
|
);
|
|
|
|
if (
|
|
protocolEventName === "network.responseStarted" &&
|
|
responseEvent.isBlocked
|
|
) {
|
|
requestChannel.suspend();
|
|
|
|
this.#blockedRequests.set(responseEvent.request.request, {
|
|
request: requestChannel,
|
|
response: responseChannel,
|
|
phase: InterceptPhase.ResponseStarted,
|
|
});
|
|
|
|
// TODO: Once we implement network.continueRequest, we should create a
|
|
// promise here which will wait until the request is resumed and removes
|
|
// the request from the blockedRequests. See Bug 1850680.
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Process the network event data for a given network event name and create
|
|
* the corresponding base parameters.
|
|
*
|
|
* @param {string} eventName
|
|
* One of the supported network event names.
|
|
* @param {object} data
|
|
* @param {string} data.contextId
|
|
* The browsing context id for the network event.
|
|
* @param {boolean} data.isNavigationRequest
|
|
* True if the network event is related to a navigation request.
|
|
* @param {number} data.redirectCount
|
|
* The redirect count for the network event.
|
|
* @param {RequestData} data.requestData
|
|
* The network.RequestData information for the network event.
|
|
* @param {number} data.timestamp
|
|
* The timestamp when the network event was created.
|
|
*/
|
|
#processNetworkEvent(eventName, data) {
|
|
const {
|
|
contextId,
|
|
isNavigationRequest,
|
|
redirectCount,
|
|
requestData,
|
|
timestamp,
|
|
} = data;
|
|
|
|
const browsingContext = lazy.TabManager.getBrowsingContextById(contextId);
|
|
|
|
const navigation = this.#getNavigationId(
|
|
eventName,
|
|
isNavigationRequest,
|
|
browsingContext,
|
|
requestData.url
|
|
);
|
|
const intercepts = this.#getNetworkIntercepts(eventName, requestData);
|
|
const isBlocked = !!intercepts.length;
|
|
|
|
const baseParameters = {
|
|
context: contextId,
|
|
isBlocked,
|
|
navigation,
|
|
redirectCount,
|
|
request: requestData,
|
|
timestamp,
|
|
};
|
|
|
|
if (isBlocked) {
|
|
baseParameters.intercepts = intercepts;
|
|
}
|
|
|
|
return baseParameters;
|
|
}
|
|
|
|
#serializeHeadersOrCookies(headersOrCookies) {
|
|
return headersOrCookies.map(item => ({
|
|
name: item.name,
|
|
value: this.#serializeStringAsBytesValue(item.value),
|
|
}));
|
|
}
|
|
|
|
/**
|
|
* Serialize in-place all cookies and headers arrays found in a given network
|
|
* event payload.
|
|
*
|
|
* @param {object} networkEvent
|
|
* The network event parameters object to serialize.
|
|
* @returns {object}
|
|
* The serialized network event parameters.
|
|
*/
|
|
#serializeNetworkEvent(networkEvent) {
|
|
// Make a shallow copy of networkEvent before serializing the headers and
|
|
// cookies arrays in request/response.
|
|
const serialized = { ...networkEvent };
|
|
|
|
// Make a shallow copy of the request data.
|
|
serialized.request = { ...networkEvent.request };
|
|
serialized.request.cookies = this.#serializeHeadersOrCookies(
|
|
networkEvent.request.cookies
|
|
);
|
|
serialized.request.headers = this.#serializeHeadersOrCookies(
|
|
networkEvent.request.headers
|
|
);
|
|
|
|
if (networkEvent.response?.headers) {
|
|
// Make a shallow copy of the response data.
|
|
serialized.response = { ...networkEvent.response };
|
|
serialized.response.headers = this.#serializeHeadersOrCookies(
|
|
networkEvent.response.headers
|
|
);
|
|
}
|
|
|
|
return serialized;
|
|
}
|
|
|
|
/**
|
|
* Serialize a string value as BytesValue.
|
|
*
|
|
* Note: This does not attempt to fully implement serialize protocol bytes
|
|
* (https://w3c.github.io/webdriver-bidi/#serialize-protocol-bytes) as the
|
|
* header values read from the Channel are already serialized as strings at
|
|
* the moment.
|
|
*
|
|
* @param {string} value
|
|
* The value to serialize.
|
|
*/
|
|
#serializeStringAsBytesValue(value) {
|
|
// TODO: For now, we handle all headers and cookies with the "string" type.
|
|
// See Bug 1835216 to add support for "base64" type and handle non-utf8
|
|
// values.
|
|
return {
|
|
type: BytesValueType.String,
|
|
value,
|
|
};
|
|
}
|
|
|
|
#startListening(event) {
|
|
if (this.#subscribedEvents.size == 0) {
|
|
this.#networkListener.startListening();
|
|
}
|
|
this.#subscribedEvents.add(event);
|
|
}
|
|
|
|
#stopListening(event) {
|
|
this.#subscribedEvents.delete(event);
|
|
if (this.#subscribedEvents.size == 0) {
|
|
this.#networkListener.stopListening();
|
|
}
|
|
}
|
|
|
|
#subscribeEvent(event) {
|
|
if (this.constructor.supportedEvents.includes(event)) {
|
|
this.#startListening(event);
|
|
}
|
|
}
|
|
|
|
#unsubscribeEvent(event) {
|
|
if (this.constructor.supportedEvents.includes(event)) {
|
|
this.#stopListening(event);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Internal commands
|
|
*/
|
|
|
|
_applySessionData(params) {
|
|
// TODO: Bug 1775231. Move this logic to a shared module or an abstract
|
|
// class.
|
|
const { category } = params;
|
|
if (category === "event") {
|
|
const filteredSessionData = params.sessionData.filter(item =>
|
|
this.messageHandler.matchesContext(item.contextDescriptor)
|
|
);
|
|
for (const event of this.#subscribedEvents.values()) {
|
|
const hasSessionItem = filteredSessionData.some(
|
|
item => item.value === event
|
|
);
|
|
// If there are no session items for this context, we should unsubscribe from the event.
|
|
if (!hasSessionItem) {
|
|
this.#unsubscribeEvent(event);
|
|
}
|
|
}
|
|
|
|
// Subscribe to all events, which have an item in SessionData.
|
|
for (const { value } of filteredSessionData) {
|
|
this.#subscribeEvent(value);
|
|
}
|
|
}
|
|
}
|
|
|
|
static get supportedEvents() {
|
|
return [
|
|
"network.beforeRequestSent",
|
|
"network.responseCompleted",
|
|
"network.responseStarted",
|
|
];
|
|
}
|
|
}
|
|
|
|
export const network = NetworkModule;
|