fix: webRequest module should work with file:// protocol (#22903)
* fix: override file:// instead of intercepting * test: webRequest module should work with file:// * fix: service work with file:// url * fix: original_response_headers can be null * fix: only register file:// when necessary
This commit is contained in:
parent
bac1c7f532
commit
e6f2605ad0
6 changed files with 101 additions and 7 deletions
|
@ -292,11 +292,12 @@ int WebRequest::OnHeadersReceived(
|
||||||
const net::HttpResponseHeaders* original_response_headers,
|
const net::HttpResponseHeaders* original_response_headers,
|
||||||
scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
|
scoped_refptr<net::HttpResponseHeaders>* override_response_headers,
|
||||||
GURL* allowed_unsafe_redirect_url) {
|
GURL* allowed_unsafe_redirect_url) {
|
||||||
|
const std::string& status_line =
|
||||||
|
original_response_headers ? original_response_headers->GetStatusLine()
|
||||||
|
: std::string();
|
||||||
return HandleResponseEvent(
|
return HandleResponseEvent(
|
||||||
kOnHeadersReceived, info, std::move(callback),
|
kOnHeadersReceived, info, std::move(callback),
|
||||||
std::make_pair(override_response_headers,
|
std::make_pair(override_response_headers, status_line), request);
|
||||||
original_response_headers->GetStatusLine()),
|
|
||||||
request);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebRequest::OnSendHeaders(extensions::WebRequestInfo* info,
|
void WebRequest::OnSendHeaders(extensions::WebRequestInfo* info,
|
||||||
|
|
|
@ -1176,7 +1176,8 @@ void ElectronBrowserClient::RegisterNonNetworkNavigationURLLoaderFactories(
|
||||||
context, false /* we don't support extensions::WebViewGuest */));
|
context, false /* we don't support extensions::WebViewGuest */));
|
||||||
#endif
|
#endif
|
||||||
auto* protocol_registry = ProtocolRegistry::FromBrowserContext(context);
|
auto* protocol_registry = ProtocolRegistry::FromBrowserContext(context);
|
||||||
protocol_registry->RegisterURLLoaderFactories(factories);
|
protocol_registry->RegisterURLLoaderFactories(
|
||||||
|
URLLoaderFactoryType::kNavigation, factories);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||||
|
@ -1236,7 +1237,8 @@ void ElectronBrowserClient::RegisterNonNetworkSubresourceURLLoaderFactories(
|
||||||
|
|
||||||
if (web_contents) {
|
if (web_contents) {
|
||||||
ProtocolRegistry::FromBrowserContext(web_contents->GetBrowserContext())
|
ProtocolRegistry::FromBrowserContext(web_contents->GetBrowserContext())
|
||||||
->RegisterURLLoaderFactories(factories);
|
->RegisterURLLoaderFactories(URLLoaderFactoryType::kDocumentSubResource,
|
||||||
|
factories);
|
||||||
}
|
}
|
||||||
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
#if BUILDFLAG(ENABLE_ELECTRON_EXTENSIONS)
|
||||||
auto factory = extensions::CreateExtensionURLLoaderFactory(render_process_id,
|
auto factory = extensions::CreateExtensionURLLoaderFactory(render_process_id,
|
||||||
|
|
|
@ -825,8 +825,11 @@ void ProxyingURLLoaderFactory::CreateLoaderAndStart(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intercept file:// protocol to support asar archives.
|
// The loader of ServiceWorker forbids loading scripts from file:// URLs, and
|
||||||
if (request.url.SchemeIsFile()) {
|
// Chromium does not provide a way to override this behavior. So in order to
|
||||||
|
// make ServiceWorker work with file:// URLs, we have to intercept its
|
||||||
|
// requests here.
|
||||||
|
if (IsForServiceWorkerScript() && request.url.SchemeIsFile()) {
|
||||||
asar::CreateAsarURLLoader(request, std::move(loader), std::move(client),
|
asar::CreateAsarURLLoader(request, std::move(loader), std::move(client),
|
||||||
new net::HttpResponseHeaders(""));
|
new net::HttpResponseHeaders(""));
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -3,12 +3,46 @@
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "shell/browser/electron_browser_context.h"
|
#include "shell/browser/electron_browser_context.h"
|
||||||
|
#include "shell/browser/net/asar/asar_url_loader.h"
|
||||||
#include "shell/browser/protocol_registry.h"
|
#include "shell/browser/protocol_registry.h"
|
||||||
|
|
||||||
namespace electron {
|
namespace electron {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Provide support for accessing asar archives in file:// protocol.
|
||||||
|
class AsarURLLoaderFactory : public network::mojom::URLLoaderFactory {
|
||||||
|
public:
|
||||||
|
AsarURLLoaderFactory() {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// network::mojom::URLLoaderFactory:
|
||||||
|
void CreateLoaderAndStart(
|
||||||
|
mojo::PendingReceiver<network::mojom::URLLoader> loader,
|
||||||
|
int32_t routing_id,
|
||||||
|
int32_t request_id,
|
||||||
|
uint32_t options,
|
||||||
|
const network::ResourceRequest& request,
|
||||||
|
mojo::PendingRemote<network::mojom::URLLoaderClient> client,
|
||||||
|
const net::MutableNetworkTrafficAnnotationTag& traffic_annotation)
|
||||||
|
override {
|
||||||
|
asar::CreateAsarURLLoader(request, std::move(loader), std::move(client),
|
||||||
|
new net::HttpResponseHeaders(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clone(
|
||||||
|
mojo::PendingReceiver<network::mojom::URLLoaderFactory> loader) override {
|
||||||
|
receivers_.Add(this, std::move(loader));
|
||||||
|
}
|
||||||
|
|
||||||
|
mojo::ReceiverSet<network::mojom::URLLoaderFactory> receivers_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
// static
|
// static
|
||||||
ProtocolRegistry* ProtocolRegistry::FromBrowserContext(
|
ProtocolRegistry* ProtocolRegistry::FromBrowserContext(
|
||||||
content::BrowserContext* context) {
|
content::BrowserContext* context) {
|
||||||
|
@ -20,7 +54,25 @@ ProtocolRegistry::ProtocolRegistry() {}
|
||||||
ProtocolRegistry::~ProtocolRegistry() = default;
|
ProtocolRegistry::~ProtocolRegistry() = default;
|
||||||
|
|
||||||
void ProtocolRegistry::RegisterURLLoaderFactories(
|
void ProtocolRegistry::RegisterURLLoaderFactories(
|
||||||
|
URLLoaderFactoryType type,
|
||||||
content::ContentBrowserClient::NonNetworkURLLoaderFactoryMap* factories) {
|
content::ContentBrowserClient::NonNetworkURLLoaderFactoryMap* factories) {
|
||||||
|
// Override the default FileURLLoaderFactory to support asar archives.
|
||||||
|
if (type == URLLoaderFactoryType::kNavigation) {
|
||||||
|
// Always allow navigating to file:// URLs.
|
||||||
|
//
|
||||||
|
// Note that Chromium calls |emplace| to create the default file factory
|
||||||
|
// after this call, so it won't override our asar factory.
|
||||||
|
DCHECK(!base::Contains(*factories, url::kFileScheme));
|
||||||
|
factories->emplace(url::kFileScheme,
|
||||||
|
std::make_unique<AsarURLLoaderFactory>());
|
||||||
|
} else if (type == URLLoaderFactoryType::kDocumentSubResource) {
|
||||||
|
// Only support requesting file:// subresource URLs when Chromium does so,
|
||||||
|
// it is usually supported under file:// or about:blank documents.
|
||||||
|
auto file_factory = factories->find(url::kFileScheme);
|
||||||
|
if (file_factory != factories->end())
|
||||||
|
file_factory->second = std::make_unique<AsarURLLoaderFactory>();
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto& it : handlers_) {
|
for (const auto& it : handlers_) {
|
||||||
factories->emplace(it.first, std::make_unique<ElectronURLLoaderFactory>(
|
factories->emplace(it.first, std::make_unique<ElectronURLLoaderFactory>(
|
||||||
it.second.first, it.second.second));
|
it.second.first, it.second.second));
|
||||||
|
|
|
@ -22,7 +22,11 @@ class ProtocolRegistry {
|
||||||
|
|
||||||
static ProtocolRegistry* FromBrowserContext(content::BrowserContext*);
|
static ProtocolRegistry* FromBrowserContext(content::BrowserContext*);
|
||||||
|
|
||||||
|
using URLLoaderFactoryType =
|
||||||
|
content::ContentBrowserClient::URLLoaderFactoryType;
|
||||||
|
|
||||||
void RegisterURLLoaderFactories(
|
void RegisterURLLoaderFactories(
|
||||||
|
URLLoaderFactoryType type,
|
||||||
content::ContentBrowserClient::NonNetworkURLLoaderFactoryMap* factories);
|
content::ContentBrowserClient::NonNetworkURLLoaderFactoryMap* factories);
|
||||||
|
|
||||||
const HandlersMap& intercept_handlers() const { return intercept_handlers_; }
|
const HandlersMap& intercept_handlers() const { return intercept_handlers_; }
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { expect } from 'chai';
|
||||||
import * as http from 'http';
|
import * as http from 'http';
|
||||||
import * as qs from 'querystring';
|
import * as qs from 'querystring';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
import * as url from 'url';
|
||||||
import * as WebSocket from 'ws';
|
import * as WebSocket from 'ws';
|
||||||
import { ipcMain, protocol, session, WebContents, webContents } from 'electron';
|
import { ipcMain, protocol, session, WebContents, webContents } from 'electron';
|
||||||
import { AddressInfo } from 'net';
|
import { AddressInfo } from 'net';
|
||||||
|
@ -133,11 +134,24 @@ describe('webRequest module', () => {
|
||||||
await ajax(defaultURL + 'serverRedirect');
|
await ajax(defaultURL + 'serverRedirect');
|
||||||
await ajax(defaultURL + 'serverRedirect');
|
await ajax(defaultURL + 'serverRedirect');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('works with file:// protocol', (done) => {
|
||||||
|
ses.webRequest.onBeforeRequest((details, callback) => {
|
||||||
|
callback({ cancel: true });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
ajax(url.format({
|
||||||
|
pathname: path.join(fixturesPath, 'blank.html').replace(/\\/g, '/'),
|
||||||
|
protocol: 'file',
|
||||||
|
slashes: true
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('webRequest.onBeforeSendHeaders', () => {
|
describe('webRequest.onBeforeSendHeaders', () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
ses.webRequest.onBeforeSendHeaders(null);
|
ses.webRequest.onBeforeSendHeaders(null);
|
||||||
|
ses.webRequest.onSendHeaders(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('receives details object', async () => {
|
it('receives details object', async () => {
|
||||||
|
@ -192,6 +206,24 @@ describe('webRequest module', () => {
|
||||||
});
|
});
|
||||||
await ajax(defaultURL);
|
await ajax(defaultURL);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('works with file:// protocol', (done) => {
|
||||||
|
const requestHeaders = {
|
||||||
|
Test: 'header'
|
||||||
|
};
|
||||||
|
ses.webRequest.onBeforeSendHeaders((details, callback) => {
|
||||||
|
callback({ requestHeaders: requestHeaders });
|
||||||
|
});
|
||||||
|
ses.webRequest.onSendHeaders((details) => {
|
||||||
|
expect(details.requestHeaders).to.deep.equal(requestHeaders);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
ajax(url.format({
|
||||||
|
pathname: path.join(fixturesPath, 'blank.html').replace(/\\/g, '/'),
|
||||||
|
protocol: 'file',
|
||||||
|
slashes: true
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('webRequest.onSendHeaders', () => {
|
describe('webRequest.onSendHeaders', () => {
|
||||||
|
|
Loading…
Add table
Reference in a new issue