diff --git a/shell/browser/net/proxying_url_loader_factory.cc b/shell/browser/net/proxying_url_loader_factory.cc index e71de44a2c..74725b43fc 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:: @@ -863,8 +888,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');