Commit c4bd2bd1 authored by Kenichi Ishibashi's avatar Kenichi Ishibashi Committed by Commit Bot

Add ServiceWorkerContextWrapper::GetInstalledRegistrationOrigins

The purpose of introducing the method is to remove GetAllOriginsInfo()
calls from ServiceWorkerQuotaClient. ServiceWorkerQuotaClient
called GetAllOriginsInfo() to check if there are registrations for an
origin.

As explained in [1], GetAllOriginsInfo() could be very expensive when
there are many origins that have service worker registrations. Instead
of calling GetAllOriginsInfo(), we can use |registered_origins_| in
ServiceWorkerContextWrapper to provide origins that have registrations.
Using |registered_origins_| should be much faster than
GetAllOriginsInfo() because we don't need to read all registrations
from the database.

Performance comparison:
It seems that ServiceWorkerQuotaClient::GetOriginsForHost() is called
for each origin which has registrations when chrome://settings is
opened. In my local environment with 15k origins/registrations, time
to finish getting all origins when chrome://settings is opened are:
* with this CL: ~4 secs
* without this CL: ~44 mins

[1] https://docs.google.com/document/d/1iaQGQzovFVL3LxDiyIvVgEUFWMhzrXpdf_6TmuwIICA/edit?usp=sharing

