Bug 1959614 - [bidi] Update NetworkDecodedBodySizeMap to be synchronous r=webdriver-reviewers,whimboo

The timing difference introduced by the async NetworkDecodedBodySizeMap leads
to regressions in playwright tests.

I propose to only use a synchronous approach until we have a better understanding of
why the playwright routing logic fails in this case.

Differential Revision: https://phabricator.services.mozilla.com/D245112
This commit is contained in:
Julian Descottes
2025-04-14 12:50:48 +00:00
parent 2a7bdda234
commit 02001a5e2d
3 changed files with 38 additions and 132 deletions

View File

@@ -2,31 +2,35 @@
* 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/. */
/**
* This map will return the default value of 0 if the decoded body size for
* a given channel was not received yet.
*
* Bug 1959614: The NetworkDecodedBodySizeMap used to setup promises to wait for
* the decoded body size to be ready. However the timing differences from this
* change leads to regressions in Playwright tests. The new implementation of
* the map is only synchronous and if the size was not received yet, 0 will be
* returned. In theory, the decoded body size should be set via
* http-on-before-stop-request which should be received before the request is
* stopped.
*/
export class NetworkDecodedBodySizeMap {
#authenticationAttemptsMap;
#channelIdToBodySizePromiseMap;
#channelIdToBodySizeMap;
constructor() {
this.#authenticationAttemptsMap = new Map();
this.#channelIdToBodySizePromiseMap = new Map();
this.#channelIdToBodySizeMap = new Map();
}
destroy() {
this.#authenticationAttemptsMap = null;
this.#channelIdToBodySizePromiseMap = null;
this.#channelIdToBodySizeMap = null;
}
async getDecodedBodySize(channelId) {
if (!this.#channelIdToBodySizePromiseMap.has(channelId)) {
const { promise, resolve } = Promise.withResolvers();
this.#channelIdToBodySizePromiseMap.set(channelId, {
promise,
resolve,
});
}
const mapEntry = this.#channelIdToBodySizePromiseMap.get(channelId);
await mapEntry.promise;
return mapEntry.decodedBodySize;
getDecodedBodySize(channelId) {
const decodedBodySize = this.#channelIdToBodySizeMap.get(channelId);
return typeof decodedBodySize === "number" ? decodedBodySize : 0;
}
/**
@@ -72,22 +76,11 @@ export class NetworkDecodedBodySizeMap {
return;
}
if (!this.#channelIdToBodySizePromiseMap.has(channelId)) {
const { promise, resolve } = Promise.withResolvers();
this.#channelIdToBodySizePromiseMap.set(channelId, {
decodedBodySize,
promise,
resolve,
});
}
const mapEntry = this.#channelIdToBodySizePromiseMap.get(channelId);
mapEntry.decodedBodySize = decodedBodySize;
mapEntry.resolve(decodedBodySize);
this.#channelIdToBodySizeMap.set(channelId, decodedBodySize);
}
delete(channelId) {
this.#authenticationAttemptsMap.delete(channelId);
this.#channelIdToBodySizePromiseMap.delete(channelId);
this.#channelIdToBodySizeMap.delete(channelId);
}
}

View File

@@ -377,7 +377,7 @@ export class NetworkEventRecord {
}
}
#onChannelCompleted = async () => {
#onChannelCompleted = () => {
if (this.#request.alreadyCompleted) {
return;
}
@@ -393,7 +393,7 @@ export class NetworkEventRecord {
// sizes.
const sizes = {};
if (this.#request.isHttpChannel && !blockedReason) {
sizes.decodedBodySize = await this.#decodedBodySizeMap.getDecodedBodySize(
sizes.decodedBodySize = this.#decodedBodySizeMap.getDecodedBodySize(
this.#request.channel.channelId
);
sizes.encodedBodySize = this.#request.channel.encodedBodySize;

View File

@@ -7,123 +7,36 @@ const { NetworkDecodedBodySizeMap } = ChromeUtils.importESModule(
"chrome://remote/content/shared/NetworkDecodedBodySizeMap.sys.mjs"
);
add_task(async function test_get_set_delete_decodedBodySize() {
add_task(async function test_decodedBodySizeMap() {
const map = new NetworkDecodedBodySizeMap();
const channelId = 1;
let onDecodedBodySize = map.getDecodedBodySize(channelId);
ok(
!(await hasPromiseResolved(onDecodedBodySize)),
"onDecodedBodySize has not resolved yet"
equal(
map.getDecodedBodySize(channelId),
0,
"By default the decode body size is 0"
);
const expectedBodySize = 12;
let expectedBodySize = 12;
map.setDecodedBodySize(channelId, expectedBodySize);
ok(
await hasPromiseResolved(onDecodedBodySize),
"onDecodedBodySize has resolved"
);
equal(
await onDecodedBodySize,
map.getDecodedBodySize(channelId),
expectedBodySize,
"decodedBodySize has the expected value"
"The expected body size was set"
);
// Check that calling getDecodedBodySize for the same channel id resolves
// immediately
onDecodedBodySize = map.getDecodedBodySize(channelId);
ok(
await hasPromiseResolved(onDecodedBodySize),
"onDecodedBodySize has resolved"
);
expectedBodySize = 24;
map.setDecodedBodySize(channelId, expectedBodySize);
equal(
await onDecodedBodySize,
map.getDecodedBodySize(channelId),
expectedBodySize,
"decodedBodySize has the expected value"
"The expected body size was updated"
);
// Delete the entry for channelId and check that the promise no longer
// resolves.
map.delete(channelId);
onDecodedBodySize = map.getDecodedBodySize(channelId);
ok(
!(await hasPromiseResolved(onDecodedBodySize)),
"onDecodedBodySize has not resolved yet"
);
map.destroy();
});
add_task(async function test_set_other_channel() {
const map = new NetworkDecodedBodySizeMap();
const channelId = 1;
const otherChannelId = 2;
const onDecodedBodySize = map.getDecodedBodySize(channelId);
ok(
!(await hasPromiseResolved(onDecodedBodySize)),
"onDecodedBodySize has not resolved yet"
);
map.setDecodedBodySize(otherChannelId, 12);
ok(
!(await hasPromiseResolved(onDecodedBodySize)),
"onDecodedBodySize has still not resolved"
);
map.destroy();
});
add_task(async function test_get_twice() {
const map = new NetworkDecodedBodySizeMap();
const channelId = 1;
const onDecodedBodySize1 = map.getDecodedBodySize(channelId);
ok(
!(await hasPromiseResolved(onDecodedBodySize1)),
"onDecodedBodySize1 has not resolved yet"
);
// Call getDecodedBodySize another time to check we still wait for the promise
const onDecodedBodySize2 = map.getDecodedBodySize(channelId);
ok(
!(await hasPromiseResolved(onDecodedBodySize2)),
"onDecodedBodySize2 has not resolved yet"
);
// Set the body size and check that both promises resolved the same value.
const expectedBodySize = 12;
map.setDecodedBodySize(channelId, expectedBodySize);
ok(
await hasPromiseResolved(onDecodedBodySize1),
"onDecodedBodySize1 has resolved"
);
ok(
await hasPromiseResolved(onDecodedBodySize2),
"onDecodedBodySize2 has resolved"
);
equal(
await onDecodedBodySize1,
expectedBodySize,
"decodedBodySize has the expected value"
);
equal(
await onDecodedBodySize2,
expectedBodySize,
"decodedBodySize has the expected value"
);
// Set another body size and check that further calls to getDecodedBodySize
// resolve the new value.
const otherBodySize = 42;
map.setDecodedBodySize(channelId, otherBodySize);
equal(
await map.getDecodedBodySize(channelId),
otherBodySize,
"decodedBodySize has the expected value"
equal(
map.getDecodedBodySize(channelId),
0,
"After deleting the channel entry, the size is back to 0"
);
map.destroy();