Commit e3e7e403 authored by Robert Ogden's avatar Robert Ogden Committed by Commit Bot

Add IsolatedPrerender ServiceWorkers Observer

This class maintains an index of all registered service workers by
origin, querying the whole list on startup and observering additional
registrations during the browsing session.

This class is owned by IsolatedPrerenderService but isn't wired into
the triggering logic, yet.

Bug: 1023486
Change-Id: I077512366b85ebe20e87fe14bf2ea9088d611820
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2031527Reviewed-by: default avatarMatt Falkenhagen <falken@chromium.org>
Reviewed-by: default avatarTarun Bansal <tbansal@chromium.org>
Commit-Queue: Robert Ogden <robertogden@chromium.org>
Cr-Commit-Position: refs/heads/master@{#737347}
parent 0ee20a0c
...@@ -1326,6 +1326,8 @@ jumbo_static_library("browser") { ...@@ -1326,6 +1326,8 @@ jumbo_static_library("browser") {
"prerender/isolated/isolated_prerender_service.h", "prerender/isolated/isolated_prerender_service.h",
"prerender/isolated/isolated_prerender_service_factory.cc", "prerender/isolated/isolated_prerender_service_factory.cc",
"prerender/isolated/isolated_prerender_service_factory.h", "prerender/isolated/isolated_prerender_service_factory.h",
"prerender/isolated/isolated_prerender_service_workers_observer.cc",
"prerender/isolated/isolated_prerender_service_workers_observer.h",
"prerender/isolated/isolated_prerender_tab_helper.cc", "prerender/isolated/isolated_prerender_tab_helper.cc",
"prerender/isolated/isolated_prerender_tab_helper.h", "prerender/isolated/isolated_prerender_tab_helper.h",
"prerender/isolated/isolated_prerender_url_loader.cc", "prerender/isolated/isolated_prerender_url_loader.cc",
......
...@@ -8,12 +8,16 @@ ...@@ -8,12 +8,16 @@
#include "base/bind.h" #include "base/bind.h"
#include "base/command_line.h" #include "base/command_line.h"
#include "base/optional.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/task/post_task.h" #include "base/task/post_task.h"
#include "base/test/metrics/histogram_tester.h" #include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h" #include "base/test/scoped_feature_list.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "chrome/browser/prerender/isolated/isolated_prerender_features.h" #include "chrome/browser/prerender/isolated/isolated_prerender_features.h"
#include "chrome/browser/prerender/isolated/isolated_prerender_service.h"
#include "chrome/browser/prerender/isolated/isolated_prerender_service_factory.h"
#include "chrome/browser/prerender/isolated/isolated_prerender_service_workers_observer.h"
#include "chrome/browser/prerender/isolated/isolated_prerender_url_loader_interceptor.h" #include "chrome/browser/prerender/isolated/isolated_prerender_url_loader_interceptor.h"
#include "chrome/browser/prerender/prerender_final_status.h" #include "chrome/browser/prerender/prerender_final_status.h"
#include "chrome/browser/prerender/prerender_handle.h" #include "chrome/browser/prerender/prerender_handle.h"
...@@ -36,6 +40,7 @@ ...@@ -36,6 +40,7 @@
#include "net/test/embedded_test_server/http_response.h" #include "net/test/embedded_test_server/http_response.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h" #include "url/gurl.h"
#include "url/origin.h"
namespace { namespace {
...@@ -64,6 +69,9 @@ class IsolatedPrerenderBrowserTest ...@@ -64,6 +69,9 @@ class IsolatedPrerenderBrowserTest
void SetUpOnMainThread() override { void SetUpOnMainThread() override {
InProcessBrowserTest::SetUpOnMainThread(); InProcessBrowserTest::SetUpOnMainThread();
// Ensure the service gets created before the tests start.
IsolatedPrerenderServiceFactory::GetForProfile(browser()->profile());
origin_server_ = std::make_unique<net::EmbeddedTestServer>( origin_server_ = std::make_unique<net::EmbeddedTestServer>(
net::EmbeddedTestServer::TYPE_HTTPS); net::EmbeddedTestServer::TYPE_HTTPS);
origin_server_->ServeFilesFromSourceDirectory("chrome/test/data"); origin_server_->ServeFilesFromSourceDirectory("chrome/test/data");
...@@ -182,7 +190,31 @@ IN_PROC_BROWSER_TEST_F(IsolatedPrerenderBrowserTest, ...@@ -182,7 +190,31 @@ IN_PROC_BROWSER_TEST_F(IsolatedPrerenderBrowserTest,
// Navigate to the same origin and expect it to have cookies. // Navigate to the same origin and expect it to have cookies.
// Note: This check needs to come after the prerender, otherwise the prerender // Note: This check needs to come after the prerender, otherwise the prerender
// will be cancelled because the origin was recently loaded. // will be canceled because the origin was recently loaded.
ui_test_utils::NavigateToURL(browser(), GetOriginServerURL("/simple.html")); ui_test_utils::NavigateToURL(browser(), GetOriginServerURL("/simple.html"));
EXPECT_EQ(1U, origin_server_request_with_cookies()); EXPECT_EQ(1U, origin_server_request_with_cookies());
} }
IN_PROC_BROWSER_TEST_F(
IsolatedPrerenderBrowserTest,
DISABLE_ON_WIN_MAC_CHROMEOS(ServiceWorkerRegistrationIsObserved)) {
SetDataSaverEnabled(true);
// Load a page that registers a service worker.
ui_test_utils::NavigateToURL(
browser(),
GetOriginServerURL("/service_worker/create_service_worker.html"));
EXPECT_EQ("DONE", EvalJs(browser()->tab_strip_model()->GetActiveWebContents(),
"register('network_fallback_worker.js');"));
IsolatedPrerenderService* isolated_prerender_service =
IsolatedPrerenderServiceFactory::GetForProfile(browser()->profile());
EXPECT_EQ(base::Optional<bool>(true),
isolated_prerender_service->service_workers_observer()
->IsServiceWorkerRegisteredForOrigin(
url::Origin::Create(GetOriginServerURL("/"))));
EXPECT_EQ(base::Optional<bool>(false),
isolated_prerender_service->service_workers_observer()
->IsServiceWorkerRegisteredForOrigin(
url::Origin::Create(GURL("https://unregistered.com"))));
}
...@@ -8,19 +8,24 @@ ...@@ -8,19 +8,24 @@
#include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_factory.h" #include "chrome/browser/data_reduction_proxy/data_reduction_proxy_chrome_settings_factory.h"
#include "chrome/browser/prerender/isolated/isolated_prerender_params.h" #include "chrome/browser/prerender/isolated/isolated_prerender_params.h"
#include "chrome/browser/prerender/isolated/isolated_prerender_proxy_configurator.h" #include "chrome/browser/prerender/isolated/isolated_prerender_proxy_configurator.h"
#include "chrome/browser/prerender/isolated/isolated_prerender_service_workers_observer.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
IsolatedPrerenderService::IsolatedPrerenderService(Profile* profile) IsolatedPrerenderService::IsolatedPrerenderService(Profile* profile)
: profile_(profile), : profile_(profile),
proxy_configurator_( proxy_configurator_(
std::make_unique<IsolatedPrerenderProxyConfigurator>()) { std::make_unique<IsolatedPrerenderProxyConfigurator>()),
service_workers_observer_(
std::make_unique<IsolatedPrerenderServiceWorkersObserver>(profile)) {
DataReductionProxyChromeSettings* drp_settings = DataReductionProxyChromeSettings* drp_settings =
DataReductionProxyChromeSettingsFactory::GetForBrowserContext(profile_); DataReductionProxyChromeSettingsFactory::GetForBrowserContext(profile_);
if (drp_settings) if (drp_settings)
drp_settings->AddDataReductionProxySettingsObserver(this); drp_settings->AddDataReductionProxySettingsObserver(this);
} }
IsolatedPrerenderService::~IsolatedPrerenderService() { IsolatedPrerenderService::~IsolatedPrerenderService() = default;
void IsolatedPrerenderService::Shutdown() {
DataReductionProxyChromeSettings* drp_settings = DataReductionProxyChromeSettings* drp_settings =
DataReductionProxyChromeSettingsFactory::GetForBrowserContext(profile_); DataReductionProxyChromeSettingsFactory::GetForBrowserContext(profile_);
if (drp_settings) if (drp_settings)
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
class Profile; class Profile;
class IsolatedPrerenderProxyConfigurator; class IsolatedPrerenderProxyConfigurator;
class IsolatedPrerenderServiceWorkersObserver;
// This service owns browser-level objects used in Isolated Prerenders. // This service owns browser-level objects used in Isolated Prerenders.
class IsolatedPrerenderService class IsolatedPrerenderService
...@@ -25,6 +26,10 @@ class IsolatedPrerenderService ...@@ -25,6 +26,10 @@ class IsolatedPrerenderService
return proxy_configurator_.get(); return proxy_configurator_.get();
} }
IsolatedPrerenderServiceWorkersObserver* service_workers_observer() {
return service_workers_observer_.get();
}
private: private:
// data_reduction_proxy::DataReductionProxySettingsObserver: // data_reduction_proxy::DataReductionProxySettingsObserver:
void OnProxyRequestHeadersChanged( void OnProxyRequestHeadersChanged(
...@@ -32,12 +37,19 @@ class IsolatedPrerenderService ...@@ -32,12 +37,19 @@ class IsolatedPrerenderService
void OnSettingsInitialized() override; void OnSettingsInitialized() override;
void OnDataSaverEnabledChanged(bool enabled) override; void OnDataSaverEnabledChanged(bool enabled) override;
// KeyedService:
void Shutdown() override;
// The current profile, not owned. // The current profile, not owned.
Profile* profile_; Profile* profile_;
// The custom proxy configurator for Isolated Prerenders. // The custom proxy configurator for Isolated Prerenders.
std::unique_ptr<IsolatedPrerenderProxyConfigurator> proxy_configurator_; std::unique_ptr<IsolatedPrerenderProxyConfigurator> proxy_configurator_;
// The storage partition-level observer of registered service workers.
std::unique_ptr<IsolatedPrerenderServiceWorkersObserver>
service_workers_observer_;
IsolatedPrerenderService(const IsolatedPrerenderService&) = delete; IsolatedPrerenderService(const IsolatedPrerenderService&) = delete;
IsolatedPrerenderService& operator=(const IsolatedPrerenderService&) = delete; IsolatedPrerenderService& operator=(const IsolatedPrerenderService&) = delete;
}; };
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/prerender/isolated/isolated_prerender_service_workers_observer.h"
#include "chrome/browser/profiles/profile.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/service_worker_context.h"
#include "content/public/browser/storage_partition.h"
IsolatedPrerenderServiceWorkersObserver::
IsolatedPrerenderServiceWorkersObserver(Profile* profile) {
// For testing.
if (!profile)
return;
// Only the default storage partition is supported for Isolated Prerender.
content::ServiceWorkerContext* service_worker_context =
content::BrowserContext::GetDefaultStoragePartition(profile)
->GetServiceWorkerContext();
service_worker_context->GetAllOriginsInfo(
base::BindOnce(&IsolatedPrerenderServiceWorkersObserver::OnHasUsageInfo,
weak_factory_.GetWeakPtr()));
observer_.Add(service_worker_context);
}
IsolatedPrerenderServiceWorkersObserver::
~IsolatedPrerenderServiceWorkersObserver() = default;
base::Optional<bool>
IsolatedPrerenderServiceWorkersObserver::IsServiceWorkerRegisteredForOrigin(
const url::Origin& query_origin) const {
if (!has_usage_info_) {
return base::nullopt;
}
for (const url::Origin& origin : registered_origins_) {
if (origin == query_origin) {
return true;
}
}
return false;
}
void IsolatedPrerenderServiceWorkersObserver::OnRegistrationCompleted(
const GURL& scope) {
registered_origins_.insert(url::Origin::Create(scope));
}
void IsolatedPrerenderServiceWorkersObserver::OnDestruct(
content::ServiceWorkerContext* context) {
observer_.Remove(context);
}
void IsolatedPrerenderServiceWorkersObserver::CallOnHasUsageInfoForTesting(
const std::vector<content::StorageUsageInfo>& usage_info) {
OnHasUsageInfo(usage_info);
}
void IsolatedPrerenderServiceWorkersObserver::OnHasUsageInfo(
const std::vector<content::StorageUsageInfo>& usage_info) {
has_usage_info_ = true;
for (const content::StorageUsageInfo& info : usage_info) {
registered_origins_.insert(info.origin);
}
}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_PRERENDER_ISOLATED_ISOLATED_PRERENDER_SERVICE_WORKERS_OBSERVER_H_
#define CHROME_BROWSER_PRERENDER_ISOLATED_ISOLATED_PRERENDER_SERVICE_WORKERS_OBSERVER_H_
#include <set>
#include <vector>
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/scoped_observer.h"
#include "content/public/browser/service_worker_context.h"
#include "content/public/browser/service_worker_context_observer.h"
#include "content/public/browser/storage_usage_info.h"
#include "url/gurl.h"
#include "url/origin.h"
class Profile;
// A helper class that queries and observes the ServiceWorkerContext in order to
// provide a synchronous lookup of registered service workers to the rest of
// this feature.
// Note:
// * Service worker de-registration is intentionally ignored. We don't want to
// trigger the Isolated Prerender feature when there has been a service worker
// for an origin at any time during the browsing session.
class IsolatedPrerenderServiceWorkersObserver
: public content::ServiceWorkerContextObserver {
public:
explicit IsolatedPrerenderServiceWorkersObserver(Profile* profile);
~IsolatedPrerenderServiceWorkersObserver() override;
// Returns true if there is a service worker registered for the given
// |query_origin| in the default storage partition, or false if there isn't
// one. Returns nullopt when |this| is still waiting for ServiceWorkerContext
// to provide a response.
base::Optional<bool> IsServiceWorkerRegisteredForOrigin(
const url::Origin& query_origin) const;
// Publicly exposes |OnHasUsageInfo| for testing only.
void CallOnHasUsageInfoForTesting(
const std::vector<content::StorageUsageInfo>& usage_info);
private:
// content::ServiceWorkerContextObserver:
void OnRegistrationCompleted(const GURL& scope) override;
void OnDestruct(content::ServiceWorkerContext* context) override;
// content::GetUsageInfoCallback
void OnHasUsageInfo(const std::vector<content::StorageUsageInfo>& usage_info);
ScopedObserver<content::ServiceWorkerContext,
content::ServiceWorkerContextObserver>
observer_{this};
// Set to true when |OnHasUsageInfo| is called.
bool has_usage_info_ = false;
// The origin of all registered service workers.
std::set<url::Origin> registered_origins_;
base::WeakPtrFactory<IsolatedPrerenderServiceWorkersObserver> weak_factory_{
this};
IsolatedPrerenderServiceWorkersObserver(
const IsolatedPrerenderServiceWorkersObserver&) = delete;
IsolatedPrerenderServiceWorkersObserver& operator=(
const IsolatedPrerenderServiceWorkersObserver&) = delete;
};
#endif // CHROME_BROWSER_PRERENDER_ISOLATED_ISOLATED_PRERENDER_SERVICE_WORKERS_OBSERVER_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/prerender/isolated/isolated_prerender_service_workers_observer.h"
#include "base/optional.h"
#include "content/public/browser/storage_usage_info.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace {
const GURL kTestURL("https://test.com/path?foo=bar");
const GURL kOtherURL("https://other.com/path?what=ever");
const url::Origin kTestOrigin(url::Origin::Create(kTestURL));
const url::Origin kOtherOrigin(url::Origin::Create(kOtherURL));
} // namespace
TEST(IsolatedPrerenderServiceWorkersObserverTest, NotReady) {
IsolatedPrerenderServiceWorkersObserver observer(nullptr);
EXPECT_EQ(base::nullopt,
observer.IsServiceWorkerRegisteredForOrigin(kTestOrigin));
}
TEST(IsolatedPrerenderServiceWorkersObserverTest, UsageInfoCallback) {
IsolatedPrerenderServiceWorkersObserver observer(nullptr);
content::StorageUsageInfo info{kTestOrigin, /*total_size_bytes=*/0,
/*last_modified=*/base::Time()};
observer.CallOnHasUsageInfoForTesting({info});
EXPECT_EQ(base::Optional<bool>(true),
observer.IsServiceWorkerRegisteredForOrigin(kTestOrigin));
EXPECT_EQ(base::Optional<bool>(false),
observer.IsServiceWorkerRegisteredForOrigin(kOtherOrigin));
}
TEST(IsolatedPrerenderServiceWorkersObserverTest, OnRegistration) {
IsolatedPrerenderServiceWorkersObserver observer(nullptr);
observer.CallOnHasUsageInfoForTesting({});
EXPECT_EQ(base::Optional<bool>(false),
observer.IsServiceWorkerRegisteredForOrigin(kTestOrigin));
content::ServiceWorkerContextObserver* sw_observer = &observer;
sw_observer->OnRegistrationCompleted(kTestURL);
EXPECT_EQ(base::Optional<bool>(true),
observer.IsServiceWorkerRegisteredForOrigin(kTestOrigin));
}
...@@ -35,6 +35,9 @@ bool ShouldInterceptRequestForPrerender( ...@@ -35,6 +35,9 @@ bool ShouldInterceptRequestForPrerender(
// TODO(crbug.com/1023486): Add other triggering checks. // TODO(crbug.com/1023486): Add other triggering checks.
// TODO(robertogden): Bail GetStoragePartitionForSite(url) !=
// GetDefaultStoragePartitionForSite().
content::WebContents* web_contents = content::WebContents* web_contents =
content::WebContents::FromFrameTreeNodeId(frame_tree_node_id); content::WebContents::FromFrameTreeNodeId(frame_tree_node_id);
if (!web_contents) if (!web_contents)
......
...@@ -3312,6 +3312,7 @@ test("unit_tests") { ...@@ -3312,6 +3312,7 @@ test("unit_tests") {
"../browser/prefs/proxy_policy_unittest.cc", "../browser/prefs/proxy_policy_unittest.cc",
"../browser/prefs/session_startup_pref_unittest.cc", "../browser/prefs/session_startup_pref_unittest.cc",
"../browser/prerender/isolated/isolated_prerender_proxy_configurator_unittest.cc", "../browser/prerender/isolated/isolated_prerender_proxy_configurator_unittest.cc",
"../browser/prerender/isolated/isolated_prerender_service_workers_observer_unittest.cc",
"../browser/prerender/isolated/isolated_prerender_tab_helper_unittest.cc", "../browser/prerender/isolated/isolated_prerender_tab_helper_unittest.cc",
"../browser/prerender/isolated/isolated_prerender_url_loader_interceptor_unittest.cc", "../browser/prerender/isolated/isolated_prerender_url_loader_interceptor_unittest.cc",
"../browser/prerender/prerender_history_unittest.cc", "../browser/prerender/prerender_history_unittest.cc",
......
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