Bug: 807440
Change-Id: I804adf45292e9191482ff7f3c412ce03a0eb7bb8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2253259
Commit-Queue: Kenichi Ishibashi <bashi@chromium.org>
Reviewed-by: default avatarVictor Costan <pwnall@chromium.org>
Reviewed-by: default avatarMatt Falkenhagen <falken@chromium.org>
Cr-Commit-Position: refs/heads/master@{#781178}
parent f413a90c
......@@ -1100,6 +1100,31 @@ void ServiceWorkerContextWrapper::GetAllRegistrationsOnCoreThread(
context_core_->registry()->GetAllRegistrationsInfos(std::move(callback));
}
void ServiceWorkerContextWrapper::GetInstalledRegistrationOrigins(
base::Optional<std::string> host_filter,
GetInstalledRegistrationOriginsCallback callback) {
RunOrPostTaskOnCoreThread(
FROM_HERE, base::BindOnce(&ServiceWorkerContextWrapper::
GetInstalledRegistrationOriginsOnCoreThread,
this, host_filter, std::move(callback),
base::ThreadTaskRunnerHandle::Get()));
}
void ServiceWorkerContextWrapper::GetInstalledRegistrationOriginsOnCoreThread(
base::Optional<std::string> host_filter,
GetInstalledRegistrationOriginsCallback callback,
scoped_refptr<base::SingleThreadTaskRunner> task_runner_for_callback) {
DCHECK_CURRENTLY_ON(GetCoreThreadId());
std::set<url::Origin> origins;
for (const auto& origin : registered_origins_) {
if (host_filter.has_value() && host_filter.value() != origin.host())
continue;
origins.insert(origin);
}
task_runner_for_callback->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), std::move(origins)));
}
void ServiceWorkerContextWrapper::GetStorageUsageForOrigin(
const url::Origin& origin,
GetStorageUsageForOriginCallback callback) {
......
......@@ -75,6 +75,8 @@ class CONTENT_EXPORT ServiceWorkerContextWrapper
ServiceWorkerRegistry::GetUserKeysAndDataCallback;
using GetUserDataForAllRegistrationsCallback =
ServiceWorkerRegistry::GetUserDataForAllRegistrationsCallback;
using GetInstalledRegistrationOriginsCallback =
base::OnceCallback<void(const std::set<url::Origin>& origins)>;
using GetStorageUsageForOriginCallback =
ServiceWorkerRegistry::GetStorageUsageForOriginCallback;
......@@ -306,11 +308,24 @@ class CONTENT_EXPORT ServiceWorkerContextWrapper
const std::string& key_prefix,
StatusCallback callback);
// Returns a set of origins which have at least one stored registration.
// The set doesn't include installing/uninstalling/uninstalled registrations.
// When |host_filter| is specified the set only includes origins whose host
// matches |host_filter|.
// This function can be called from any thread and the callback is called on
// that thread.
void GetInstalledRegistrationOrigins(
base::Optional<std::string> host_filter,
GetInstalledRegistrationOriginsCallback callback);
// Returns total resource size stored in the storage for |origin|.
// This can be called from any thread.
// This can be called from any thread and the callback is called on that
// thread.
void GetStorageUsageForOrigin(const url::Origin& origin,
GetStorageUsageForOriginCallback callback);
// Returns a list of ServiceWorkerRegistration for |origin|. The list includes
// stored registrations and installing (not stored yet) registrations.
// Must be called on the core thread, and the callback is called on that
// thread. This restriction is because the callback gets pointers to live
// registrations, which live on the core thread.
......@@ -557,6 +572,10 @@ class CONTENT_EXPORT ServiceWorkerContextWrapper
void StopAllServiceWorkersOnCoreThread(
base::OnceClosure callback,
scoped_refptr<base::SingleThreadTaskRunner> task_runner_for_callback);
void GetInstalledRegistrationOriginsOnCoreThread(
base::Optional<std::string> host_filter,
GetInstalledRegistrationOriginsCallback callback,
scoped_refptr<base::SingleThreadTaskRunner> task_runner_for_callback);
void GetStorageUsageForOriginOnCoreThread(
const url::Origin& origin,
GetStorageUsageForOriginCallback callback);
......
......@@ -62,6 +62,48 @@ class ServiceWorkerContextWrapperTest : public testing::Test {
ServiceWorkerRegistry* registry() { return context()->registry(); }
ServiceWorkerStorage* storage() { return context()->storage(); }
blink::ServiceWorkerStatusCode StoreRegistration(
scoped_refptr<ServiceWorkerRegistration> registration) {
blink::ServiceWorkerStatusCode result;
base::RunLoop loop;
registry()->StoreRegistration(
registration.get(), registration->waiting_version(),
base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
result = status;
loop.Quit();
}));
loop.Run();
return result;
}
blink::ServiceWorkerStatusCode DeleteRegistration(
scoped_refptr<ServiceWorkerRegistration> registration) {
blink::ServiceWorkerStatusCode result;
base::RunLoop loop;
registry()->DeleteRegistration(
registration, registration->scope().GetOrigin(),
base::BindLambdaForTesting([&](blink::ServiceWorkerStatusCode status) {
result = status;
loop.Quit();
}));
loop.Run();
return result;
}
std::set<url::Origin> GetInstalledRegistrationOrigins(
base::Optional<std::string> host_filter) {
std::set<url::Origin> result;
base::RunLoop loop;
wrapper_->GetInstalledRegistrationOrigins(
host_filter,
base::BindLambdaForTesting([&](const std::set<url::Origin>& origins) {
result = origins;
loop.Quit();
}));
loop.Run();
return result;
}
protected:
BrowserTaskEnvironment task_environment_{BrowserTaskEnvironment::IO_MAINLOOP};
base::ScopedTempDir user_data_directory_;
......@@ -240,4 +282,184 @@ TEST_F(ServiceWorkerContextWrapperTest, DeleteRegistration) {
EXPECT_FALSE(wrapper_->HasRegistrationForOrigin(
url::Origin::Create(GURL("https://example2.com"))));
}
// GetInstalledRegistrationOrigins tests:
// No registration.
TEST_F(ServiceWorkerContextWrapperTest, GetInstalledRegistrationOrigins_Empty) {
wrapper_->WaitForRegistrationsInitializedForTest();
// No registration stored yet.
std::set<url::Origin> registered_origins =
GetInstalledRegistrationOrigins(base::nullopt);
EXPECT_EQ(registered_origins.size(), 0UL);
}
// On registration.
TEST_F(ServiceWorkerContextWrapperTest, GetInstalledRegistrationOrigins_One) {
const GURL scope("https://example.com/");
const GURL script("https://example.com/sw.js");
const url::Origin origin = url::Origin::Create(scope.GetOrigin());
wrapper_->WaitForRegistrationsInitializedForTest();
scoped_refptr<ServiceWorkerRegistration> registration =
CreateServiceWorkerRegistrationAndVersion(context(), scope, script,
/*resource_id=*/1);
ASSERT_EQ(StoreRegistration(registration),
blink::ServiceWorkerStatusCode::kOk);
base::RunLoop().RunUntilIdle();
std::set<url::Origin> installed_origins =
GetInstalledRegistrationOrigins(base::nullopt);
ASSERT_EQ(installed_origins.size(), 1UL);
EXPECT_EQ(*installed_origins.begin(), origin);
}
// Two registrations from the same origin.
TEST_F(ServiceWorkerContextWrapperTest,
GetInstalledRegistrationOrigins_SameOrigin) {
const GURL scope1("https://example.com/foo");
const GURL script1("https://example.com/foo/sw.js");
const url::Origin origin = url::Origin::Create(scope1.GetOrigin());
const GURL scope2("https://example.com/bar");
const GURL script2("https://example.com/bar/sw.js");
wrapper_->WaitForRegistrationsInitializedForTest();
scoped_refptr<ServiceWorkerRegistration> registration1 =
CreateServiceWorkerRegistrationAndVersion(context(), scope1, script1,
/*resource_id=*/1);
ASSERT_EQ(StoreRegistration(registration1),
blink::ServiceWorkerStatusCode::kOk);
scoped_refptr<ServiceWorkerRegistration> registration2 =
CreateServiceWorkerRegistrationAndVersion(context(), scope2, script2,
/*resource_id=*/2);
ASSERT_EQ(StoreRegistration(registration2),
blink::ServiceWorkerStatusCode::kOk);
base::RunLoop().RunUntilIdle();
std::set<url::Origin> installed_origins =
GetInstalledRegistrationOrigins(base::nullopt);
ASSERT_EQ(installed_origins.size(), 1UL);
EXPECT_EQ(*installed_origins.begin(), origin);
}
// Two registrations from different origins.
TEST_F(ServiceWorkerContextWrapperTest,
GetInstalledRegistrationOrigins_DifferentOrigin) {
const GURL scope1("https://example1.com/foo");
const GURL script1("https://example1.com/foo/sw.js");
const url::Origin origin1 = url::Origin::Create(scope1.GetOrigin());
const GURL scope2("https://example2.com/bar");
const GURL script2("https://example2.com/bar/sw.js");
const url::Origin origin2 = url::Origin::Create(scope2.GetOrigin());
wrapper_->WaitForRegistrationsInitializedForTest();
scoped_refptr<ServiceWorkerRegistration> registration1 =
CreateServiceWorkerRegistrationAndVersion(context(), scope1, script1,
/*resource_id=*/1);
ASSERT_EQ(StoreRegistration(registration1),
blink::ServiceWorkerStatusCode::kOk);
scoped_refptr<ServiceWorkerRegistration> registration2 =
CreateServiceWorkerRegistrationAndVersion(context(), scope2, script2,
/*resource_id=*/2);
ASSERT_EQ(StoreRegistration(registration2),
blink::ServiceWorkerStatusCode::kOk);
base::RunLoop().RunUntilIdle();
std::set<url::Origin> installed_origins =
GetInstalledRegistrationOrigins(base::nullopt);
ASSERT_EQ(installed_origins.size(), 2UL);
EXPECT_TRUE(base::Contains(installed_origins, origin1));
EXPECT_TRUE(base::Contains(installed_origins, origin2));
}
// One registration, host filter matches it.
TEST_F(ServiceWorkerContextWrapperTest,
GetInstalledRegistrationOrigins_HostFilterMatch) {
const GURL scope("https://example.com/");
const GURL script("https://example.com/sw.js");
const url::Origin origin = url::Origin::Create(scope.GetOrigin());
wrapper_->WaitForRegistrationsInitializedForTest();
scoped_refptr<ServiceWorkerRegistration> registration =
CreateServiceWorkerRegistrationAndVersion(context(), scope, script,
/*resource_id=*/1);
ASSERT_EQ(StoreRegistration(registration),
blink::ServiceWorkerStatusCode::kOk);
base::RunLoop().RunUntilIdle();
std::set<url::Origin> installed_origins =
GetInstalledRegistrationOrigins("example.com");
ASSERT_EQ(installed_origins.size(), 1UL);
EXPECT_EQ(*installed_origins.begin(), origin);
}
// One registration, host filter does not match it.
TEST_F(ServiceWorkerContextWrapperTest,
GetInstalledRegistrationOrigins_HostFilterNoMatch) {
const GURL scope("https://example.com/");
const GURL script("https://example.com/sw.js");
const url::Origin origin = url::Origin::Create(scope.GetOrigin());
wrapper_->WaitForRegistrationsInitializedForTest();
scoped_refptr<ServiceWorkerRegistration> registration =
CreateServiceWorkerRegistrationAndVersion(context(), scope, script,
/*resource_id=*/1);
ASSERT_EQ(StoreRegistration(registration),
blink::ServiceWorkerStatusCode::kOk);
base::RunLoop().RunUntilIdle();
std::set<url::Origin> installed_origins =
GetInstalledRegistrationOrigins("example.test");
EXPECT_EQ(installed_origins.size(), 0UL);
}
// Two registrations, one is deleted, only the other one is returned.
TEST_F(ServiceWorkerContextWrapperTest,
GetInstalledRegistrationOrigins_DeletedRegistration) {
const GURL scope1("https://example1.com/foo");
const GURL script1("https://example1.com/foo/sw.js");
const url::Origin origin1 = url::Origin::Create(scope1.GetOrigin());
const GURL scope2("https://example2.com/bar");
const GURL script2("https://example2.com/bar/sw.js");
const url::Origin origin2 = url::Origin::Create(scope2.GetOrigin());
wrapper_->WaitForRegistrationsInitializedForTest();
scoped_refptr<ServiceWorkerRegistration> registration1 =
CreateServiceWorkerRegistrationAndVersion(context(), scope1, script1,
/*resource_id=*/1);
ASSERT_EQ(StoreRegistration(registration1),
blink::ServiceWorkerStatusCode::kOk);
scoped_refptr<ServiceWorkerRegistration> registration2 =
CreateServiceWorkerRegistrationAndVersion(context(), scope2, script2,
/*resource_id=*/2);
ASSERT_EQ(StoreRegistration(registration2),
blink::ServiceWorkerStatusCode::kOk);
base::RunLoop().RunUntilIdle();
{
std::set<url::Origin> installed_origins =
GetInstalledRegistrationOrigins(base::nullopt);
ASSERT_EQ(installed_origins.size(), 2UL);
EXPECT_TRUE(base::Contains(installed_origins, origin1));
EXPECT_TRUE(base::Contains(installed_origins, origin2));
}
// Delete |registration2|.
ASSERT_EQ(DeleteRegistration(registration2),
blink::ServiceWorkerStatusCode::kOk);
base::RunLoop().RunUntilIdle();
// After |registration2| is deleted, only |origin1| should be returned.
{
std::set<url::Origin> installed_origins =
GetInstalledRegistrationOrigins(base::nullopt);
ASSERT_EQ(installed_origins.size(), 1UL);
EXPECT_EQ(*installed_origins.begin(), origin1);
}
}
} // namespace content
......@@ -22,20 +22,6 @@ using storage::QuotaClient;
namespace content {
namespace {
void ReportOrigins(QuotaClient::GetOriginsCallback callback,
bool restrict_on_host,
const std::string host,
const std::vector<StorageUsageInfo>& usage_info) {
std::set<url::Origin> origins;
for (const StorageUsageInfo& info : usage_info) {
if (restrict_on_host && info.origin.host() != host) {
continue;
}
origins.insert(info.origin);
}
std::move(callback).Run(origins);
}
void ReportToQuotaStatus(QuotaClient::DeletionCallback callback, bool status) {
std::move(callback).Run(status ? blink::mojom::QuotaStatusCode::kOk
: blink::mojom::QuotaStatusCode::kUnknown);
......@@ -46,7 +32,6 @@ void FindUsageForOrigin(QuotaClient::GetUsageCallback callback,
int64_t usage) {
std::move(callback).Run(usage);
}
} // namespace
ServiceWorkerQuotaClient::ServiceWorkerQuotaClient(
......@@ -68,16 +53,14 @@ void ServiceWorkerQuotaClient::GetOriginUsage(const url::Origin& origin,
void ServiceWorkerQuotaClient::GetOriginsForType(StorageType type,
GetOriginsCallback callback) {
DCHECK_EQ(type, StorageType::kTemporary);
context_->GetAllOriginsInfo(
base::BindOnce(&ReportOrigins, std::move(callback), false, ""));
context_->GetInstalledRegistrationOrigins(base::nullopt, std::move(callback));
}
void ServiceWorkerQuotaClient::GetOriginsForHost(StorageType type,
const std::string& host,
GetOriginsCallback callback) {
DCHECK_EQ(type, StorageType::kTemporary);
context_->GetAllOriginsInfo(
base::BindOnce(&ReportOrigins, std::move(callback), true, host));
context_->GetInstalledRegistrationOrigins(host, std::move(callback));
}
void ServiceWorkerQuotaClient::DeleteOriginData(const url::Origin& origin,
......
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