From c851e5cc865fa550ca7a0d2a1cbcdbc22ff9a658 Mon Sep 17 00:00:00 2001 From: "trop[bot]" <37223003+trop[bot]@users.noreply.github.com> Date: Wed, 29 Apr 2026 21:55:38 -0500 Subject: [PATCH] fix: use no-op header client for Fetch-intercepted requests (#51372) fix: use no-op header client for Fetch-intercepted requests (#50744) * fix: use the non-pass-through path for Fetch-intercepted requests * Revert "fix: use the non-pass-through path for Fetch-intercepted requests" This reverts commit 395fb8bb8c9a59f7e069705f058ac3031a07f7f0. * fix: use no-op header client for Fetch-intercepted requests * fix: bring back `DCHECK` that was prematurely removed * style: reformat code Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Noah Gregory --- .../net/proxying_url_loader_factory.cc | 33 +++++++++++++++- spec/api-debugger-spec.ts | 38 +++++++++++++++++++ 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/shell/browser/net/proxying_url_loader_factory.cc b/shell/browser/net/proxying_url_loader_factory.cc index 45863af87c..50644e54b7 100644 --- a/shell/browser/net/proxying_url_loader_factory.cc +++ b/shell/browser/net/proxying_url_loader_factory.cc @@ -13,6 +13,7 @@ #include "base/strings/string_split.h" #include "content/public/browser/browser_context.h" #include "extensions/browser/extension_navigation_ui_data.h" +#include "mojo/public/cpp/bindings/self_owned_receiver.h" #include "net/base/completion_repeating_callback.h" #include "net/base/load_flags.h" #include "net/http/http_response_headers.h" @@ -29,6 +30,30 @@ namespace electron { +namespace { + +class NoOpHeaderClient final : public network::mojom::TrustedHeaderClient { + public: + NoOpHeaderClient() = default; + NoOpHeaderClient(const NoOpHeaderClient&) = delete; + NoOpHeaderClient& operator=(const NoOpHeaderClient&) = delete; + ~NoOpHeaderClient() override = default; + + void OnBeforeSendHeaders(const net::HttpRequestHeaders& headers, + OnBeforeSendHeadersCallback callback) override { + std::move(callback).Run(net::OK, std::nullopt); + } + + void OnHeadersReceived(const std::string& headers, + const net::IPEndPoint& remote_endpoint, + const std::optional& ssl_info, + OnHeadersReceivedCallback callback) override { + std::move(callback).Run(net::OK, std::nullopt, std::nullopt); + } +}; + +} // namespace + ProxyingURLLoaderFactory::InProgressRequest::FollowRedirectParams:: FollowRedirectParams() = default; ProxyingURLLoaderFactory::InProgressRequest::FollowRedirectParams:: @@ -869,8 +894,14 @@ void ProxyingURLLoaderFactory::OnLoaderCreated( int32_t request_id, mojo::PendingReceiver receiver) { auto it = network_request_id_to_web_request_id_.find(request_id); - if (it == network_request_id_to_web_request_id_.end()) + if (it == network_request_id_to_web_request_id_.end()) { + // Chromium can require the header client pipe to be bound even when + // Electron is using the pass-through path. Dropping the receiver here + // disconnects the URLLoader and causes the request to fail with ERR_FAILED. + mojo::MakeSelfOwnedReceiver(std::make_unique(), + std::move(receiver)); return; + } auto request_it = requests_.find(it->second); DCHECK(request_it != requests_.end()); diff --git a/spec/api-debugger-spec.ts b/spec/api-debugger-spec.ts index a09d7422b6..226c18aacf 100644 --- a/spec/api-debugger-spec.ts +++ b/spec/api-debugger-spec.ts @@ -180,6 +180,44 @@ describe('debugger module', () => { await loadingFinished; }); + it('can continue a Fetch-paused document navigation without webRequest listeners', async () => { + server = http.createServer((_req, res) => { + res.setHeader('Content-Type', 'text/html; charset=utf-8'); + res.end('

Hello World

'); + }); + + const { port } = await listen(server); + const url = `http://localhost:${port}`; + const continueRequests: Array> = []; + + const onMessage = (_event: Electron.Event, method: string, params: any) => { + if (method === 'Fetch.requestPaused') { + continueRequests.push( + w.webContents.debugger.sendCommand('Fetch.continueRequest', { + requestId: params.requestId + }) + ); + } + }; + + w.webContents.debugger.attach(); + w.webContents.debugger.on('message', onMessage); + + try { + await w.webContents.debugger.sendCommand('Fetch.enable', { + patterns: [{ resourceType: 'Document' }] + }); + + await expect(w.loadURL(url)).to.eventually.be.fulfilled(); + await Promise.all(continueRequests); + } finally { + w.webContents.debugger.off('message', onMessage); + if (w.webContents.debugger.isAttached()) { + w.webContents.debugger.detach(); + } + } + }); + it('can get and set cookies using the Storage API', async () => { await w.webContents.loadURL('about:blank'); w.webContents.debugger.attach('1.1');