Commit b8a7f58f authored by Makoto Shimazu's avatar Makoto Shimazu Committed by Commit Bot

Create non-network factories for subresource loads from a service worker

Previously we skipped to create non-network factories for subresource loading
from a service worker as a performance optimization. It works on
S13nServiceWorker because extensions are handled by a resource dispatcher host.
However, NetworkService doesn't work well because those requests need to be
routed to extensions before going to the NetworkService. This CL fixes by always
creating non-network factories for subresource loads.

Bug: 901443
Change-Id: Ia932dc11352c14be9f33c9e3448891cf66d699e0
Reviewed-on: https://chromium-review.googlesource.com/c/1322258Reviewed-by: default avatarJohn Abd-El-Malek <jam@chromium.org>
Reviewed-by: default avatarMatt Falkenhagen <falken@chromium.org>
Commit-Queue: John Abd-El-Malek <jam@chromium.org>
Cr-Commit-Position: refs/heads/master@{#606099}
parent 1b35e9d3
...@@ -170,32 +170,15 @@ IN_PROC_BROWSER_TEST_F(ChromeDoNotTrackTest, FetchFromServiceWorker) { ...@@ -170,32 +170,15 @@ IN_PROC_BROWSER_TEST_F(ChromeDoNotTrackTest, FetchFromServiceWorker) {
const GURL url = embedded_test_server()->GetURL( const GURL url = embedded_test_server()->GetURL(
std::string("/workers/fetch_from_service_worker.html")); std::string("/workers/fetch_from_service_worker.html"));
ui_test_utils::NavigateToURL(browser(), url); ui_test_utils::NavigateToURL(browser(), url);
EXPECT_EQ("ready", EvalJs(GetWebContents(), "setup();"));
// Wait until service worker become ready.
const base::string16 title = base::ASCIIToUTF16("DONE");
{
content::TitleWatcher watcher(GetWebContents(), title);
EXPECT_EQ(title, watcher.WaitAndGetTitle());
}
ExpectPageTextEq("ready");
const std::string script = const std::string script =
"fetch_from_service_worker('" + fetch_url.spec() + "');"; "fetch_from_service_worker('" + fetch_url.spec() + "');";
ASSERT_TRUE(ExecJs(GetWebContents(), script)); EXPECT_EQ("1", EvalJs(GetWebContents(), script));
{
content::TitleWatcher watcher(GetWebContents(), title);
EXPECT_EQ(title, watcher.WaitAndGetTitle());
}
ExpectPageTextEq("1");
// Updating settings should be reflected immediately. // Updating settings should be reflected immediately.
SetEnableDoNotTrack(false /* enabled */); SetEnableDoNotTrack(false /* enabled */);
ASSERT_TRUE(ExecJs(GetWebContents(), script)); EXPECT_EQ("None", EvalJs(GetWebContents(), script));
{
content::TitleWatcher watcher(GetWebContents(), title);
EXPECT_EQ(title, watcher.WaitAndGetTitle());
}
ExpectPageTextEq("None");
} }
} // namespace } // namespace
...@@ -164,6 +164,37 @@ IN_PROC_BROWSER_TEST_F(ExtensionFetchTest, ...@@ -164,6 +164,37 @@ IN_PROC_BROWSER_TEST_F(ExtensionFetchTest,
EXPECT_EQ("text content", fetch_result); EXPECT_EQ("text content", fetch_result);
} }
// Calling fetch() from a http(s) service worker context to a
// chrome-extensions:// URL since the loading path in a service worker is
// different from pages.
// This is a regression test for https://crbug.com/901443.
IN_PROC_BROWSER_TEST_F(
ExtensionFetchTest,
HostCanFetchWebAccessibleExtensionResource_FetchFromServiceWorker) {
TestExtensionDir dir;
dir.WriteManifestWithSingleQuotes(
"{"
"'background': {'scripts': ['bg.js']},"
"'manifest_version': 2,"
"'name': 'HostCanFetchWebAccessibleExtensionResource_"
"FetchFromServiceWorker',"
"'version': '1',"
"'web_accessible_resources': ['text']"
"}");
const Extension* extension = WriteFilesAndLoadTestExtension(&dir);
ASSERT_TRUE(extension);
content::WebContents* tab =
CreateAndNavigateTab(embedded_test_server()->GetURL(
"/workers/fetch_from_service_worker.html"));
EXPECT_EQ("ready", content::EvalJs(tab, "setup();"));
EXPECT_EQ("text content",
content::EvalJs(
tab, base::StringPrintf(
"fetch_from_service_worker('%s');",
extension->GetResourceURL("text").spec().c_str())));
}
IN_PROC_BROWSER_TEST_F(ExtensionFetchTest, IN_PROC_BROWSER_TEST_F(ExtensionFetchTest,
HostCannotFetchNonWebAccessibleExtensionResource) { HostCannotFetchNonWebAccessibleExtensionResource) {
TestExtensionDir dir; TestExtensionDir dir;
......
<script> <script>
let registration = null; async function setup() {
await navigator.serviceWorker.register('fetch_from_service_worker.js');
self.onload = async () => { await navigator.serviceWorker.ready;
registration = await navigator.serviceWorker.register( return 'ready';
'fetch_from_service_worker.js');
if (!registration) {
document.body.innerText = 'Registration failed';
document.title = 'DONE';
return;
}
registration = await navigator.serviceWorker.ready;
document.body.innerText = 'ready';
document.title = 'DONE';
} }
function fetch_from_service_worker(url) { function fetch_from_service_worker(url) {
document.title = 'Fetching'; return new Promise(async resolve => {
document.innerText = ''; const registration = await navigator.serviceWorker.ready;
if (!registration) { const channel = new MessageChannel();
document.innerText = 'Registration failed'; channel.port1.onmessage = e => { resolve(e.data); };
document.title = 'DONE'; registration.active.postMessage({url}, [channel.port2]);
return; });
}
const channel = new MessageChannel();
channel.port1.onmessage = e => {
document.body.innerText = e.data;
document.title = 'DONE';
};
registration.active.postMessage({url: url}, [channel.port2]);
} }
</script> </script>
...@@ -2,17 +2,20 @@ ...@@ -2,17 +2,20 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
async function MessageHandler(e) { async function handleMessage(e) {
const port = e.ports[0]; try {
const response = await fetch(e.data.url); const response = await fetch(e.data.url);
if (!response.ok) { if (!response.ok) {
port.postMessage('bad response'); e.ports[0].postMessage('bad response');
return; return;
}
const text = await response.text();
e.ports[0].postMessage(text);
} catch (error) {
e.ports[0].postMessage(`${error}`);
} }
const text = await response.text();
port.postMessage(text);
} }
self.addEventListener('message', e => { self.addEventListener('message', e => {
e.waitUntil(MessageHandler(e)); e.waitUntil(handleMessage(e));
}); });
...@@ -96,14 +96,12 @@ void NotifyWorkerVersionDoomedOnUI(int worker_process_id, int worker_route_id) { ...@@ -96,14 +96,12 @@ void NotifyWorkerVersionDoomedOnUI(int worker_process_id, int worker_route_id) {
// Returns a factory bundle for doing loads on behalf of the specified |rph| and // Returns a factory bundle for doing loads on behalf of the specified |rph| and
// |origin|. The returned bundle has a default factory that goes to network and // |origin|. The returned bundle has a default factory that goes to network and
// if |use_non_network_factories| is true it may also include scheme-specific // it may also include scheme-specific factories that don't go to network.
// factories that don't go to network.
// //
// The network factory does not support reconnection to the network service. // The network factory does not support reconnection to the network service.
std::unique_ptr<URLLoaderFactoryBundleInfo> CreateFactoryBundle( std::unique_ptr<URLLoaderFactoryBundleInfo> CreateFactoryBundle(
RenderProcessHost* rph, RenderProcessHost* rph,
const url::Origin& origin, const url::Origin& origin) {
bool use_non_network_factories) {
auto factory_bundle = std::make_unique<URLLoaderFactoryBundleInfo>(); auto factory_bundle = std::make_unique<URLLoaderFactoryBundleInfo>();
network::mojom::URLLoaderFactoryPtrInfo default_factory_info; network::mojom::URLLoaderFactoryPtrInfo default_factory_info;
if (!GetNetworkFactoryCallbackForTest()) { if (!GetNetworkFactoryCallbackForTest()) {
...@@ -118,29 +116,27 @@ std::unique_ptr<URLLoaderFactoryBundleInfo> CreateFactoryBundle( ...@@ -118,29 +116,27 @@ std::unique_ptr<URLLoaderFactoryBundleInfo> CreateFactoryBundle(
} }
factory_bundle->default_factory_info() = std::move(default_factory_info); factory_bundle->default_factory_info() = std::move(default_factory_info);
if (use_non_network_factories) { ContentBrowserClient::NonNetworkURLLoaderFactoryMap non_network_factories;
ContentBrowserClient::NonNetworkURLLoaderFactoryMap non_network_factories; GetContentClient()
GetContentClient() ->browser()
->browser() ->RegisterNonNetworkSubresourceURLLoaderFactories(
->RegisterNonNetworkSubresourceURLLoaderFactories( rph->GetID(), MSG_ROUTING_NONE, &non_network_factories);
rph->GetID(), MSG_ROUTING_NONE, &non_network_factories);
for (auto& pair : non_network_factories) {
for (auto& pair : non_network_factories) { const std::string& scheme = pair.first;
const std::string& scheme = pair.first; std::unique_ptr<network::mojom::URLLoaderFactory> factory =
std::unique_ptr<network::mojom::URLLoaderFactory> factory = std::move(pair.second);
std::move(pair.second);
// To be safe, ignore schemes that aren't allowed to register service
// To be safe, ignore schemes that aren't allowed to register service // workers. We assume that importScripts and fetch() should fail on such
// workers. We assume that importScripts and fetch() should fail on such // schemes.
// schemes. if (!base::ContainsValue(GetServiceWorkerSchemes(), scheme))
if (!base::ContainsValue(GetServiceWorkerSchemes(), scheme)) continue;
continue; network::mojom::URLLoaderFactoryPtr factory_ptr;
network::mojom::URLLoaderFactoryPtr factory_ptr; mojo::MakeStrongBinding(std::move(factory),
mojo::MakeStrongBinding(std::move(factory), mojo::MakeRequest(&factory_ptr));
mojo::MakeRequest(&factory_ptr)); factory_bundle->scheme_specific_factory_infos().emplace(
factory_bundle->scheme_specific_factory_infos().emplace( scheme, factory_ptr.PassInterface());
scheme, factory_ptr.PassInterface());
}
} }
return factory_bundle; return factory_bundle;
} }
...@@ -238,13 +234,7 @@ void SetupOnUIThread(base::WeakPtr<ServiceWorkerProcessManager> process_manager, ...@@ -238,13 +234,7 @@ void SetupOnUIThread(base::WeakPtr<ServiceWorkerProcessManager> process_manager,
// These bundles don't support reconnection to the network service, see // These bundles don't support reconnection to the network service, see
// below comments. // below comments.
if (blink::ServiceWorkerUtils::IsServicificationEnabled()) { if (blink::ServiceWorkerUtils::IsServicificationEnabled()) {
// For performance, we only create the loader factories for non-http(s) const url::Origin origin = url::Origin::Create(params->script_url);
// URLs (e.g. chrome-extension://) when the main script URL is
// non-http(s). We assume an http(s) service worker cannot
// importScripts or fetch() a non-http(s) URL.
bool use_non_network_factories = !params->script_url.SchemeIsHTTPOrHTTPS();
url::Origin origin = url::Origin::Create(params->script_url);
// The bundle for the browser is passed to ServiceWorkerScriptLoaderFactory // The bundle for the browser is passed to ServiceWorkerScriptLoaderFactory
// and used to request non-installed service worker scripts. It's OK to not // and used to request non-installed service worker scripts. It's OK to not
...@@ -252,16 +242,14 @@ void SetupOnUIThread(base::WeakPtr<ServiceWorkerProcessManager> process_manager, ...@@ -252,16 +242,14 @@ void SetupOnUIThread(base::WeakPtr<ServiceWorkerProcessManager> process_manager,
// until the service worker reaches the 'installed' state. // until the service worker reaches the 'installed' state.
// //
// TODO(falken): Only make this bundle for non-installed service workers. // TODO(falken): Only make this bundle for non-installed service workers.
factory_bundle_for_browser = factory_bundle_for_browser = CreateFactoryBundle(rph, origin);
CreateFactoryBundle(rph, origin, use_non_network_factories);
// The bundle for the renderer is passed to the service worker, and // The bundle for the renderer is passed to the service worker, and
// used for subresource loading from the service worker (i.e., fetch()). // used for subresource loading from the service worker (i.e., fetch()).
// It's OK to not support reconnection to the network service because the // It's OK to not support reconnection to the network service because the
// service worker terminates itself when the connection breaks, so a new // service worker terminates itself when the connection breaks, so a new
// instance can be started. // instance can be started.
factory_bundle_for_renderer = factory_bundle_for_renderer = CreateFactoryBundle(rph, origin);
CreateFactoryBundle(rph, origin, use_non_network_factories);
} }
// Register to DevTools and update params accordingly. // Register to DevTools and update params accordingly.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment