forked from mirrors/gecko-dev
Bug 1848156 - [bidi] Suspend requests which match an intercept on BeforeRequestSent or ResponseStarted r=webdriver-reviewers,whimboo
Depends on D185997 Differential Revision: https://phabricator.services.mozilla.com/D185456
This commit is contained in:
parent
40f8df623f
commit
69f0469916
2 changed files with 216 additions and 47 deletions
|
|
@ -18,14 +18,15 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
|||
* NetworkListener instance which created it.
|
||||
*/
|
||||
export class NetworkEventRecord {
|
||||
#channel;
|
||||
#contextId;
|
||||
#fromCache;
|
||||
#isMainDocumentChannel;
|
||||
#networkListener;
|
||||
#redirectCount;
|
||||
#requestChannel;
|
||||
#requestData;
|
||||
#requestId;
|
||||
#responseChannel;
|
||||
#responseData;
|
||||
#wrappedChannel;
|
||||
|
||||
|
|
@ -40,7 +41,9 @@ export class NetworkEventRecord {
|
|||
* The NetworkListener which created this NetworkEventRecord.
|
||||
*/
|
||||
constructor(networkEvent, channel, networkListener) {
|
||||
this.#channel = channel;
|
||||
this.#requestChannel = channel;
|
||||
this.#responseChannel = null;
|
||||
|
||||
this.#fromCache = networkEvent.fromCache;
|
||||
this.#isMainDocumentChannel = channel.isMainDocumentChannel;
|
||||
|
||||
|
|
@ -122,6 +125,8 @@ export class NetworkEventRecord {
|
|||
*/
|
||||
addResponseStart(options) {
|
||||
const { channel, fromCache, rawHeaders = "" } = options;
|
||||
this.#responseChannel = channel;
|
||||
|
||||
const { headers } =
|
||||
lazy.NetworkUtils.fetchResponseHeadersAndCookies(channel);
|
||||
|
||||
|
|
@ -232,6 +237,7 @@ export class NetworkEventRecord {
|
|||
this.#networkListener.emit("before-request-sent", {
|
||||
contextId: this.#contextId,
|
||||
isNavigationRequest: this.#isMainDocumentChannel,
|
||||
requestChannel: this.#requestChannel,
|
||||
redirectCount: this.#redirectCount,
|
||||
requestData: this.#requestData,
|
||||
timestamp: Date.now(),
|
||||
|
|
@ -245,7 +251,9 @@ export class NetworkEventRecord {
|
|||
contextId: this.#contextId,
|
||||
isNavigationRequest: this.#isMainDocumentChannel,
|
||||
redirectCount: this.#redirectCount,
|
||||
requestChannel: this.#requestChannel,
|
||||
requestData: this.#requestData,
|
||||
responseChannel: this.#responseChannel,
|
||||
responseData: this.#responseData,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
|
@ -258,7 +266,9 @@ export class NetworkEventRecord {
|
|||
contextId: this.#contextId,
|
||||
isNavigationRequest: this.#isMainDocumentChannel,
|
||||
redirectCount: this.#redirectCount,
|
||||
requestChannel: this.#requestChannel,
|
||||
requestData: this.#requestData,
|
||||
responseChannel: this.#responseChannel,
|
||||
responseData: this.#responseData,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
|
|
@ -288,7 +298,9 @@ export class NetworkEventRecord {
|
|||
}
|
||||
|
||||
#getBrowsingContext() {
|
||||
const id = lazy.NetworkUtils.getChannelBrowsingContextID(this.#channel);
|
||||
const id = lazy.NetworkUtils.getChannelBrowsingContextID(
|
||||
this.#requestChannel
|
||||
);
|
||||
return BrowsingContext.get(id);
|
||||
}
|
||||
|
||||
|
|
@ -314,7 +326,7 @@ export class NetworkEventRecord {
|
|||
|
||||
try {
|
||||
mimeType = this.#wrappedChannel.contentType;
|
||||
const contentCharset = this.#channel.contentCharset;
|
||||
const contentCharset = this.#requestChannel.contentCharset;
|
||||
if (contentCharset) {
|
||||
mimeType += `;charset=${contentCharset}`;
|
||||
}
|
||||
|
|
@ -379,7 +391,9 @@ export class NetworkEventRecord {
|
|||
* any event from this class.
|
||||
*/
|
||||
#updateDataFromTimedChannel() {
|
||||
const timedChannel = this.#channel.QueryInterface(Ci.nsITimedChannel);
|
||||
const timedChannel = this.#requestChannel.QueryInterface(
|
||||
Ci.nsITimedChannel
|
||||
);
|
||||
this.#redirectCount = timedChannel.redirectCount;
|
||||
this.#requestData.timings = this.#getTimingsFromTimedChannel(timedChannel);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ 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:
|
||||
|
|
@ -24,12 +26,20 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
|||
/**
|
||||
* @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.
|
||||
*
|
||||
|
|
@ -223,6 +233,7 @@ const InterceptPhase = {
|
|||
/* eslint-enable jsdoc/valid-types */
|
||||
|
||||
class NetworkModule extends Module {
|
||||
#blockedRequests;
|
||||
#interceptMap;
|
||||
#networkListener;
|
||||
#subscribedEvents;
|
||||
|
|
@ -230,6 +241,9 @@ class NetworkModule extends Module {
|
|||
constructor(messageHandler) {
|
||||
super(messageHandler);
|
||||
|
||||
// Map of request id to BlockedRequest
|
||||
this.#blockedRequests = new Map();
|
||||
|
||||
// Map of intercept id to InterceptProperties
|
||||
this.#interceptMap = new Map();
|
||||
|
||||
|
|
@ -248,6 +262,7 @@ class NetworkModule extends Module {
|
|||
this.#networkListener.off("response-started", this.#onResponseEvent);
|
||||
this.#networkListener.destroy();
|
||||
|
||||
this.#blockedRequests = null;
|
||||
this.#interceptMap = null;
|
||||
this.#subscribedEvents = null;
|
||||
}
|
||||
|
|
@ -359,24 +374,67 @@ class NetworkModule extends Module {
|
|||
};
|
||||
}
|
||||
|
||||
#getOrCreateNavigationId(browsingContext, url) {
|
||||
const navigation =
|
||||
#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
|
||||
);
|
||||
|
||||
// Check if an ongoing navigation is available for this browsing context.
|
||||
// onBeforeRequestSent might be too early for the NavigationManager.
|
||||
// `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 (navigation && !navigation.finished) {
|
||||
return navigation.navigationId;
|
||||
if (
|
||||
eventName === "network.beforeRequestSent" &&
|
||||
(!navigation || navigation.finished)
|
||||
) {
|
||||
navigation = lazy.notifyNavigationStarted({
|
||||
contextDetails: { context: browsingContext },
|
||||
url,
|
||||
});
|
||||
}
|
||||
|
||||
// No ongoing navigation for this browsing context, create a new one.
|
||||
return lazy.notifyNavigationStarted({
|
||||
contextDetails: { context: browsingContext },
|
||||
url,
|
||||
}).navigationId;
|
||||
return navigation ? navigation.navigationId : null;
|
||||
}
|
||||
|
||||
#onBeforeRequestSent = (name, data) => {
|
||||
|
|
@ -384,39 +442,61 @@ class NetworkModule extends Module {
|
|||
contextId,
|
||||
isNavigationRequest,
|
||||
redirectCount,
|
||||
requestChannel,
|
||||
requestData,
|
||||
timestamp,
|
||||
} = data;
|
||||
|
||||
const browsingContext = lazy.TabManager.getBrowsingContextById(contextId);
|
||||
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 navigationId = isNavigationRequest
|
||||
? this.#getOrCreateNavigationId(browsingContext, requestData.url)
|
||||
: null;
|
||||
|
||||
const baseParameters = {
|
||||
context: contextId,
|
||||
navigation: navigationId,
|
||||
redirectCount,
|
||||
request: requestData,
|
||||
timestamp,
|
||||
};
|
||||
|
||||
const beforeRequestSentEvent = this.#serializeNetworkEvent({
|
||||
...baseParameters,
|
||||
initiator,
|
||||
});
|
||||
|
||||
const browsingContext = lazy.TabManager.getBrowsingContextById(contextId);
|
||||
this.emitEvent(
|
||||
"network.beforeRequestSent",
|
||||
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) => {
|
||||
|
|
@ -424,44 +504,119 @@ class NetworkModule extends Module {
|
|||
contextId,
|
||||
isNavigationRequest,
|
||||
redirectCount,
|
||||
requestChannel,
|
||||
requestData,
|
||||
responseChannel,
|
||||
responseData,
|
||||
timestamp,
|
||||
} = data;
|
||||
|
||||
const browsingContext = lazy.TabManager.getBrowsingContextById(contextId);
|
||||
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 navigation = isNavigationRequest
|
||||
? this.messageHandler.navigationManager.getNavigationForBrowsingContext(
|
||||
browsingContext
|
||||
)
|
||||
: null;
|
||||
|
||||
const baseParameters = {
|
||||
context: contextId,
|
||||
navigation: navigation ? navigation.navigationId : null,
|
||||
const baseParameters = this.#processNetworkEvent(protocolEventName, {
|
||||
contextId,
|
||||
isNavigationRequest,
|
||||
redirectCount,
|
||||
request: requestData,
|
||||
requestData,
|
||||
timestamp,
|
||||
};
|
||||
});
|
||||
|
||||
const responseEvent = this.#serializeNetworkEvent({
|
||||
...baseParameters,
|
||||
response: responseData,
|
||||
});
|
||||
|
||||
const protocolEventName =
|
||||
name === "response-started"
|
||||
? "network.responseStarted"
|
||||
: "network.responseCompleted";
|
||||
|
||||
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,
|
||||
|
|
|
|||
Loading…
Reference in a new issue