tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 6c989f8755f6b51b8a0ee9cb296deb23a3ecd65c
parent f2f4aa5ed427daae506c06b12fc662b9a8844a5a
Author: Julian Descottes <jdescottes@mozilla.com>
Date:   Thu, 16 Oct 2025 14:06:21 +0000

Bug 1966494 - Add browser mochitest to test fetch after suspend in http-on-examine-response r=valentin

Differential Revision: https://phabricator.services.mozilla.com/D250450

Diffstat:
Mnetwerk/test/browser/browser.toml | 2++
Anetwerk/test/browser/browser_fetch_after_suspending_request.js | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 89 insertions(+), 0 deletions(-)

diff --git a/netwerk/test/browser/browser.toml b/netwerk/test/browser/browser.toml @@ -180,6 +180,8 @@ support-files = ["res_hello_h1.sjs"] ["browser_essential_domain_fallbacks.js"] +["browser_fetch_after_suspending_request.js"] + ["browser_fetch_lnk.js"] run-if = ["os == 'win'"] support-files = ["file_lnk.lnk",] diff --git a/netwerk/test/browser/browser_fetch_after_suspending_request.js b/netwerk/test/browser/browser_fetch_after_suspending_request.js @@ -0,0 +1,87 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function () { + info("Run test with network.http.rcwn.enabled set to true"); + await test_fetch_after_suspend(true); + info("Run test with network.http.rcwn.enabled set to false"); + await test_fetch_after_suspend(false); +}); + +let wrappedChannel; + +async function test_fetch_after_suspend(rcwnEnabled) { + info("Set network.http.rcwn.enabled to " + rcwnEnabled); + await SpecialPowers.pushPrefEnv({ + set: [["network.http.rcwn.enabled", rcwnEnabled]], + }); + + info("Add a new test tab"); + const tab = BrowserTestUtils.addTab( + gBrowser, + "https://example.com/document-builder.sjs?html=tab" + ); + await BrowserTestUtils.browserLoaded(tab.linkedBrowser); + + const testBlockedUrl = "https://example.com/?test-blocked"; + + info(`Add an observer to suspend the next channel to ${testBlockedUrl}`); + const { promise, resolve } = Promise.withResolvers(); + const onExamineResponse = subject => { + if (!(subject instanceof Ci.nsIHttpChannel)) { + return; + } + + const channel = subject.QueryInterface(Ci.nsIHttpChannel); + if (channel.URI.displaySpec !== testBlockedUrl) { + return; + } + + wrappedChannel = ChannelWrapper.get(channel); + wrappedChannel.suspend("test-blocked-suspend"); + Services.obs.removeObserver(onExamineResponse, "http-on-examine-response"); + resolve(); + }; + Services.obs.addObserver(onExamineResponse, "http-on-examine-response"); + + info(`Send fetch call for ${testBlockedUrl}`); + let first = fetch(tab.linkedBrowser, testBlockedUrl); + + info( + "Wait for the fetch request to be suspended in http-on-examine-response" + ); + await promise; + + info("Fetch the same URL again"); + let second = fetch(tab.linkedBrowser, testBlockedUrl); + let secondCompleted = false; + second.then(() => { + secondCompleted = true; + }); + + // eslint-disable-next-line mozilla/no-arbitrary-setTimeout + let timer = new Promise(resolve => setTimeout(resolve, 6000)); + await Promise.race([second, timer]); + + Assert.equal( + secondCompleted, + true, + "The second fetch should resolve successfully" + ); + + // Resume the first channel and await its completion so we don't leak anything. + wrappedChannel.resume(); + await first; + + info("Cleanup"); + gBrowser.removeTab(tab); +} + +function fetch(browser, url) { + return SpecialPowers.spawn(browser, [url], async _url => { + const response = await content.fetch(_url); + await response.text(); + }); +}