Skip to content

Commit

Permalink
fileSystemProvider: service worker lifetime manager
Browse files Browse the repository at this point in the history
Add a keyed service to track initiation and completion of FSP requests
and increment/decrement keepalive count of a service worker that has an
outstanding request.

Bug: b:255520330, b:249182641
Change-Id: I25f924da1e89505f52405e0d94288fe88d0e1d35
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4105401
Reviewed-by: Luciano Pacheco <lucmult@chromium.org>
Commit-Queue: Alexander Bolodurin <alexbn@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1084821}
  • Loading branch information
Alexander Bolodurin authored and Chromium LUCI CQ committed Dec 19, 2022
1 parent b21ea70 commit 7d7bfdc
Show file tree
Hide file tree
Showing 5 changed files with 513 additions and 0 deletions.
2 changes: 2 additions & 0 deletions chrome/browser/BUILD.gn
Expand Up @@ -5404,6 +5404,8 @@ static_library("browser") {
"chromeos/extensions/file_system_provider/file_system_provider_api.h",
"chromeos/extensions/file_system_provider/provider_function.cc",
"chromeos/extensions/file_system_provider/provider_function.h",
"chromeos/extensions/file_system_provider/service_worker_lifetime_manager.cc",
"chromeos/extensions/file_system_provider/service_worker_lifetime_manager.h",
"chromeos/extensions/info_private_api.cc",
"chromeos/extensions/info_private_api.h",
"chromeos/extensions/login_screen/login/cleanup/cleanup_manager_lacros.cc",
Expand Down
3 changes: 3 additions & 0 deletions chrome/browser/chromeos/BUILD.gn
Expand Up @@ -784,6 +784,8 @@ source_set("chromeos") {
"extensions/file_system_provider/file_system_provider_api.h",
"extensions/file_system_provider/provider_function.cc",
"extensions/file_system_provider/provider_function.h",
"extensions/file_system_provider/service_worker_lifetime_manager.cc",
"extensions/file_system_provider/service_worker_lifetime_manager.h",
"extensions/login_screen/login/cleanup/cleanup_manager_ash.cc",
"extensions/login_screen/login/cleanup/cleanup_manager_ash.h",
"extensions/login_screen/login/cleanup/clipboard_cleanup_handler.cc",
Expand Down Expand Up @@ -953,6 +955,7 @@ source_set("unit_tests") {
"app_mode/app_session_unittest.cc",
"app_mode/chrome_kiosk_app_launcher_unittest.cc",
"app_mode/kiosk_app_service_launcher_unittest.cc",
"extensions/file_system_provider/service_worker_lifetime_manager_unittest.cc",
"extensions/login_screen/login/cleanup/cleanup_manager_unittest.cc",
"extensions/login_screen/login/cleanup/extension_cleanup_handler_unittest.cc",
"extensions/login_screen/login/cleanup/lacros_cleanup_handler_unittest.cc",
Expand Down
@@ -0,0 +1,133 @@
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/extensions/file_system_provider/service_worker_lifetime_manager.h"

#include <tuple>
#include <utility>

#include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/process_manager.h"
#include "extensions/browser/process_manager_factory.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_database.mojom-forward.h"

namespace extensions::file_system_provider {

bool RequestKey::operator<(const RequestKey& other) const {
return std::tie(extension_id, file_system_id, request_id) <
std::tie(other.extension_id, other.file_system_id, other.request_id);
}

ServiceWorkerLifetimeManager::ServiceWorkerLifetimeManager(
content::BrowserContext* context)
// Context can be null in tests.
: process_manager_(context ? extensions::ProcessManager::Get(context)
: nullptr) {}

ServiceWorkerLifetimeManager::~ServiceWorkerLifetimeManager() = default;

ServiceWorkerLifetimeManager* ServiceWorkerLifetimeManager::Get(
content::BrowserContext* context) {
return ServiceWorkerLifetimeManagerFactory::GetForBrowserContext(context);
}

void ServiceWorkerLifetimeManager::StartRequest(const RequestKey& key) {
DCHECK(!base::Contains(requests_, key));
requests_[key] = {};
}

void ServiceWorkerLifetimeManager::FinishRequest(
const RequestKey& request_key) {
auto it = requests_.find(request_key);
if (it == requests_.end()) {
return;
}
std::set<KeepaliveKey> keepalive_keys = std::move(it->second);
requests_.erase(it);
for (const KeepaliveKey& keepalive_key : keepalive_keys) {
DecrementKeepalive(keepalive_key);
}
}

void ServiceWorkerLifetimeManager::RequestDispatched(
const RequestKey& key,
const EventTarget& target) {
if (target.service_worker_version_id ==
blink::mojom::kInvalidServiceWorkerVersionId) {
return;
}
auto it = requests_.find(key);
if (it == requests_.end()) {
return;
}
std::set<KeepaliveKey>& keepalive_keys = it->second;
WorkerId worker_id{
target.extension_id,
target.render_process_id,
target.service_worker_version_id,
target.worker_thread_id,
};
std::string uuid = IncrementKeepalive(worker_id);
keepalive_keys.insert(KeepaliveKey{worker_id, uuid});
}

void ServiceWorkerLifetimeManager::Shutdown() {
for (const auto& [_, keys] : requests_) {
for (const KeepaliveKey& key : keys) {
DecrementKeepalive(key);
}
}
}

bool ServiceWorkerLifetimeManager::KeepaliveKey::operator<(
const KeepaliveKey& other) const {
return std::tie(worker_id, request_uuid) <
std::tie(other.worker_id, other.request_uuid);
}

std::string ServiceWorkerLifetimeManager::IncrementKeepalive(
const WorkerId& worker_id) {
return process_manager_->IncrementServiceWorkerKeepaliveCount(
worker_id,
content::ServiceWorkerExternalRequestTimeoutType::kDoesNotTimeout,
extensions::Activity::Type::EVENT, /*extra_data=*/"");
}

void ServiceWorkerLifetimeManager::DecrementKeepalive(const KeepaliveKey& key) {
process_manager_->DecrementServiceWorkerKeepaliveCount(
key.worker_id, key.request_uuid, extensions::Activity::Type::EVENT,
/*extra_data=*/"");
}

// static
ServiceWorkerLifetimeManager*
ServiceWorkerLifetimeManagerFactory::GetForBrowserContext(
content::BrowserContext* context) {
return static_cast<ServiceWorkerLifetimeManager*>(
GetInstance()->GetServiceForBrowserContext(context, true));
}

// static
ServiceWorkerLifetimeManagerFactory*
ServiceWorkerLifetimeManagerFactory::GetInstance() {
return base::Singleton<ServiceWorkerLifetimeManagerFactory>::get();
}

ServiceWorkerLifetimeManagerFactory::ServiceWorkerLifetimeManagerFactory()
: BrowserContextKeyedServiceFactory(
"ServiceWorkerLifetimeManagerFactory",
BrowserContextDependencyManager::GetInstance()) {
DependsOn(extensions::ProcessManagerFactory::GetInstance());
}

ServiceWorkerLifetimeManagerFactory::~ServiceWorkerLifetimeManagerFactory() =
default;

KeyedService* ServiceWorkerLifetimeManagerFactory::BuildServiceInstanceFor(
content::BrowserContext* context) const {
return new ServiceWorkerLifetimeManager(context);
}

} // namespace extensions::file_system_provider
@@ -0,0 +1,120 @@
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_SYSTEM_PROVIDER_SERVICE_WORKER_LIFETIME_MANAGER_H_
#define CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_SYSTEM_PROVIDER_SERVICE_WORKER_LIFETIME_MANAGER_H_

#include <map>
#include <set>
#include <string>

#include "base/memory/singleton.h"
#include "components/keyed_service/content/browser_context_keyed_service_factory.h"
#include "components/keyed_service/core/keyed_service.h"
#include "extensions/browser/service_worker/worker_id.h"
#include "extensions/common/extension_id.h"

namespace content {
class BrowserContext;
}

namespace extensions {

class ProcessManager;
struct EventTarget;

namespace file_system_provider {

// Identifies a unique fileSystemProvider request: request ID sequence of
// integers tracked per a filesystem instance, or per provider (extension) for
// requests that aren't specific to a filesystem instance.
struct RequestKey {
extensions::ExtensionId extension_id;
std::string file_system_id;
int request_id;

bool operator<(const RequestKey& other) const;
};

// Tracks fileSystemProvider requests that have been dispatched to service
// workers but not replied to yet, and keeps service workers alive while there
// are requests in progress.
class ServiceWorkerLifetimeManager : public KeyedService {
public:
ServiceWorkerLifetimeManager(const ServiceWorkerLifetimeManager&) = delete;
ServiceWorkerLifetimeManager& operator=(const ServiceWorkerLifetimeManager&) =
delete;
~ServiceWorkerLifetimeManager() override;

static ServiceWorkerLifetimeManager* Get(content::BrowserContext*);

// Signals that a request has been sent to a fileSystemProvider. Called when
// the request is about to be dispatched (the actual targets that received the
// request aren't known yet).
void StartRequest(const RequestKey&);
// Signals that a request previously sent to a fileSystemProvider has
// finished. Called either when a request has been replied to (the first
// response finishes the request), or is cancelled, due to timeout or being
// aborted.
void FinishRequest(const RequestKey&);
// Signals that a request has been dispatched to a service worker with
// registered fileSystemProvider listeners. Called for each service worker the
// request has been dispatched to.
void RequestDispatched(const RequestKey&, const EventTarget&);
// KeyedService:
void Shutdown() override;

protected:
struct KeepaliveKey {
WorkerId worker_id;
std::string request_uuid;

bool operator==(const KeepaliveKey& other) const;
bool operator<(const KeepaliveKey& other) const;
};

explicit ServiceWorkerLifetimeManager(content::BrowserContext*);

// Virtual for tests.
virtual std::string IncrementKeepalive(const WorkerId&);
virtual void DecrementKeepalive(const KeepaliveKey&);

private:
friend class ServiceWorkerLifetimeManagerFactory;
FRIEND_TEST_ALL_PREFIXES(ServiceWorkerLifetimeManagerTest,
TestDispatchMultipleEvents);

raw_ptr<ProcessManager> process_manager_;
std::map<RequestKey, std::set<KeepaliveKey>> requests_;
};

// KeyedService factory for ServiceWorkerLifetimeManager.
class ServiceWorkerLifetimeManagerFactory
: public BrowserContextKeyedServiceFactory {
public:
ServiceWorkerLifetimeManagerFactory(
const ServiceWorkerLifetimeManagerFactory&) = delete;
ServiceWorkerLifetimeManagerFactory& operator=(
const ServiceWorkerLifetimeManagerFactory&) = delete;

static ServiceWorkerLifetimeManager* GetForBrowserContext(
content::BrowserContext*);
static ServiceWorkerLifetimeManagerFactory* GetInstance();

private:
friend struct base::DefaultSingletonTraits<
ServiceWorkerLifetimeManagerFactory>;

ServiceWorkerLifetimeManagerFactory();
~ServiceWorkerLifetimeManagerFactory() override;

// BrowserContextKeyedServiceFactory:
KeyedService* BuildServiceInstanceFor(
content::BrowserContext* context) const override;
};

} // namespace file_system_provider
} // namespace extensions

#endif // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_SYSTEM_PROVIDER_SERVICE_WORKER_LIFETIME_MANAGER_H_

0 comments on commit 7d7bfdc

Please sign in to comment.