Backed out 8 changesets (bug 1898565, bug 1880803, bug 1850680) for causing high frequency failures at url_patterns.py . CLOSED TREE

Backed out changeset 2e388b7f6f91 (bug 1898565)
Backed out changeset 3147811b0446 (bug 1850680)
Backed out changeset 9a98ace82049 (bug 1850680)
Backed out changeset 8ff3dc16fcf9 (bug 1850680)
Backed out changeset 1c4084febc9c (bug 1850680)
Backed out changeset aeeda14ca643 (bug 1850680)
Backed out changeset fb750d3b365d (bug 1880803)
Backed out changeset 4d63b73b4125 (bug 1880803)
This commit is contained in:
Butkovits Atila 2024-05-24 14:05:56 +03:00
parent 57be48d020
commit 2ca6aa8f02
23 changed files with 350 additions and 848 deletions

View file

@ -236,8 +236,6 @@ const UPDATE_PROPS = [
"proxyHttpVersion",
"proxyStatus",
"proxyStatusText",
"fromCache",
"fromServiceWorker",
];
const PANELS = {

View file

@ -55,6 +55,7 @@ const CONTENT_TYPE_REGEXP = /^content-type/i;
* - discardResponseBody: boolean
* - fromCache: boolean
* - fromServiceWorker: boolean
* - rawHeaders: string
* - timestamp: number
* @param {nsIChannel} channel
* The channel related to this network event
@ -115,6 +116,7 @@ class NetworkEventActor extends Actor {
cookies,
headers,
postData: {},
rawHeaders: networkEventOptions.rawHeaders,
};
this._resource = this._createResource(networkEventOptions, channel);
@ -437,22 +439,6 @@ class NetworkEventActor extends Actor {
* Listeners for new network event data coming from NetworkMonitor.
******************************************************************/
addCacheDetails({ fromCache, fromServiceWorker }) {
this._resource.fromCache = fromCache;
this._resource.fromServiceWorker = fromServiceWorker;
this._onEventUpdate("cacheDetails", { fromCache, fromServiceWorker });
}
addRawHeaders({ channel, rawHeaders }) {
this._request.rawHeaders = rawHeaders;
// For regular requests, some additional headers might only be available
// when rawHeaders are provided, so we update the request headers here.
const { headers } =
lazy.NetworkUtils.fetchRequestHeadersAndCookies(channel);
this._request.headers = headers;
}
/**
* Add network request POST data.
*

View file

@ -341,10 +341,6 @@ class NetworkEventWatcher {
const { resourceUpdates, receivedUpdates } = networkEvent;
switch (updateResource.updateType) {
case "cacheDetails":
resourceUpdates.fromCache = updateResource.fromCache;
resourceUpdates.fromServiceWorker = updateResource.fromServiceWorker;
break;
case "responseStart":
resourceUpdates.httpVersion = updateResource.httpVersion;
resourceUpdates.status = updateResource.status;

View file

@ -180,6 +180,47 @@ export var NetworkHelper = {
return null;
},
/**
* Reads the posted text from the page's cache.
*
* @param nsIDocShell docShell
* @param string charset
* @returns string or null
* Returns the posted string if it was possible to read from
* docShell otherwise null.
*/
readPostTextFromPage(docShell, charset) {
const webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
return this.readPostTextFromPageViaWebNav(webNav, charset);
},
/**
* Reads the posted text from the page's cache, given an nsIWebNavigation
* object.
*
* @param nsIWebNavigation webNav
* @param string charset
* @returns string or null
* Returns the posted string if it was possible to read from
* webNav, otherwise null.
*/
readPostTextFromPageViaWebNav(webNav, charset) {
if (webNav instanceof Ci.nsIWebPageDescriptor) {
const descriptor = webNav.currentDescriptor;
if (
descriptor instanceof Ci.nsISHEntry &&
descriptor.postData &&
descriptor instanceof Ci.nsISeekableStream
) {
descriptor.seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
return this.readAndConvertFromStream(descriptor, charset);
}
}
return null;
},
/**
* Gets the topFrameElement that is associated with request. This
* works in single-process and multiprocess contexts. It may cross
@ -247,6 +288,23 @@ export var NetworkHelper = {
return null;
},
/**
* Determines whether the request has been made for the top level document.
*
* @param nsIHttpChannel request
* @returns Boolean True if the request represents the top level document.
*/
isTopLevelLoad(request) {
if (request instanceof Ci.nsIChannel) {
const loadInfo = request.loadInfo;
if (loadInfo?.isTopLevelLoad) {
return request.loadFlags & Ci.nsIChannel.LOAD_DOCUMENT_URI;
}
}
return false;
},
/**
* Loads the content of url from the cache.
*

View file

@ -95,9 +95,6 @@ const HTTP_DOWNLOAD_ACTIVITIES = [
*
* @constructor
* @param {Object} options
* @param {boolean} options.earlyEvents
* Create network events before the transaction is committed and sent to
* the server.
* @param {Function(nsIChannel): boolean} options.ignoreChannelFunction
* This function will be called for every detected channel to decide if it
* should be monitored or not.
@ -139,20 +136,6 @@ export class NetworkObserver {
* @type {boolean}
*/
#authPromptListenerEnabled = false;
/**
* Whether network events should be created before being sent to the server or
* not. This is currently opt-in because it relies on an observer notification
* which is emitted too early (http-on-before-connect). Due to this, the event
* is initially missing some headers added only when the necko transaction is
* created.
* It also changes the order in which we detect flight and preflight CORS
* requests. When using early events, the order corresponds to the order in
* which the channels are created (first the flight request, then the
* preflight). When using the activity observer, the order corresponds to the
* order in which the requests are sent to the server (first preflight, then
* flight).
*/
#createEarlyEvents = false;
/**
* See constructor argument of the same name.
*
@ -211,7 +194,7 @@ export class NetworkObserver {
#throttler = null;
constructor(options = {}) {
const { earlyEvents, ignoreChannelFunction, onNetworkEvent } = options;
const { ignoreChannelFunction, onNetworkEvent } = options;
if (typeof ignoreChannelFunction !== "function") {
throw new Error(
`Expected "ignoreChannelFunction" to be a function, got ${ignoreChannelFunction} (${typeof ignoreChannelFunction})`
@ -224,7 +207,6 @@ export class NetworkObserver {
);
}
this.#createEarlyEvents = earlyEvents;
this.#ignoreChannelFunction = ignoreChannelFunction;
this.#onNetworkEvent = onNetworkEvent;
@ -249,14 +231,6 @@ export class NetworkObserver {
this.#fileChannelExaminer,
"file-channel-opened"
);
if (this.#createEarlyEvents) {
Services.obs.addObserver(
this.#httpBeforeConnect,
"http-on-before-connect"
);
}
Services.obs.addObserver(this.#httpStopRequest, "http-on-stop-request");
} else {
Services.obs.addObserver(
@ -347,29 +321,6 @@ export class NetworkObserver {
}
);
#httpBeforeConnect = DevToolsInfaillibleUtils.makeInfallible(
(subject, topic) => {
if (
this.#isDestroyed ||
topic != "http-on-before-connect" ||
!(subject instanceof Ci.nsIHttpChannel)
) {
return;
}
const channel = subject.QueryInterface(Ci.nsIHttpChannel);
if (this.#ignoreChannelFunction(channel)) {
return;
}
// Here we create the network event from an early platform notification.
// Additional details about the event will be provided using the various
// callbacks on the network event owner.
const httpActivity = this.#createOrGetActivityObject(channel);
this.#createNetworkEvent(httpActivity);
}
);
#httpStopRequest = DevToolsInfaillibleUtils.makeInfallible(
(subject, topic) => {
if (
@ -404,17 +355,12 @@ export class NetworkObserver {
// Do not pass any blocked reason, as this request is just fine.
// Bug 1489217 - Prevent watching for this request response content,
// as this request is already running, this is too late to watch for it.
this.#createNetworkEvent(httpActivity, {
inProgressRequest: true,
});
this.#createNetworkEvent(subject, { inProgressRequest: true });
} else {
// Handles any early blockings e.g by Web Extensions or by CORS
const { blockingExtension, blockedReason } =
lazy.NetworkUtils.getBlockedReason(channel, httpActivity.fromCache);
this.#createNetworkEvent(httpActivity, {
blockedReason,
blockingExtension,
});
this.#createNetworkEvent(subject, { blockedReason, blockingExtension });
}
}
);
@ -484,13 +430,48 @@ export class NetworkObserver {
channel.QueryInterface(Ci.nsIHttpChannelInternal);
// Retrieve or create the http activity.
const httpActivity = this.#createOrGetActivityObject(channel);
let httpActivity = this.#createOrGetActivityObject(channel);
if (topic === "http-on-examine-cached-response") {
this.#handleExamineCachedResponse(httpActivity);
// Service worker requests emits cached-response notification on non-e10s,
// and we fake one on e10s.
const fromServiceWorker = this.#interceptedChannels.has(channel);
this.#interceptedChannels.delete(channel);
// If this is a cached response (which are also emitted by service worker requests),
// there never was a request event so we need to construct one here
// so the frontend gets all the expected events.
if (!httpActivity.owner) {
httpActivity = this.#createNetworkEvent(channel, {
fromCache: !fromServiceWorker,
fromServiceWorker,
});
}
// We need to send the request body to the frontend for
// the faked (cached/service worker request) event.
this.#prepareRequestBody(httpActivity);
this.#sendRequestBody(httpActivity);
// There also is never any timing events, so we can fire this
// event with zeroed out values.
const timings = this.#setupHarTimings(httpActivity);
const serverTimings = this.#extractServerTimings(httpActivity.channel);
const serviceWorkerTimings =
this.#extractServiceWorkerTimings(httpActivity);
httpActivity.owner.addServerTimings(serverTimings);
httpActivity.owner.addServiceWorkerTimings(serviceWorkerTimings);
httpActivity.owner.addEventTimings(
timings.total,
timings.timings,
timings.offsets
);
} else if (topic === "http-on-failed-opening-request") {
this.#handleFailedOpeningRequest(httpActivity);
const { blockedReason } = lazy.NetworkUtils.getBlockedReason(
channel,
httpActivity.fromCache
);
this.#createNetworkEvent(channel, { blockedReason });
}
if (httpActivity.owner) {
@ -504,78 +485,6 @@ export class NetworkObserver {
}
);
#handleExamineCachedResponse(httpActivity) {
const channel = httpActivity.channel;
const fromServiceWorker = this.#interceptedChannels.has(channel);
const fromCache = !fromServiceWorker;
// Set the cache flags on the httpActivity object, they will be used later
// on during the lifecycle of the channel.
httpActivity.fromCache = fromCache;
httpActivity.fromServiceWorker = fromServiceWorker;
// Service worker requests emits cached-response notification on non-e10s,
// and we fake one on e10s.
this.#interceptedChannels.delete(channel);
if (!httpActivity.owner) {
// If this is a cached response (which are also emitted by service worker requests),
// there never was a request event so we need to construct one here
// so the frontend gets all the expected events.
this.#createNetworkEvent(httpActivity);
} else if (this.#createEarlyEvents) {
// However if we already created an event because the NetworkObserver
// is using early events, simply forward the cache details to the
// event owner.
if (typeof httpActivity.owner.addCacheDetails == "function") {
httpActivity.owner.addCacheDetails({
fromCache: httpActivity.fromCache,
fromServiceWorker: httpActivity.fromServiceWorker,
});
} else {
console.error(
"NetworkObserver was created with earlyEvents:true, but " +
"network event owner does not implement 'addCacheDetails'."
);
}
} else {
// XXX: Find what kind of requests end up here.
}
// We need to send the request body to the frontend for
// the faked (cached/service worker request) event.
this.#prepareRequestBody(httpActivity);
this.#sendRequestBody(httpActivity);
// There also is never any timing events, so we can fire this
// event with zeroed out values.
const timings = this.#setupHarTimings(httpActivity);
const serverTimings = this.#extractServerTimings(httpActivity.channel);
const serviceWorkerTimings =
this.#extractServiceWorkerTimings(httpActivity);
httpActivity.owner.addServerTimings(serverTimings);
httpActivity.owner.addServiceWorkerTimings(serviceWorkerTimings);
httpActivity.owner.addEventTimings(
timings.total,
timings.timings,
timings.offsets
);
}
#handleFailedOpeningRequest(httpActivity) {
const channel = httpActivity.channel;
const { blockedReason } = lazy.NetworkUtils.getBlockedReason(
channel,
httpActivity.fromCache
);
this.#createNetworkEvent(httpActivity, {
blockedReason,
});
}
/**
* Observe notifications for the http-on-modify-request topic, coming from
* the nsIObserverService.
@ -628,14 +537,17 @@ export class NetworkObserver {
logPlatformEvent(topic, channel);
const fileActivity = this.#createOrGetActivityObject(channel);
fileActivity.owner = this.#onNetworkEvent({}, channel);
fileActivity.owner.addResponseStart({
channel: fileActivity.channel,
fromCache: fileActivity.fromCache || fileActivity.fromServiceWorker,
rawHeaders: fileActivity.responseRawHeaders,
proxyResponseRawHeaders: fileActivity.proxyResponseRawHeaders,
});
this.#createNetworkEvent(subject, {});
if (fileActivity.owner) {
fileActivity.owner.addResponseStart({
channel: fileActivity.channel,
fromCache: fileActivity.fromCache || fileActivity.fromServiceWorker,
rawHeaders: fileActivity.responseRawHeaders,
proxyResponseRawHeaders: fileActivity.proxyResponseRawHeaders,
});
}
}
);
@ -665,6 +577,7 @@ export class NetworkObserver {
};
}
}
switch (activitySubtype) {
case gActivityDistributor.ACTIVITY_SUBTYPE_REQUEST_BODY_SENT:
this.#prepareRequestBody(httpActivity);
@ -808,41 +721,78 @@ export class NetworkObserver {
* - Register listener to record response content
*/
#createNetworkEvent(
httpActivity,
{ timestamp, blockedReason, blockingExtension, inProgressRequest } = {}
channel,
{
timestamp,
rawHeaders,
fromCache,
fromServiceWorker,
blockedReason,
blockingExtension,
inProgressRequest,
}
) {
if (
blockedReason === undefined &&
this.#shouldBlockChannel(httpActivity.channel)
) {
if (channel instanceof Ci.nsIFileChannel) {
const fileActivity = this.#createOrGetActivityObject(channel);
if (timestamp) {
fileActivity.timings.REQUEST_HEADER = {
first: timestamp,
last: timestamp,
};
}
fileActivity.owner = this.#onNetworkEvent({}, channel);
return fileActivity;
}
const httpActivity = this.#createOrGetActivityObject(channel);
if (timestamp) {
httpActivity.timings.REQUEST_HEADER = {
first: timestamp,
last: timestamp,
};
}
if (blockedReason === undefined && this.#shouldBlockChannel(channel)) {
// Check the request URL with ones manually blocked by the user in DevTools.
// If it's meant to be blocked, we cancel the request and annotate the event.
httpActivity.channel.cancel(Cr.NS_BINDING_ABORTED);
channel.cancel(Cr.NS_BINDING_ABORTED);
blockedReason = "devtools";
}
httpActivity.owner = this.#onNetworkEvent(
{
timestamp,
fromCache: httpActivity.fromCache,
fromServiceWorker: httpActivity.fromServiceWorker,
fromCache,
fromServiceWorker,
rawHeaders,
blockedReason,
blockingExtension,
discardRequestBody: !this.#saveRequestAndResponseBodies,
discardResponseBody: !this.#saveRequestAndResponseBodies,
},
httpActivity.channel
channel
);
httpActivity.fromCache = fromCache;
httpActivity.fromServiceWorker = fromServiceWorker;
// Bug 1489217 - Avoid watching for response content for blocked or in-progress requests
// as it can't be observed and would throw if we try.
if (blockedReason === undefined && !inProgressRequest) {
this.#setupResponseListener(httpActivity);
this.#setupResponseListener(httpActivity, {
fromCache,
fromServiceWorker,
});
}
if (this.#authPromptListenerEnabled) {
new lazy.NetworkAuthListener(httpActivity.channel, httpActivity.owner);
}
return httpActivity;
}
/**
@ -862,30 +812,8 @@ export class NetworkObserver {
return;
}
const httpActivity = this.#createOrGetActivityObject(channel);
if (timestamp) {
httpActivity.timings.REQUEST_HEADER = {
first: timestamp,
last: timestamp,
};
}
// TODO: In theory httpActivity.owner is missing only if #createEarlyEvents
// is false. However, there is a scenario in DevTools where this can still
// happen.
// If NetworkObserver clear() is called after the event was detected, the
// activity will be deletedld again have an ownerless notification here.
// Should be addressed in Bug 1756770.
if (!httpActivity.owner) {
// If we are not creating events using the early platform notification
// this should be the first time we are notified about this channel.
this.#createNetworkEvent(httpActivity, {
timestamp,
});
}
httpActivity.owner.addRawHeaders({
channel,
this.#createNetworkEvent(channel, {
timestamp,
rawHeaders,
});
}
@ -1044,11 +972,11 @@ export class NetworkObserver {
* @param object httpActivity
* The HTTP activity object we are tracking.
*/
#setupResponseListener(httpActivity) {
#setupResponseListener(httpActivity, { fromCache, fromServiceWorker }) {
const channel = httpActivity.channel;
channel.QueryInterface(Ci.nsITraceableChannel);
if (!httpActivity.fromCache) {
if (!fromCache) {
const throttler = this.#getThrottler();
if (throttler) {
httpActivity.downloadThrottle = throttler.manage(channel);
@ -1069,7 +997,7 @@ export class NetworkObserver {
const newListener = new lazy.NetworkResponseListener(
httpActivity,
this.#decodedCertificateCache,
httpActivity.fromServiceWorker
fromServiceWorker
);
// Remember the input stream, so it isn't released by GC.
@ -1100,11 +1028,30 @@ export class NetworkObserver {
return;
}
const sentBody = lazy.NetworkHelper.readPostTextFromRequest(
let sentBody = lazy.NetworkHelper.readPostTextFromRequest(
httpActivity.channel,
httpActivity.charset
);
if (
sentBody !== null &&
this.window &&
httpActivity.url == this.window.location.href
) {
// If the request URL is the same as the current page URL, then
// we can try to get the posted text from the page directly.
// This check is necessary as otherwise the
// lazy.NetworkHelper.readPostTextFromPageViaWebNav()
// function is called for image requests as well but these
// are not web pages and as such don't store the posted text
// in the cache of the webpage.
const webNav = this.window.docShell.QueryInterface(Ci.nsIWebNavigation);
sentBody = lazy.NetworkHelper.readPostTextFromPageViaWebNav(
webNav,
httpActivity.charset
);
}
if (sentBody !== null) {
httpActivity.sentBody = sentBody;
}
@ -1567,12 +1514,6 @@ export class NetworkObserver {
this.#httpStopRequest,
"http-on-stop-request"
);
if (this.#createEarlyEvents) {
Services.obs.removeObserver(
this.#httpBeforeConnect,
"http-on-before-connect"
);
}
} else {
Services.obs.removeObserver(
this.#httpFailedOpening,

View file

@ -9,31 +9,25 @@ const REQUEST_URL =
// Check that the NetworkObserver can detect basic requests and calls the
// onNetworkEvent callback when expected.
async function testSingleRequest({ earlyEvents }) {
add_task(async function testSingleRequest() {
await addTab(TEST_URL);
const onNetworkEvents = waitForNetworkEvents(REQUEST_URL, 1, earlyEvents);
const onNetworkEvents = waitForNetworkEvents(REQUEST_URL, 1);
await SpecialPowers.spawn(gBrowser.selectedBrowser, [REQUEST_URL], _url => {
content.wrappedJSObject.fetch(_url);
});
const events = await onNetworkEvents;
is(events.length, 1, "Received the expected number of network events");
}
add_task(async function () {
await testSingleRequest({ earlyEvents: false });
await testSingleRequest({ earlyEvents: true });
});
async function testMultipleRequests({ earlyEvents }) {
add_task(async function testMultipleRequests() {
await addTab(TEST_URL);
const EXPECTED_REQUESTS_COUNT = 5;
const onNetworkEvents = waitForNetworkEvents(
REQUEST_URL,
EXPECTED_REQUESTS_COUNT,
earlyEvents
EXPECTED_REQUESTS_COUNT
);
await SpecialPowers.spawn(
gBrowser.selectedBrowser,
@ -51,18 +45,13 @@ async function testMultipleRequests({ earlyEvents }) {
EXPECTED_REQUESTS_COUNT,
"Received the expected number of network events"
);
}
add_task(async function () {
await testMultipleRequests({ earlyEvents: false });
await testMultipleRequests({ earlyEvents: true });
});
async function testOnNetworkEventArguments({ earlyEvents }) {
add_task(async function testOnNetworkEventArguments() {
await addTab(TEST_URL);
const onNetworkEvent = new Promise(resolve => {
const networkObserver = new NetworkObserver({
earlyEvents,
ignoreChannelFunction: () => false,
onNetworkEvent: (...args) => {
resolve(args);
@ -80,8 +69,4 @@ async function testOnNetworkEventArguments({ earlyEvents }) {
is(args.length, 2, "Received two arguments");
is(typeof args[0], "object", "First argument is an object");
ok(args[1] instanceof Ci.nsIChannel, "Second argument is a channel");
}
add_task(async function () {
await testOnNetworkEventArguments({ earlyEvents: false });
await testOnNetworkEventArguments({ earlyEvents: true });
});

View file

@ -64,13 +64,12 @@ class AuthCredentialsProvidingOwner extends NetworkEventOwner {
}
}
async function testAuthRequestWithoutListener({ earlyEvents }) {
add_task(async function testAuthRequestWithoutListener() {
cleanupAuthManager();
const tab = await addTab(TEST_URL);
const events = [];
const networkObserver = new NetworkObserver({
earlyEvents,
ignoreChannelFunction: channel => channel.URI.spec !== AUTH_URL,
onNetworkEvent: () => {
const owner = new AuthForwardingOwner();
@ -107,19 +106,14 @@ async function testAuthRequestWithoutListener({ earlyEvents }) {
networkObserver.destroy();
gBrowser.removeTab(tab);
}
add_task(async function () {
await testAuthRequestWithoutListener({ earlyEvents: false });
await testAuthRequestWithoutListener({ earlyEvents: true });
});
async function testAuthRequestWithForwardingListener({ earlyEvents }) {
add_task(async function testAuthRequestWithForwardingListener() {
cleanupAuthManager();
const tab = await addTab(TEST_URL);
const events = [];
const networkObserver = new NetworkObserver({
earlyEvents,
ignoreChannelFunction: channel => channel.URI.spec !== AUTH_URL,
onNetworkEvent: () => {
info("waitForNetworkEvents received a new event");
@ -164,19 +158,14 @@ async function testAuthRequestWithForwardingListener({ earlyEvents }) {
networkObserver.destroy();
gBrowser.removeTab(tab);
}
add_task(async function () {
await testAuthRequestWithForwardingListener({ earlyEvents: false });
await testAuthRequestWithForwardingListener({ earlyEvents: true });
});
async function testAuthRequestWithCancellingListener({ earlyEvents }) {
add_task(async function testAuthRequestWithCancellingListener() {
cleanupAuthManager();
const tab = await addTab(TEST_URL);
const events = [];
const networkObserver = new NetworkObserver({
earlyEvents,
ignoreChannelFunction: channel => channel.URI.spec !== AUTH_URL,
onNetworkEvent: () => {
const owner = new AuthCancellingOwner();
@ -219,19 +208,14 @@ async function testAuthRequestWithCancellingListener({ earlyEvents }) {
networkObserver.destroy();
gBrowser.removeTab(tab);
}
add_task(async function () {
await testAuthRequestWithCancellingListener({ earlyEvents: false });
await testAuthRequestWithCancellingListener({ earlyEvents: true });
});
async function testAuthRequestWithWrongCredentialsListener({ earlyEvents }) {
add_task(async function testAuthRequestWithWrongCredentialsListener() {
cleanupAuthManager();
const tab = await addTab(TEST_URL);
const events = [];
const networkObserver = new NetworkObserver({
earlyEvents,
ignoreChannelFunction: channel => channel.URI.spec !== AUTH_URL,
onNetworkEvent: (event, channel) => {
const owner = new AuthCredentialsProvidingOwner(
@ -275,19 +259,14 @@ async function testAuthRequestWithWrongCredentialsListener({ earlyEvents }) {
networkObserver.destroy();
gBrowser.removeTab(tab);
}
add_task(async function () {
await testAuthRequestWithWrongCredentialsListener({ earlyEvents: false });
await testAuthRequestWithWrongCredentialsListener({ earlyEvents: true });
});
async function testAuthRequestWithCredentialsListener({ earlyEvents }) {
add_task(async function testAuthRequestWithCredentialsListener() {
cleanupAuthManager();
const tab = await addTab(TEST_URL);
const events = [];
const networkObserver = new NetworkObserver({
earlyEvents,
ignoreChannelFunction: channel => channel.URI.spec !== AUTH_URL,
onNetworkEvent: (event, channel) => {
const owner = new AuthCredentialsProvidingOwner(
@ -342,10 +321,6 @@ async function testAuthRequestWithCredentialsListener({ earlyEvents }) {
networkObserver.destroy();
gBrowser.removeTab(tab);
}
add_task(async function () {
await testAuthRequestWithCredentialsListener({ earlyEvents: false });
await testAuthRequestWithCredentialsListener({ earlyEvents: true });
});
function assertEventOwner(event, expectedFlags) {

View file

@ -10,12 +10,11 @@ const GZIPPED_REQUEST_URL = URL_ROOT + `gzipped.sjs`;
const OVERRIDE_FILENAME = "override.js";
const OVERRIDE_HTML_FILENAME = "override.html";
async function testLocalOverride({ earlyEvents }) {
add_task(async function testLocalOverride() {
await addTab(TEST_URL);
let eventsCount = 0;
const networkObserver = new NetworkObserver({
earlyEvents,
ignoreChannelFunction: channel => channel.URI.spec !== REQUEST_URL,
onNetworkEvent: event => {
info("received a network event");
@ -74,16 +73,11 @@ async function testLocalOverride({ earlyEvents }) {
await BrowserTestUtils.waitForCondition(() => eventsCount >= 1);
networkObserver.destroy();
}
add_task(async function () {
await testLocalOverride({ earlyEvents: false });
await testLocalOverride({ earlyEvents: true });
});
async function testHtmlFileOverride({ earlyEvents }) {
add_task(async function testHtmlFileOverride() {
let eventsCount = 0;
const networkObserver = new NetworkObserver({
earlyEvents,
ignoreChannelFunction: channel => channel.URI.spec !== TEST_URL,
onNetworkEvent: event => {
info("received a network event");
@ -118,19 +112,14 @@ async function testHtmlFileOverride({ earlyEvents }) {
);
await BrowserTestUtils.waitForCondition(() => eventsCount >= 1);
networkObserver.destroy();
}
add_task(async function () {
await testHtmlFileOverride({ earlyEvents: false });
await testHtmlFileOverride({ earlyEvents: true });
});
// Exact same test, but with a gzipped request, which requires very special treatment
async function testLocalOverrideGzipped({ earlyEvents }) {
add_task(async function testLocalOverrideGzipped() {
await addTab(TEST_URL);
let eventsCount = 0;
const networkObserver = new NetworkObserver({
earlyEvents,
ignoreChannelFunction: channel => channel.URI.spec !== GZIPPED_REQUEST_URL,
onNetworkEvent: event => {
info("received a network event");
@ -187,8 +176,4 @@ async function testLocalOverrideGzipped({ earlyEvents }) {
await BrowserTestUtils.waitForCondition(() => eventsCount >= 1);
networkObserver.destroy();
}
add_task(async function () {
await testLocalOverrideGzipped({ earlyEvents: false });
await testLocalOverrideGzipped({ earlyEvents: true });
});

View file

@ -5,7 +5,7 @@
// Tests that all the expected service worker requests are received
// by the network observer.
async function testServiceWorkerSuccessRequests({ earlyEvents }) {
add_task(async function testServiceWorkerSuccessRequests() {
await addTab(URL_ROOT + "doc_network-observer.html");
const REQUEST_URL =
@ -21,7 +21,7 @@ async function testServiceWorkerSuccessRequests({ earlyEvents }) {
REQUEST_URL + "json",
];
const onNetworkEvents = waitForNetworkEvents(null, 4, earlyEvents);
const onNetworkEvents = waitForNetworkEvents(null, 4);
info("Register the service worker and send requests...");
await SpecialPowers.spawn(
@ -65,14 +65,10 @@ async function testServiceWorkerSuccessRequests({ earlyEvents }) {
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
await content.wrappedJSObject.unregisterServiceWorker();
});
}
add_task(async function () {
await testServiceWorkerSuccessRequests({ earlyEvents: false });
await testServiceWorkerSuccessRequests({ earlyEvents: true });
});
// Tests that the expected failed service worker request is received by the network observer.
async function testServiceWorkerFailedRequests({ earlyEvents }) {
add_task(async function testServiceWorkerFailedRequests() {
await addTab(URL_ROOT + "doc_network-observer-missing-service-worker.html");
const REQUEST_URL =
@ -85,7 +81,7 @@ async function testServiceWorkerFailedRequests({ earlyEvents }) {
"https://example.com/browser/devtools/shared/network-observer/test/browser/sjs_network-observer-test-server.sjs?sts=200&fmt=js",
];
const onNetworkEvents = waitForNetworkEvents(null, 2, earlyEvents);
const onNetworkEvents = waitForNetworkEvents(null, 2);
await SpecialPowers.spawn(
gBrowser.selectedBrowser,
[REQUEST_URL],
@ -114,8 +110,4 @@ async function testServiceWorkerFailedRequests({ earlyEvents }) {
`The request for ${channel.URI.spec} is not from the service worker\n`
);
}
}
add_task(async function () {
await testServiceWorkerFailedRequests({ earlyEvents: false });
await testServiceWorkerFailedRequests({ earlyEvents: true });
});

View file

@ -101,14 +101,9 @@ function createNetworkEventOwner() {
* A promise which will resolve with an array of network event owners, when
* the expected event count is reached.
*/
async function waitForNetworkEvents(
expectedUrl = null,
expectedRequestsCount,
earlyEvents = false
) {
async function waitForNetworkEvents(expectedUrl = null, expectedRequestsCount) {
const events = [];
const networkObserver = new NetworkObserver({
earlyEvents,
ignoreChannelFunction: channel =>
expectedUrl ? channel.URI.spec !== expectedUrl : false,
onNetworkEvent: () => {

View file

@ -4,8 +4,6 @@
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
NetworkHelper:
"resource://devtools/shared/network-observer/NetworkHelper.sys.mjs",
NetworkUtils:
"resource://devtools/shared/network-observer/NetworkUtils.sys.mjs",
@ -24,6 +22,7 @@ export class NetworkRequest {
#contextId;
#navigationId;
#navigationManager;
#postData;
#rawHeaders;
#redirectCount;
#requestId;
@ -85,12 +84,7 @@ export class NetworkRequest {
}
get postDataSize() {
const charset = lazy.NetworkUtils.getCharset(this.#channel);
const sentBody = lazy.NetworkHelper.readPostTextFromRequest(
this.#channel,
charset
);
return sentBody ? sentBody.length : 0;
return this.#postData ? this.#postData.size : 0;
}
get redirectCount() {
@ -109,16 +103,6 @@ export class NetworkRequest {
return this.#wrappedChannel;
}
/**
* Add information about raw headers, collected from NetworkObserver events.
*
* @param {string} rawHeaders
* The raw headers.
*/
addRawHeaders(rawHeaders) {
this.#rawHeaders = rawHeaders || "";
}
/**
* Retrieve the Fetch timings for the NetworkRequest.
*
@ -199,64 +183,18 @@ export class NetworkRequest {
}
/**
* Set the request post body
* Update the postData for this NetworkRequest. This is currently forwarded
* by the DevTools' NetworkObserver.
*
* @param {string} body
* The body to set.
*/
setRequestBody(body) {
// Update the requestObserversCalled flag to allow modifying the request,
// and reset once done.
this.#channel.requestObserversCalled = false;
try {
this.#channel.QueryInterface(Ci.nsIUploadChannel2);
const bodyStream = Cc[
"@mozilla.org/io/string-input-stream;1"
].createInstance(Ci.nsIStringInputStream);
bodyStream.setData(body, body.length);
this.#channel.explicitSetUploadStream(
bodyStream,
null,
-1,
this.#channel.requestMethod,
false
);
} finally {
// Make sure to reset the flag once the modification was attempted.
this.#channel.requestObserversCalled = true;
}
}
/**
* Set a request header
* TODO: We should read this information dynamically from the channel so that
* we can get updated information in case it was modified via network
* interception.
*
* @param {string} name
* The header's name.
* @param {string} value
* The header's value.
* @param {object} postData
* The request POST data.
*/
setRequestHeader(name, value) {
this.#channel.setRequestHeader(name, value, false);
}
/**
* Update the request's method.
*
* @param {string} method
* The method to set.
*/
setRequestMethod(method) {
// Update the requestObserversCalled flag to allow modifying the request,
// and reset once done.
this.#channel.requestObserversCalled = false;
try {
this.#channel.requestMethod = method;
} finally {
// Make sure to reset the flag once the modification was attempted.
this.#channel.requestObserversCalled = true;
}
setPostData(postData) {
this.#postData = postData;
}
/**

View file

@ -60,38 +60,17 @@ export class NetworkEventRecord {
}
}
/**
* Add network request cache details.
*
* Required API for a NetworkObserver event owner.
*
* @param {object} options
* @param {boolean} options.fromCache
*/
addCacheDetails(options) {
const { fromCache } = options;
this.#fromCache = fromCache;
}
/**
* Add network request raw headers.
*
* Required API for a NetworkObserver event owner.
*
* @param {object} options
* @param {string} options.rawHeaders
*/
addRawHeaders(options) {
const { rawHeaders } = options;
this.#request.addRawHeaders(rawHeaders);
}
/**
* Add network request POST data.
*
* Required API for a NetworkObserver event owner.
*
* @param {object} postData
* The request POST data.
*/
addRequestPostData() {}
addRequestPostData(postData) {
this.#request.setPostData(postData);
}
/**
* Add the initial network response information.

View file

@ -63,7 +63,6 @@ export class NetworkListener {
}
this.#devtoolsNetworkObserver = new lazy.NetworkObserver({
earlyEvents: true,
ignoreChannelFunction: this.#ignoreChannelFunction,
onNetworkEvent: this.#onNetworkEvent,
});

View file

@ -502,6 +502,13 @@
"expectations": ["FAIL"],
"comment": "TODO: add a comment explaining why this expectation is required (include links to issues)"
},
{
"testIdPattern": "[network.spec] network Page.setExtraHTTPHeaders *",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["SKIP"],
"comment": "Firefox does not support headers override"
},
{
"testIdPattern": "[network.spec] network Request.initiator should return the initiator",
"platforms": ["darwin", "linux", "win32"],
@ -759,11 +766,11 @@
"comment": "BiDi spec expect the request to not trim the hash"
},
{
"testIdPattern": "[requestinterception-experimental.spec] cooperative request interception Request.continue should redirect in a way non-observable to page",
"testIdPattern": "[requestinterception-experimental.spec] cooperative request interception Request.continue *",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"],
"comment": "Firefox needs support for the url parameter of continueRequest"
"expectations": ["SKIP"],
"comment": "TODO: Needs full support for continueRequest in Firefox https://bugzilla.mozilla.org/show_bug.cgi?id=1850680"
},
{
"testIdPattern": "[requestinterception-experimental.spec] cooperative request interception Request.resourceType should work for document type",
@ -829,18 +836,11 @@
"comment": "`HTTPRequest.resourceType()` has no eqivalent in BiDi spec"
},
{
"testIdPattern": "[requestinterception.spec] request interception Request.continue should redirect in a way non-observable to page",
"testIdPattern": "[requestinterception.spec] request interception Request.continue *",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"],
"comment": "Firefox needs support for the url parameter of continueRequest"
},
{
"testIdPattern": "[requestinterception.spec] request interception Request.continue should fail if the header value is invalid",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"],
"comment": "Puppeteer expecting a chrome only error https://github.com/puppeteer/puppeteer/issues/12412"
"expectations": ["SKIP"],
"comment": "TODO: Needs full support for continueRequest in Firefox https://bugzilla.mozilla.org/show_bug.cgi?id=1850680"
},
{
"testIdPattern": "[requestinterception.spec] request interception Request.resourceType should work for document type",
@ -3419,6 +3419,20 @@
"expectations": ["SKIP"],
"comment": "TODO: Needs support for enabling cache in BiDi without CDP https://github.com/w3c/webdriver-bidi/issues/582"
},
{
"testIdPattern": "[requestinterception-experimental.spec] cooperative request interception Page.setRequestInterception should send referer",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["SKIP"],
"comment": "Firefox does not support headers override"
},
{
"testIdPattern": "[requestinterception-experimental.spec] cooperative request interception Page.setRequestInterception should show custom HTTP headers",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["SKIP"],
"comment": "Firefox does not support headers override"
},
{
"testIdPattern": "[requestinterception-experimental.spec] cooperative request interception Page.setRequestInterception should work when header manipulation headers with redirect",
"platforms": ["darwin", "linux", "win32"],
@ -3426,6 +3440,13 @@
"expectations": ["SKIP"],
"comment": "TODO: Needs investigation, on Firefox the test is passing even if headers are not actually modified"
},
{
"testIdPattern": "[requestinterception-experimental.spec] cooperative request interception Page.setRequestInterception should work with custom referer headers",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["SKIP"],
"comment": "Firefox does not support headers override"
},
{
"testIdPattern": "[requestinterception-experimental.spec] cooperative request interception Page.setRequestInterception should work with encoded server - 2",
"platforms": ["darwin", "linux", "win32"],
@ -3433,6 +3454,13 @@
"expectations": ["SKIP"],
"comment": "TODO: Needs support for data URIs in Firefox https://bugzilla.mozilla.org/show_bug.cgi?id=1805176"
},
{
"testIdPattern": "[requestinterception-experimental.spec] cooperative request interception Page.setRequestInterception should work with equal requests",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"],
"comment": "TODO: Needs investigation, it looks like Firefox lets the request go also to the server"
},
{
"testIdPattern": "[requestinterception-experimental.spec] cooperative request interception Page.setRequestInterception should work with file URLs",
"platforms": ["darwin", "linux", "win32"],
@ -3564,6 +3592,34 @@
"expectations": ["SKIP"],
"comment": "TODO: Needs support for enabling cache in BiDi without CDP https://github.com/w3c/webdriver-bidi/issues/582"
},
{
"testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should send referer",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"],
"comment": "Firefox does not support headers override"
},
{
"testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should send referer",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["SKIP"],
"comment": "Firefox does not support headers override"
},
{
"testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should show custom HTTP headers",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"],
"comment": "Firefox does not support headers override"
},
{
"testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should show custom HTTP headers",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["SKIP"],
"comment": "Firefox does not support headers override"
},
{
"testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should work when header manipulation headers with redirect",
"platforms": ["darwin", "linux", "win32"],
@ -3571,6 +3627,20 @@
"expectations": ["SKIP"],
"comment": "TODO: Needs investigation, on Firefox the test is passing even if headers are not actually modified"
},
{
"testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should work with custom referer headers",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"],
"comment": "Firefox does not support headers override"
},
{
"testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should work with custom referer headers",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["SKIP"],
"comment": "Firefox does not support headers override"
},
{
"testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should work with encoded server - 2",
"platforms": ["darwin", "linux", "win32"],
@ -3578,6 +3648,13 @@
"expectations": ["SKIP"],
"comment": "TODO: Needs support for data URIs in Firefox https://bugzilla.mozilla.org/show_bug.cgi?id=1805176"
},
{
"testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should work with equal requests",
"platforms": ["darwin", "linux", "win32"],
"parameters": ["firefox", "webDriverBiDi"],
"expectations": ["FAIL"],
"comment": "TODO: Needs investigation"
},
{
"testIdPattern": "[requestinterception.spec] request interception Page.setRequestInterception should work with file URLs",
"platforms": ["darwin", "linux", "win32"],

View file

@ -433,20 +433,19 @@ class NetworkModule extends Module {
* @param {object=} options
* @param {string} options.request
* The id of the blocked request that should be continued.
* @param {BytesValue=} options.body
* @param {BytesValue=} options.body [unsupported]
* Optional BytesValue to replace the body of the request.
* @param {Array<CookieHeader>=} options.cookies
* @param {Array<CookieHeader>=} options.cookies [unsupported]
* Optional array of cookie header values to replace the cookie header of
* the request.
* @param {Array<Header>=} options.headers
* @param {Array<Header>=} options.headers [unsupported]
* Optional array of headers to replace the headers of the request.
* request.
* @param {string=} options.method
* @param {string=} options.method [unsupported]
* Optional string to replace the method of the request.
* @param {string=} options.url [unsupported]
* Optional string to replace the url of the request. If the provided url
* is not a valid URL, an InvalidArgumentError will be thrown.
* Support will be added in https://bugzilla.mozilla.org/show_bug.cgi?id=1898158
*
* @throws {InvalidArgumentError}
* Raised if an argument is of an invalid type or value.
@ -474,6 +473,10 @@ class NetworkModule extends Module {
body,
`Expected "body" to be a network.BytesValue, got ${body}`
);
throw new lazy.error.UnsupportedOperationError(
`"body" not supported yet in network.continueRequest`
);
}
if (cookies !== null) {
@ -488,9 +491,12 @@ class NetworkModule extends Module {
`Expected values in "cookies" to be network.CookieHeader, got ${cookie}`
);
}
throw new lazy.error.UnsupportedOperationError(
`"cookies" not supported yet in network.continueRequest`
);
}
const deserializedHeaders = [];
if (headers !== null) {
lazy.assert.array(
headers,
@ -502,19 +508,11 @@ class NetworkModule extends Module {
header,
`Expected values in "headers" to be network.Header, got ${header}`
);
// Deserialize headers immediately to validate the value
const deserializedHeader = this.#deserializeHeader(header);
lazy.assert.that(
value => this.#isValidHttpToken(value),
`Expected "header" name to be a valid HTTP token, got ${deserializedHeader[0]}`
)(deserializedHeader[0]);
lazy.assert.that(
value => this.#isValidHeaderValue(value),
`Expected "header" value to be a valid header value, got ${deserializedHeader[1]}`
)(deserializedHeader[1]);
deserializedHeaders.push(deserializedHeader);
}
throw new lazy.error.UnsupportedOperationError(
`"headers" not supported yet in network.continueRequest`
);
}
if (method !== null) {
@ -522,10 +520,10 @@ class NetworkModule extends Module {
method,
`Expected "method" to be a string, got ${method}`
);
lazy.assert.that(
value => this.#isValidHttpToken(value),
`Expected "method" to be a valid HTTP token, got ${method}`
)(method);
throw new lazy.error.UnsupportedOperationError(
`"method" not supported yet in network.continueRequest`
);
}
if (url !== null) {
@ -551,53 +549,6 @@ class NetworkModule extends Module {
);
}
if (method !== null) {
request.setRequestMethod(method);
}
if (headers !== null) {
// Delete all existing request headers not found in the headers parameter.
request.getHeadersList().forEach(([name]) => {
if (!headers.some(header => header.name == name)) {
request.setRequestHeader(name, "");
}
});
// Set all headers specified in the headers parameter.
for (const [name, value] of deserializedHeaders) {
request.setRequestHeader(name, value);
}
}
if (cookies !== null) {
let cookieHeader = "";
for (const cookie of cookies) {
if (cookieHeader != "") {
cookieHeader += ";";
}
cookieHeader += this.#serializeCookieHeader(cookie);
}
let foundCookieHeader = false;
const requestHeaders = request.getHeadersList();
for (const [name] of requestHeaders) {
if (name.toLowerCase() == "cookie") {
request.setRequestHeader(name, cookieHeader);
foundCookieHeader = true;
break;
}
}
if (!foundCookieHeader) {
request.setRequestHeader("Cookie", cookieHeader);
}
}
if (body !== null) {
const value = deserializeBytesValue(body);
request.setRequestBody(value);
}
request.wrappedChannel.resume();
resolveBlockedEvent();
@ -1158,12 +1109,6 @@ class NetworkModule extends Module {
}
}
#deserializeHeader(protocolHeader) {
const name = protocolHeader.name;
const value = deserializeBytesValue(protocolHeader.value);
return [name, value];
}
#extractChallenges(response) {
let headerName;
@ -1365,65 +1310,6 @@ class NetworkModule extends Module {
return `Request (id: ${requestData.request}) suspended by WebDriver BiDi in ${phase} phase`;
}
#isValidHeaderValue(value) {
if (!value.length) {
return true;
}
// For non-empty strings check against:
// - leading or trailing tabs & spaces
// - new lines and null bytes
const chars = value.split("");
const tabOrSpace = [" ", "\t"];
const forbiddenChars = ["\r", "\n", "\0"];
return (
!tabOrSpace.includes(chars.at(0)) &&
!tabOrSpace.includes(chars.at(-1)) &&
forbiddenChars.every(c => !chars.includes(c))
);
}
/**
* This helper is adapted from a C++ validation helper in nsHttp.cpp.
*
* @see https://searchfox.org/mozilla-central/rev/445a6e86233c733c5557ef44e1d33444adaddefc/netwerk/protocol/http/nsHttp.cpp#169
*/
#isValidHttpToken(token) {
// prettier-ignore
// This array corresponds to all char codes between 0 and 127, which is the
// range of supported char codes for HTTP tokens. Within this range,
// accepted char codes are marked with a 1, forbidden char codes with a 0.
const validTokenMap = [
0, 0, 0, 0, 0, 0, 0, 0, // 0
0, 0, 0, 0, 0, 0, 0, 0, // 8
0, 0, 0, 0, 0, 0, 0, 0, // 16
0, 0, 0, 0, 0, 0, 0, 0, // 24
0, 1, 0, 1, 1, 1, 1, 1, // 32
0, 0, 1, 1, 0, 1, 1, 0, // 40
1, 1, 1, 1, 1, 1, 1, 1, // 48
1, 1, 0, 0, 0, 0, 0, 0, // 56
0, 1, 1, 1, 1, 1, 1, 1, // 64
1, 1, 1, 1, 1, 1, 1, 1, // 72
1, 1, 1, 1, 1, 1, 1, 1, // 80
1, 1, 1, 0, 0, 0, 1, 1, // 88
1, 1, 1, 1, 1, 1, 1, 1, // 96
1, 1, 1, 1, 1, 1, 1, 1, // 104
1, 1, 1, 1, 1, 1, 1, 1, // 112
1, 1, 1, 0, 1, 0, 1, 0 // 120
];
if (!token.length) {
return false;
}
return token
.split("")
.map(s => s.charCodeAt(0))
.every(c => validTokenMap[c]);
}
#onAuthRequired = (name, data) => {
const { authCallbacks, request, response } = data;
@ -1739,12 +1625,6 @@ class NetworkModule extends Module {
return params;
}
#serializeCookieHeader(cookieHeader) {
const name = cookieHeader.name;
const value = deserializeBytesValue(cookieHeader.value);
return `${name}=${value}`;
}
#serializeHeader(name, value) {
return {
name,
@ -1839,23 +1719,4 @@ class NetworkModule extends Module {
}
}
/**
* Deserialize a network BytesValue.
*
* @param {BytesValue} bytesValue
* The BytesValue to deserialize.
* @returns {string}
* The deserialized value.
*/
export function deserializeBytesValue(bytesValue) {
const { type, value } = bytesValue;
if (type === BytesValueType.String) {
return value;
}
// For type === BytesValueType.Base64.
return atob(value);
}
export const network = NetworkModule;

View file

@ -10,8 +10,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
assert: "chrome://remote/content/shared/webdriver/Assert.sys.mjs",
BytesValueType:
"chrome://remote/content/webdriver-bidi/modules/root/network.sys.mjs",
deserializeBytesValue:
"chrome://remote/content/webdriver-bidi/modules/root/network.sys.mjs",
error: "chrome://remote/content/shared/webdriver/Errors.sys.mjs",
TabManager: "chrome://remote/content/shared/TabManager.sys.mjs",
UserContextManager:
@ -285,8 +283,7 @@ class StorageModule extends Module {
// The cookie store is defined by originAttributes.
const originAttributes = this.#getOriginAttributes(partitionKey);
// The cookie value is a network.BytesValue.
const deserializedValue = lazy.deserializeBytesValue(value);
const deserializedValue = this.#deserializeProtocolBytes(value);
// The XPCOM interface requires to be specified if a cookie is session.
const isSession = expiry === null;
@ -574,7 +571,7 @@ class StorageModule extends Module {
break;
case "value":
deserializedValue = lazy.deserializeBytesValue(value);
deserializedValue = this.#deserializeProtocolBytes(value);
break;
default:
@ -587,6 +584,21 @@ class StorageModule extends Module {
return deserializedFilter;
}
/**
* Deserialize the value to string, since platform API
* returns cookie's value as a string.
*/
#deserializeProtocolBytes(cookieValue) {
const { type, value } = cookieValue;
if (type === lazy.BytesValueType.String) {
return value;
}
// For type === BytesValueType.Base64.
return atob(value);
}
/**
* Build a partition key.
*

View file

@ -17,8 +17,6 @@ ChromeUtils.defineESModuleGetters(lazy, {
// A minimal struct for onNetworkEvent handling
class NetworkEventRecord {
addCacheDetails() {}
addRawHeaders() {}
addRequestPostData() {}
addResponseStart() {}
addSecurityInfo() {}

View file

@ -66,7 +66,6 @@ async def setup_blocked_request(
password="password",
realm="test",
navigate=False,
**kwargs,
):
await setup_network_test(events=[f"network.{phase}"])
@ -100,7 +99,7 @@ async def setup_blocked_request(
)
)
else:
asyncio.ensure_future(fetch(blocked_url, context=context, **kwargs))
asyncio.ensure_future(fetch(blocked_url, context=context))
event = await wait_for_future_safe(network_event)
request = event["request"]["request"]

View file

@ -1,37 +0,0 @@
import pytest
from webdriver.bidi.modules.network import NetworkStringValue
from ... import recursive_compare
from .. import assert_response_event, RESPONSE_COMPLETED_EVENT
pytestmark = pytest.mark.asyncio
@pytest.mark.parametrize(
"request_post_data, modified_post_data, expected_size",
[
["{'a': 1}", "", 0],
[None, "{'a': 123}", 10],
["{'a': 1}", "{'a': 12345678}", 15],
],
)
async def test_request_body(
bidi_session,
setup_blocked_request,
subscribe_events,
wait_for_event,
request_post_data,
modified_post_data,
expected_size,
):
request = await setup_blocked_request(
"beforeRequestSent", method="POST", post_data=request_post_data
)
await subscribe_events(events=[RESPONSE_COMPLETED_EVENT])
on_response_completed = wait_for_event(RESPONSE_COMPLETED_EVENT)
body = NetworkStringValue(modified_post_data)
await bidi_session.network.continue_request(request=request, body=body)
response_event = await on_response_completed
assert response_event["request"]["bodySize"] == expected_size

View file

@ -1,104 +0,0 @@
import pytest
from webdriver.bidi.modules.network import CookieHeader, Header, NetworkStringValue
from webdriver.bidi.modules.script import ContextTarget
from ... import recursive_compare
from .. import assert_response_event, RESPONSE_COMPLETED_EVENT
pytestmark = pytest.mark.asyncio
@pytest.mark.parametrize(
"document_cookies, modified_cookies",
[
[{"a": "1"}, {}],
[{}, {"b": "2"}],
[{"a": "1", "b": "2"}, {"c": "3", "d": "4"}],
[{"a": "1"}, {"a": "not-1"}],
],
)
async def test_modify_cookies(
setup_blocked_request,
subscribe_events,
wait_for_event,
bidi_session,
top_context,
document_cookies,
modified_cookies,
):
expression = ""
for name, value in document_cookies.items():
expression += f"document.cookie = '{name}={value}';"
await bidi_session.script.evaluate(
expression=expression,
target=ContextTarget(top_context["context"]),
await_promise=False,
)
request = await setup_blocked_request("beforeRequestSent")
await subscribe_events(events=[RESPONSE_COMPLETED_EVENT])
cookies = []
for name, value in modified_cookies.items():
cookies.append(CookieHeader(name=name, value=NetworkStringValue(value)))
on_response_completed = wait_for_event(RESPONSE_COMPLETED_EVENT)
await bidi_session.network.continue_request(request=request, cookies=cookies)
response_event = await on_response_completed
event_cookies = response_event["request"]["cookies"]
assert len(event_cookies) == len(cookies)
for cookie in cookies:
event_cookie = next(
filter(lambda c: c["name"] == cookie["name"], event_cookies), None
)
recursive_compare(cookie, event_cookie)
await bidi_session.storage.delete_cookies()
async def test_override_header_cookie(
setup_blocked_request,
subscribe_events,
wait_for_event,
bidi_session,
):
request = await setup_blocked_request(
"beforeRequestSent", headers={"Cookie": "a=1"}
)
await subscribe_events(events=[RESPONSE_COMPLETED_EVENT])
cookie = CookieHeader(name="b", value=NetworkStringValue("2"))
on_response_completed = wait_for_event(RESPONSE_COMPLETED_EVENT)
await bidi_session.network.continue_request(request=request, cookies=[cookie])
response_event = await on_response_completed
event_cookies = response_event["request"]["cookies"]
recursive_compare([cookie], event_cookies)
await bidi_session.storage.delete_cookies()
async def test_override_modified_header_cookies(
setup_blocked_request,
subscribe_events,
wait_for_event,
bidi_session,
):
request = await setup_blocked_request("beforeRequestSent")
await subscribe_events(events=[RESPONSE_COMPLETED_EVENT])
header = Header(name="Cookie", value=NetworkStringValue("a=1"))
cookie = CookieHeader(name="b", value=NetworkStringValue("2"))
on_response_completed = wait_for_event(RESPONSE_COMPLETED_EVENT)
await bidi_session.network.continue_request(
request=request, headers=[header], cookies=[cookie]
)
response_event = await on_response_completed
event_cookies = response_event["request"]["cookies"]
recursive_compare([cookie], event_cookies)
await bidi_session.storage.delete_cookies()

View file

@ -1,60 +0,0 @@
import pytest
from webdriver.bidi.modules.network import Header, NetworkStringValue
from webdriver.bidi.modules.script import ContextTarget
from .. import assert_response_event, RESPONSE_COMPLETED_EVENT
pytestmark = pytest.mark.asyncio
@pytest.mark.parametrize(
"request_headers, modified_headers",
[
[{"a": "1"}, {}],
[{}, {"b": "2"}],
[{"a": "1", "b": "2"}, {"c": "3", "d": "4"}],
[{"a": "1"}, {"a": "not-1"}],
],
)
async def test_modify_headers(
setup_blocked_request,
subscribe_events,
wait_for_event,
bidi_session,
request_headers,
modified_headers,
):
request = await setup_blocked_request("beforeRequestSent", headers=request_headers)
await subscribe_events(events=[RESPONSE_COMPLETED_EVENT])
headers = []
for name, value in modified_headers.items():
headers.append(Header(name=name, value=NetworkStringValue(value)))
on_response_completed = wait_for_event(RESPONSE_COMPLETED_EVENT)
await bidi_session.network.continue_request(request=request, headers=headers)
response_event = await on_response_completed
assert_response_event(response_event, expected_request={"headers": headers})
async def test_override_cookies(
setup_blocked_request,
subscribe_events,
wait_for_event,
bidi_session,
top_context,
):
await bidi_session.script.evaluate(
expression="document.cookie = 'foo=bar';",
target=ContextTarget(top_context["context"]),
await_promise=False,
)
request = await setup_blocked_request("beforeRequestSent")
await subscribe_events(events=[RESPONSE_COMPLETED_EVENT])
on_response_completed = wait_for_event(RESPONSE_COMPLETED_EVENT)
await bidi_session.network.continue_request(request=request, headers=[])
response_event = await on_response_completed
assert len(response_event["request"]["cookies"]) == 0

View file

@ -192,19 +192,6 @@ async def test_params_headers_header_name_invalid_type(
)
@pytest.mark.parametrize("value", ["", "\u0000", "\"", "{","\u0080"])
async def test_params_headers_header_name_invalid_value(
setup_blocked_request, bidi_session, value
):
request = await setup_blocked_request("beforeRequestSent")
with pytest.raises(error.InvalidArgumentException):
await bidi_session.network.continue_request(
request=request,
headers=[create_header(overrides={"name": value})],
)
@pytest.mark.parametrize("value", [None, False, 42, "foo", []])
async def test_params_headers_header_value_invalid_type(
setup_blocked_request, bidi_session, value
@ -270,19 +257,6 @@ async def test_params_headers_header_value_value_invalid_type(
)
@pytest.mark.parametrize("value", [" a", "a ", "\ta", "a\t", "a\nb", "a\0b"])
async def test_params_headers_header_value_value_invalid_value(
setup_blocked_request, bidi_session, value
):
request = await setup_blocked_request("beforeRequestSent")
with pytest.raises(error.InvalidArgumentException):
await bidi_session.network.continue_request(
request=request,
headers=[create_header(value_overrides={"value": value})],
)
@pytest.mark.parametrize("value", [False, 42, {}, []])
async def test_params_method_invalid_type(setup_blocked_request, bidi_session, value):
request = await setup_blocked_request("beforeRequestSent")
@ -291,14 +265,6 @@ async def test_params_method_invalid_type(setup_blocked_request, bidi_session, v
await bidi_session.network.continue_request(request=request, method=value)
@pytest.mark.parametrize("value", ["", "\u0000", "\"", "{","\u0080"])
async def test_params_method_invalid_value(setup_blocked_request, bidi_session, value):
request = await setup_blocked_request("beforeRequestSent")
with pytest.raises(error.InvalidArgumentException):
await bidi_session.network.continue_request(request=request, method=value)
@pytest.mark.parametrize("value", [None, False, 42, {}, []])
async def test_params_request_invalid_type(bidi_session, value):
with pytest.raises(error.InvalidArgumentException):

View file

@ -1,37 +0,0 @@
import pytest
from webdriver.bidi.modules.script import ContextTarget
from ... import recursive_compare
from .. import assert_response_event, RESPONSE_COMPLETED_EVENT
pytestmark = pytest.mark.asyncio
METHODS = [
"DELETE",
"GET",
"HEAD",
"OPTIONS",
"PATCH",
"POST",
"PUT",
]
@pytest.mark.parametrize("request_method", METHODS)
@pytest.mark.parametrize("updated_method", METHODS)
async def test_request_method(
setup_blocked_request,
subscribe_events,
wait_for_event,
bidi_session,
request_method,
updated_method,
):
request = await setup_blocked_request("beforeRequestSent", method=request_method)
await subscribe_events(events=[RESPONSE_COMPLETED_EVENT])
on_response_completed = wait_for_event(RESPONSE_COMPLETED_EVENT)
await bidi_session.network.continue_request(request=request, method=updated_method)
response_event = await on_response_completed
assert_response_event(response_event, expected_request={"method": updated_method})