0

[Extensions] Clean up LazyContextId issues.

The CL cleans up issues within LazyContextId and how it's used.

1. LazyContextId currently stores the service worker scope for
instances created for a service worker-based extension. Since we
only ever register the extension's script at the root scope, there's
no need to store this, since it can be inferred from the extension's
ID.

2. ServiceWorkerTaskQueue::SequencedContextId is a std::pair
containing a LazyContextId and a base::UnguessableToken that's
generated when the queue tries to activate the service worker. This
CL changes this to a struct with named fields for clarity. It also
only stores the extension's ID and the BrowserContext from the
LazyContextId, since that's all that's needed.

3. There are places where LazyContextId instances are created and the
type of the instance (for and EventPage-based extension or a service
worker-based extension) is inferred from the constructor used. Since
the scope is no longer saved, there is no longer a need for unique
constructors and the type cannot be inferred. This CL adds static
member functions that are clearly named and create the expected
type of LazyContextId. There is also an option to create one for
an extension where the function determines the type of LazyContextId
based on the extension. This is the safest and clearest way, so many
call sites are migrated to use this static member function.

Bug: None
Change-Id: If40c9a7fa9fa0148bb2d1b0faa7f988a18dfe654
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5050418
Reviewed-by: Fabian Sommer <fabiansommer@chromium.org>
Reviewed-by: Ben Reich <benreich@chromium.org>
Reviewed-by: Trent Apted <tapted@chromium.org>
Auto-Submit: David Bertoni <dbertoni@chromium.org>
Commit-Queue: David Bertoni <dbertoni@chromium.org>
Reviewed-by: Devlin Cronin <rdevlin.cronin@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1236662}
This commit is contained in:
David Bertoni
2023-12-12 23:33:32 +00:00
committed by Chromium LUCI CQ
parent b061739061
commit 9cfed26659
23 changed files with 188 additions and 153 deletions

@ -295,7 +295,9 @@ class PlatformAppPathLauncher
// available, or it might be in the process of being unloaded, in which case
// the lazy background task queue is used to load the extension and then
// call back to us.
const extensions::LazyContextId context_id(context_, extension_id);
const auto context_id =
extensions::LazyContextId::ForExtension(context_, app);
CHECK(context_id.IsForBackgroundPage());
extensions::LazyContextTaskQueue* const queue = context_id.GetTaskQueue();
if (queue->ShouldEnqueueTask(context_, app)) {
queue->AddPendingTask(

@ -245,7 +245,9 @@ void FileBrowserHandlerExecutor::ExecuteFileActionsOnUIThread(
extensions::ExtensionHost* extension_host =
manager->GetBackgroundHostForExtension(extension_->id());
const extensions::LazyContextId context_id(profile_, extension_->id());
const auto context_id =
extensions::LazyContextId::ForExtension(profile_, extension_.get());
CHECK(context_id.IsForBackgroundPage());
extensions::LazyContextTaskQueue* task_queue = context_id.GetTaskQueue();
if (task_queue->ShouldEnqueueTask(profile_, extension_.get())) {

@ -195,8 +195,8 @@ class ExtensionLoadObserver final
}
// Ensure that the extension's background host is active.
const extensions::LazyContextId context_id(browser_context,
extension->id());
const auto context_id =
extensions::LazyContextId::ForExtension(browser_context, extension);
extensions::LazyContextTaskQueue* queue = context_id.GetTaskQueue();
if (!queue->ShouldEnqueueTask(browser_context, extension)) {
// The background host already exists.

@ -175,7 +175,9 @@ void FileBrowserHandlerExecutorFlow::PrepareToLaunch() {
// available, or it might be in the process of being unloaded, in which case
// the lazy background task queue is used to load the extension and then
// call back to us.
const extensions::LazyContextId context_id(profile_, extension_->id());
const auto context_id =
extensions::LazyContextId::ForExtension(profile_, extension_.get());
CHECK(context_id.IsForBackgroundPage());
extensions::LazyContextTaskQueue* task_queue = context_id.GetTaskQueue();
if (task_queue->ShouldEnqueueTask(profile_, extension_.get())) {

@ -19,8 +19,9 @@
#include "extensions/browser/api/offscreen/offscreen_document_manager.h"
#include "extensions/browser/background_script_executor.h"
#include "extensions/browser/extension_util.h"
#include "extensions/browser/lazy_context_id.h"
#include "extensions/browser/lazy_context_task_queue.h"
#include "extensions/browser/offscreen_document_host.h"
#include "extensions/browser/service_worker_task_queue.h"
#include "extensions/browser/test_extension_registry_observer.h"
#include "extensions/common/extension.h"
#include "extensions/common/features/feature_channel.h"
@ -99,8 +100,10 @@ scoped_refptr<const Extension> SetExtensionIncognitoEnabled(
// Wakes up the service worker for the `extension` in the given `profile`.
void WakeUpServiceWorker(const Extension& extension, Profile& profile) {
base::RunLoop run_loop;
ServiceWorkerTaskQueue::Get(&profile)->AddPendingTask(
LazyContextId(&profile, extension.id(), extension.url()),
const auto context_id = LazyContextId::ForExtension(&profile, &extension);
ASSERT_TRUE(context_id.IsForServiceWorker());
context_id.GetTaskQueue()->AddPendingTask(
context_id,
base::BindOnce([](std::unique_ptr<LazyContextTaskQueue::ContextInfo>) {
}).Then(run_loop.QuitWhenIdleClosure()));
run_loop.Run();

@ -73,9 +73,7 @@ void InspectInactiveServiceWorkerBackground(const Extension* extension,
DevToolsOpenedByAction opened_by) {
DCHECK(extension);
DCHECK(BackgroundInfo::IsServiceWorkerBased(extension));
LazyContextId context_id(
profile, extension->id(),
Extension::GetBaseURLFromExtensionId(extension->id()));
const auto context_id = LazyContextId::ForExtension(profile, extension);
context_id.GetTaskQueue()->AddPendingTask(
context_id,
base::BindOnce(&InspectServiceWorkerBackgroundHelper, opened_by));
@ -91,7 +89,7 @@ void InspectBackgroundPage(const Extension* extension,
InspectExtensionHost(
opened_by, std::make_unique<LazyContextTaskQueue::ContextInfo>(host));
} else {
const LazyContextId context_id(profile, extension->id());
const auto context_id = LazyContextId::ForExtension(profile, extension);
context_id.GetTaskQueue()->AddPendingTask(
context_id, base::BindOnce(&InspectExtensionHost, opened_by));
}

@ -2302,8 +2302,8 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerBasedBackgroundTest,
const Extension* extension = LoadExtension(test_dir.UnpackedPath());
ASSERT_TRUE(extension);
ASSERT_EQ(test_extension_id, extension->id());
LazyContextId context_id(browser()->profile(), extension->id(),
extension->url());
LazyContextId context_id =
LazyContextId::ForExtension(browser()->profile(), extension);
// Let the worker start so it rejects 'install' event. This causes the worker
// to stop.
observer.WaitForWorkerStart();

@ -80,11 +80,7 @@ const char kProhibitedByPoliciesError[] =
LazyContextId LazyContextIdFor(content::BrowserContext* browser_context,
const Extension* extension) {
if (BackgroundInfo::HasLazyBackgroundPage(extension))
return LazyContextId(browser_context, extension->id());
DCHECK(BackgroundInfo::IsServiceWorkerBased(extension));
return LazyContextId(browser_context, extension->id(), extension->url());
return LazyContextId::ForExtension(browser_context, extension);
}
const Extension* GetExtensionForNativeAppChannel(

@ -110,15 +110,16 @@ void DispatchOnStartupEventImpl(
return;
}
if (ExtensionsBrowserClient::Get()->IsShuttingDown() ||
!ExtensionsBrowserClient::Get()->IsValidContext(browser_context)) {
return;
}
// Don't send onStartup events to incognito browser contexts.
if (browser_context->IsOffTheRecord()) {
return;
}
if (ExtensionsBrowserClient::Get()->IsShuttingDown() ||
!ExtensionsBrowserClient::Get()->IsValidContext(browser_context)) {
return;
}
ExtensionSystem* system = ExtensionSystem::Get(browser_context);
if (!system) {
return;
@ -132,7 +133,8 @@ void DispatchOnStartupEventImpl(
.GetByID(extension_id);
if (extension && BackgroundInfo::HasPersistentBackgroundPage(extension) &&
first_call) {
const LazyContextId context_id(browser_context, extension_id);
const auto context_id =
LazyContextId::ForExtension(browser_context, extension);
LazyContextTaskQueue* task_queue = context_id.GetTaskQueue();
if (task_queue->ShouldEnqueueTask(browser_context, extension)) {
task_queue->AddPendingTask(
@ -639,9 +641,13 @@ void RuntimeAPI::OnExtensionInstalledAndLoaded(
}
ExtensionFunction::ResponseAction RuntimeGetBackgroundPageFunction::Run() {
if (!BackgroundInfo::HasBackgroundPage(extension())) {
return RespondNow(Error(kNoBackgroundPageError));
}
ExtensionHost* host = ProcessManager::Get(browser_context())
->GetBackgroundHostForExtension(extension_id());
const LazyContextId context_id(browser_context(), extension_id());
const auto context_id =
LazyContextId::ForExtension(browser_context(), extension());
LazyContextTaskQueue* task_queue = context_id.GetTaskQueue();
if (task_queue->ShouldEnqueueTask(browser_context(), extension())) {
task_queue->AddPendingTask(

@ -138,11 +138,12 @@ LazyContextId LazyContextIdForListener(const EventListener* listener,
// TODO(lazyboy): Clean these inconsistencies across different types of event
// listener and their corresponding background types.
if (is_service_worker_based_extension && listener->is_for_service_worker()) {
return LazyContextId(browser_context, listener->extension_id(),
listener->listener_url());
return LazyContextId::ForServiceWorker(browser_context,
listener->extension_id());
}
return LazyContextId(browser_context, listener->extension_id());
return LazyContextId::ForBackgroundPage(browser_context,
listener->extension_id());
}
// A global identifier used to distinguish extension events.

@ -608,7 +608,8 @@ void ExtensionRegistrar::MaybeSpinUpLazyContext(const Extension* extension,
// installed, this will result in a no-op task that's not necessary, since
// this is really only needed for a previously-installed extension. However,
// that cost is minimal, since the worker is already active.
const LazyContextId context_id(browser_context_, extension);
const auto context_id =
LazyContextId::ForExtension(browser_context_, extension);
context_id.GetTaskQueue()->AddPendingTask(context_id, base::DoNothing());
}

@ -205,7 +205,8 @@ void AppViewGuest::CreateWebContents(std::unique_ptr<GuestViewBase> owned_this,
return;
}
const LazyContextId context_id(browser_context(), guest_extension->id());
const auto context_id =
LazyContextId::ForExtension(browser_context(), guest_extension);
LazyContextTaskQueue* queue = context_id.GetTaskQueue();
if (queue->ShouldEnqueueTask(browser_context(), guest_extension)) {
queue->AddPendingTask(

@ -117,10 +117,15 @@ void LazyBackgroundTaskQueue::ProcessPendingTasks(
DCHECK(extension);
if (!ExtensionsBrowserClient::Get()->IsSameContext(browser_context,
browser_context_))
browser_context_)) {
return;
}
const auto key = LazyContextId::ForExtension(browser_context, extension);
if (key.IsForServiceWorker()) {
return;
}
PendingTasksKey key(browser_context, extension->id());
auto map_it = pending_tasks_.find(key);
if (map_it == pending_tasks_.end()) {
if (BackgroundInfo::HasLazyBackgroundPage(extension))
@ -215,7 +220,8 @@ void LazyBackgroundTaskQueue::OnExtensionUnloaded(
void LazyBackgroundTaskQueue::CreateLazyBackgroundHostOnExtensionLoaded(
content::BrowserContext* browser_context,
const Extension* extension) {
PendingTasksKey key(browser_context, extension->id());
const auto key = LazyContextId::ForExtension(browser_context, extension);
CHECK(key.IsForBackgroundPage());
if (!base::Contains(pending_tasks_, key))
return;

@ -74,12 +74,11 @@ class LazyBackgroundTaskQueue : public KeyedService,
FRIEND_TEST_ALL_PREFIXES(LazyBackgroundTaskQueueTest, ProcessPendingTasks);
FRIEND_TEST_ALL_PREFIXES(LazyBackgroundTaskQueueTest,
CreateLazyBackgroundPageOnExtensionLoaded);
using PendingTasksList = std::vector<PendingTask>;
// A map between a LazyContextId and the queue of tasks pending the load of
// its background page.
using PendingTasksKey = LazyContextId;
using PendingTasksList = std::vector<PendingTask>;
using PendingTasksMap =
std::map<PendingTasksKey, std::unique_ptr<PendingTasksList>>;
std::map<LazyContextId, std::unique_ptr<PendingTasksList>>;
// ExtensionHostRegistry::Observer:
void OnExtensionHostCompletedFirstLoad(

@ -155,8 +155,8 @@ TEST_F(LazyBackgroundTaskQueueTest, AddPendingTask) {
// Adding a pending task increases the number of extensions with tasks, but
// doesn't run the task.
const LazyContextId no_background_context_id(browser_context(),
no_background->id());
const auto no_background_context_id =
LazyContextId::ForExtension(browser_context(), no_background.get());
queue.AddPendingTask(
no_background_context_id,
base::BindOnce(&LazyBackgroundTaskQueueTest::RunPendingTask,
@ -177,8 +177,8 @@ TEST_F(LazyBackgroundTaskQueueTest, AddPendingTask) {
// a background host, and if that fails, runs the task immediately.
scoped_refptr<const Extension> lazy_background =
CreateLazyBackgroundExtension();
const LazyContextId lazy_background_context_id(browser_context(),
lazy_background->id());
const auto lazy_background_context_id =
LazyContextId::ForExtension(browser_context(), lazy_background.get());
queue.AddPendingTask(
lazy_background_context_id,
base::BindOnce(&LazyBackgroundTaskQueueTest::RunPendingTask,
@ -201,7 +201,7 @@ TEST_F(LazyBackgroundTaskQueueTest, ProcessPendingTasks) {
// Schedule a task to run.
queue.AddPendingTask(
LazyContextId(browser_context(), extension->id()),
LazyContextId::ForExtension(browser_context(), extension.get()),
base::BindOnce(&LazyBackgroundTaskQueueTest::RunPendingTask,
base::Unretained(this)));
EXPECT_EQ(0, task_run_count());
@ -237,7 +237,7 @@ TEST_F(LazyBackgroundTaskQueueTest, CreateLazyBackgroundPageOnExtensionLoaded) {
EXPECT_EQ(0, process_manager()->create_count());
queue.AddPendingTask(
LazyContextId(browser_context(), lazy_background->id()),
LazyContextId::ForExtension(browser_context(), lazy_background.get()),
base::BindOnce(&LazyBackgroundTaskQueueTest::RunPendingTask,
base::Unretained(this)));
EXPECT_EQ(1u, queue.pending_tasks_.size());

@ -12,30 +12,24 @@
namespace extensions {
LazyContextId::LazyContextId(content::BrowserContext* context,
LazyContextId::LazyContextId(Type type,
content::BrowserContext* context,
const ExtensionId& extension_id)
: type_(Type::kEventPage), context_(context), extension_id_(extension_id) {}
LazyContextId::LazyContextId(content::BrowserContext* context,
const ExtensionId& extension_id,
const GURL& service_worker_scope)
: type_(Type::kServiceWorker),
context_(context),
extension_id_(extension_id),
service_worker_scope_(service_worker_scope) {}
: type_(type), context_(context), extension_id_(extension_id) {}
LazyContextId::LazyContextId(content::BrowserContext* context,
const Extension* extension)
: context_(context), extension_id_(extension->id()) {
if (BackgroundInfo::HasLazyBackgroundPage(extension)) {
type_ = Type::kEventPage;
} else {
// TODO(crbug.com/773103): This currently assumes all workers are
// registered in the '/' scope.
DCHECK(BackgroundInfo::IsServiceWorkerBased(extension));
if (BackgroundInfo::IsServiceWorkerBased(extension)) {
type_ = Type::kServiceWorker;
service_worker_scope_ =
Extension::GetBaseURLFromExtensionId(extension->id());
} else if (BackgroundInfo::HasBackgroundPage(extension)) {
// Packaged apps and extensions with persistent background and event pages
// all use the same task queue.
type_ = Type::kBackgroundPage;
} else {
// There are tests where a LazyContextId is constructed for an extension
// without a background page or service worker, so this is a fallback.
type_ = Type::kNone;
}
}

@ -9,7 +9,6 @@
#include "base/memory/raw_ptr.h"
#include "extensions/common/extension_id.h"
#include "url/gurl.h"
namespace content {
class BrowserContext;
@ -21,22 +20,20 @@ class LazyContextTaskQueue;
class LazyContextId {
public:
enum class Type {
kEventPage,
kServiceWorker,
};
static LazyContextId ForBackgroundPage(content::BrowserContext* context,
const ExtensionId& extension_id) {
return LazyContextId(Type::kBackgroundPage, context, extension_id);
}
// An event page (lazy background) context.
LazyContextId(content::BrowserContext* context,
const ExtensionId& extension_id);
static LazyContextId ForServiceWorker(content::BrowserContext* context,
const ExtensionId& extension_id) {
return LazyContextId(Type::kServiceWorker, context, extension_id);
}
// An extension service worker context.
LazyContextId(content::BrowserContext* context,
const ExtensionId& extension_id,
const GURL& service_worker_scope);
// The context is derived from the extension.
LazyContextId(content::BrowserContext* context, const Extension* extension);
static LazyContextId ForExtension(content::BrowserContext* context,
const Extension* extension) {
return LazyContextId(context, extension);
}
// Copy and move constructors.
LazyContextId(const LazyContextId& other) = default;
@ -45,44 +42,52 @@ class LazyContextId {
LazyContextId& operator=(const LazyContextId&) noexcept = default;
LazyContextId& operator=(LazyContextId&&) noexcept = default;
bool is_for_event_page() const { return type_ == Type::kEventPage; }
bool is_for_service_worker() const { return type_ == Type::kServiceWorker; }
bool IsForBackgroundPage() const { return type_ == Type::kBackgroundPage; }
bool IsForServiceWorker() const { return type_ == Type::kServiceWorker; }
content::BrowserContext* browser_context() const { return context_; }
void set_browser_context(content::BrowserContext* context) {
context_ = context;
}
const ExtensionId& extension_id() const { return extension_id_; }
const GURL& service_worker_scope() const {
DCHECK(is_for_service_worker());
return service_worker_scope_;
}
LazyContextTaskQueue* GetTaskQueue() const;
bool operator<(const LazyContextId& rhs) const {
return std::tie(type_, context_, extension_id_, service_worker_scope_) <
std::tie(rhs.type_, rhs.context_, rhs.extension_id_,
rhs.service_worker_scope_);
}
bool operator==(const LazyContextId& rhs) const {
return std::tie(type_, context_, extension_id_, service_worker_scope_) ==
std::tie(rhs.type_, rhs.context_, rhs.extension_id_,
rhs.service_worker_scope_);
}
bool operator!=(const LazyContextId& rhs) const { return !(*this == rhs); }
private:
enum class Type {
kNone,
kBackgroundPage,
kServiceWorker,
};
friend bool operator<(const LazyContextId& lhs, const LazyContextId& rhs);
friend bool operator==(const LazyContextId& lhs, const LazyContextId& rhs);
// An event page or service worker based on the type.
LazyContextId(Type type,
content::BrowserContext* context,
const ExtensionId& extension_id);
// The type is derived from the extension.
LazyContextId(content::BrowserContext* context, const Extension* extension);
Type type_;
raw_ptr<content::BrowserContext, DanglingUntriaged> context_;
ExtensionId extension_id_;
GURL service_worker_scope_;
};
inline bool operator<(const LazyContextId& lhs, const LazyContextId& rhs) {
return std::tie(lhs.type_, lhs.context_, lhs.extension_id_) <
std::tie(rhs.type_, rhs.context_, rhs.extension_id_);
}
inline bool operator==(const LazyContextId& lhs, const LazyContextId& rhs) {
return std::tie(lhs.type_, lhs.context_, lhs.extension_id_) ==
std::tie(rhs.type_, rhs.context_, rhs.extension_id_);
}
inline bool operator!=(const LazyContextId& lhs, const LazyContextId& rhs) {
return !(lhs == rhs);
}
} // namespace extensions
#endif // EXTENSIONS_BROWSER_LAZY_CONTEXT_ID_H_

@ -448,7 +448,9 @@ bool ProcessManager::WakeEventPage(const std::string& extension_id,
// The extension is already awake.
return false;
}
const LazyContextId context_id(browser_context_, extension_id);
const auto context_id =
LazyContextId::ForBackgroundPage(browser_context_, extension_id);
context_id.GetTaskQueue()->AddPendingTask(
context_id,
base::BindOnce(&PropagateExtensionWakeResult, std::move(callback)));

@ -152,8 +152,8 @@ void ServiceWorkerTaskQueue::DidStartWorkerForScope(
int process_id,
int thread_id) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
const ExtensionId& extension_id = context_id.first.extension_id();
const base::UnguessableToken& activation_token = context_id.second;
const ExtensionId& extension_id = context_id.extension_id;
const base::UnguessableToken& activation_token = context_id.token;
if (!IsCurrentActivation(extension_id, activation_token)) {
// Extension run with |activation_token| was already deactivated.
// TODO(lazyboy): Add a DCHECK that the worker in question is actually
@ -210,8 +210,7 @@ void ServiceWorkerTaskQueue::DidStartWorkerFail(
base::Time start_time,
blink::ServiceWorkerStatusCode status_code) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!IsCurrentActivation(context_id.first.extension_id(),
context_id.second)) {
if (!IsCurrentActivation(context_id.extension_id, context_id.token)) {
// This can happen is when the registration got unregistered right before we
// tried to start it. See crbug.com/999027 for details.
DCHECK(!GetWorkerState(context_id));
@ -228,7 +227,7 @@ void ServiceWorkerTaskQueue::DidStartWorkerFail(
WorkerState* worker_state = GetWorkerState(context_id);
DCHECK(worker_state);
if (g_test_observer) {
g_test_observer->DidStartWorkerFail(context_id.first.extension_id(),
g_test_observer->DidStartWorkerFail(context_id.extension_id,
worker_state->pending_tasks_.size(),
status_code);
}
@ -237,12 +236,12 @@ void ServiceWorkerTaskQueue::DidStartWorkerFail(
// perma-broken state after this as the registration wouldn't be stored if
// this happens.
LOG(ERROR)
<< "DidStartWorkerFail " << context_id.first.extension_id() << ": "
<< "DidStartWorkerFail " << context_id.extension_id << ": "
<< static_cast<std::underlying_type_t<blink::ServiceWorkerStatusCode>>(
status_code);
// If there was a pending registration for this scope, erase it.
pending_registrations_.erase(context_id.first.service_worker_scope());
// If there was a pending registration for this extension, erase it.
pending_registrations_.erase(context_id.extension_id);
}
void ServiceWorkerTaskQueue::DidInitializeServiceWorkerContext(
@ -290,9 +289,8 @@ void ServiceWorkerTaskQueue::DidStartServiceWorkerContext(
return;
}
SequencedContextId context_id(
LazyContextId(browser_context_, extension_id, service_worker_scope),
activation_token);
const SequencedContextId context_id = {extension_id, browser_context_,
activation_token};
const WorkerId worker_id = {extension_id, render_process_id,
service_worker_version_id, thread_id};
@ -332,9 +330,8 @@ void ServiceWorkerTaskQueue::DidStopServiceWorkerContext(
const WorkerId worker_id = {extension_id, render_process_id,
service_worker_version_id, thread_id};
ProcessManager::Get(browser_context_)->UnregisterServiceWorker(worker_id);
SequencedContextId context_id(
LazyContextId(browser_context_, extension_id, service_worker_scope),
activation_token);
const SequencedContextId context_id = {extension_id, browser_context_,
activation_token};
WorkerState* worker_state = GetWorkerState(context_id);
DCHECK(worker_state);
@ -366,7 +363,7 @@ bool ServiceWorkerTaskQueue::ShouldEnqueueTask(BrowserContext* context,
void ServiceWorkerTaskQueue::AddPendingTask(
const LazyContextId& lazy_context_id,
PendingTask task) {
DCHECK(lazy_context_id.is_for_service_worker());
DCHECK(lazy_context_id.IsForServiceWorker());
// TODO(lazyboy): Do we need to handle incognito context?
@ -375,7 +372,9 @@ void ServiceWorkerTaskQueue::AddPendingTask(
DCHECK(activation_token)
<< "Trying to add pending task to an inactive extension: "
<< lazy_context_id.extension_id();
const SequencedContextId context_id(lazy_context_id, *activation_token);
const SequencedContextId context_id = {lazy_context_id.extension_id(),
lazy_context_id.browser_context(),
*activation_token};
WorkerState* worker_state = GetWorkerState(context_id);
DCHECK(worker_state);
auto& tasks = worker_state->pending_tasks_;
@ -400,9 +399,8 @@ void ServiceWorkerTaskQueue::ActivateExtension(const Extension* extension) {
const ExtensionId extension_id = extension->id();
base::UnguessableToken activation_token = base::UnguessableToken::Create();
activation_tokens_[extension_id] = activation_token;
SequencedContextId context_id(
LazyContextId(browser_context_, extension_id, extension->url()),
activation_token);
const SequencedContextId context_id = {extension_id, browser_context_,
activation_token};
DCHECK(!base::Contains(worker_state_map_, context_id));
WorkerState& worker_state = worker_state_map_[context_id];
@ -477,9 +475,8 @@ void ServiceWorkerTaskQueue::DeactivateExtension(const Extension* extension) {
}
activation_tokens_.erase(extension_id);
SequencedContextId context_id(
LazyContextId(browser_context_, extension_id, extension->url()),
*activation_token);
const SequencedContextId context_id = {extension_id, browser_context_,
*activation_token};
WorkerState* worker_state = GetWorkerState(context_id);
DCHECK(worker_state);
// TODO(lazyboy): Run orphaned tasks with nullptr ContextInfo.
@ -488,7 +485,7 @@ void ServiceWorkerTaskQueue::DeactivateExtension(const Extension* extension) {
// Erase any registrations that might still have been pending being fully
// stored.
pending_registrations_.erase(extension->url());
pending_registrations_.erase(extension_id);
content::ServiceWorkerContext* service_worker_context =
GetServiceWorkerContext(extension->id());
@ -510,19 +507,18 @@ void ServiceWorkerTaskQueue::DeactivateExtension(const Extension* extension) {
void ServiceWorkerTaskQueue::RunTasksAfterStartWorker(
const SequencedContextId& context_id) {
DCHECK(context_id.first.is_for_service_worker());
const LazyContextId& lazy_context_id = context_id.first;
if (lazy_context_id.browser_context() != browser_context_)
if (context_id.browser_context != browser_context_) {
return;
}
WorkerState* worker_state = GetWorkerState(context_id);
DCHECK_NE(BrowserState::kStarted, worker_state->browser_state_);
content::ServiceWorkerContext* service_worker_context =
GetServiceWorkerContext(lazy_context_id.extension_id());
GetServiceWorkerContext(context_id.extension_id);
const GURL& scope = context_id.first.service_worker_scope();
const GURL scope =
Extension::GetServiceWorkerScopeFromExtensionId(context_id.extension_id);
service_worker_context->StartWorkerForScope(
scope, blink::StorageKey::CreateFirstParty(url::Origin::Create(scope)),
base::BindOnce(&ServiceWorkerTaskQueue::DidStartWorkerForScope,
@ -538,14 +534,14 @@ void ServiceWorkerTaskQueue::DidRegisterServiceWorker(
base::Time start_time,
blink::ServiceWorkerStatusCode status_code) {
ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_);
const ExtensionId& extension_id = context_id.first.extension_id();
const ExtensionId& extension_id = context_id.extension_id;
DCHECK(registry);
const Extension* extension =
registry->enabled_extensions().GetByID(extension_id);
if (!extension) {
return;
}
if (!IsCurrentActivation(extension_id, context_id.second)) {
if (!IsCurrentActivation(extension_id, context_id.token)) {
return;
}
@ -580,7 +576,7 @@ void ServiceWorkerTaskQueue::DidRegisterServiceWorker(
base::Time::Now() - start_time);
worker_state->registration_state_ = RegistrationState::kRegistered;
pending_registrations_.emplace(extension->url(),
pending_registrations_.emplace(extension->id(),
*GetCurrentActivationToken(extension->id()));
if (worker_state->has_pending_tasks()) {
@ -674,10 +670,11 @@ void ServiceWorkerTaskQueue::RunPendingTasksIfWorkerReady(
const auto& worker_id = *worker_state->worker_id_;
for (auto& task : tasks) {
auto context_info = std::make_unique<LazyContextTaskQueue::ContextInfo>(
context_id.first.extension_id(),
context_id.extension_id,
content::RenderProcessHost::FromID(worker_id.render_process_id),
worker_id.version_id, worker_id.thread_id,
context_id.first.service_worker_scope());
Extension::GetServiceWorkerScopeFromExtensionId(
context_id.extension_id));
std::move(task).Run(std::move(context_info));
}
}
@ -700,7 +697,8 @@ ServiceWorkerTaskQueue::GetCurrentActivationToken(
void ServiceWorkerTaskQueue::OnRegistrationStored(int64_t registration_id,
const GURL& scope) {
auto iter = pending_registrations_.find(scope);
const std::string extension_id = scope.host();
auto iter = pending_registrations_.find(extension_id);
if (iter == pending_registrations_.end()) {
return;
}
@ -713,7 +711,6 @@ void ServiceWorkerTaskQueue::OnRegistrationStored(int64_t registration_id,
base::UnguessableToken activation_token = iter->second;
pending_registrations_.erase(iter);
std::string extension_id = scope.host();
ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_);
const Extension* extension =
registry->enabled_extensions().GetByID(extension_id);
@ -764,7 +761,9 @@ size_t ServiceWorkerTaskQueue::GetNumPendingTasksForTest(
if (!activation_token) {
return 0u;
}
const SequencedContextId context_id(lazy_context_id, *activation_token);
const SequencedContextId context_id = {lazy_context_id.extension_id(),
lazy_context_id.browser_context(),
*activation_token};
WorkerState* worker_state = GetWorkerState(context_id);
return worker_state ? worker_state->pending_tasks_.size() : 0u;
}
@ -822,7 +821,7 @@ void ServiceWorkerTaskQueue::DidVerifyRegistration(
// We expected a SW registration (as ExtensionPrefs said so), but there isn't
// one. Re-register SW script if the extension is still installed (it's
// possible it was uninstalled while we were checking).
const ExtensionId& extension_id = context_id.first.extension_id();
const ExtensionId& extension_id = context_id.extension_id;
ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_);
DCHECK(registry);
const Extension* extension =

@ -186,7 +186,16 @@ class ServiceWorkerTaskQueue : public KeyedService,
size_t GetNumPendingTasksForTest(const LazyContextId& lazy_context_id);
private:
using SequencedContextId = std::pair<LazyContextId, base::UnguessableToken>;
struct SequencedContextId {
ExtensionId extension_id;
raw_ptr<content::BrowserContext> browser_context;
base::UnguessableToken token;
bool operator<(const SequencedContextId& rhs) const {
return std::tie(extension_id, browser_context, token) <
std::tie(rhs.extension_id, rhs.browser_context, rhs.token);
}
};
class WorkerState;
@ -280,10 +289,9 @@ class ServiceWorkerTaskQueue : public KeyedService,
// succeeded in the first step (triggering `DidRegisterServiceWorker`), but
// have not yet been stored. They are cleared out (and the registration state
// is stored) in response to `OnRegistrationStored`.
// The key is the service worker scope (which is the associated extension's
// base URL), and the value is the activation token expected for that
// registration.
std::map<GURL, base::UnguessableToken> pending_registrations_;
// The key is the extension's ID and the value is the activation token
// expected for that registration.
std::map<ExtensionId, base::UnguessableToken> pending_registrations_;
base::WeakPtrFactory<ServiceWorkerTaskQueue> weak_factory_{this};
};

@ -122,12 +122,16 @@ void DoTaskQueueFunction(content::BrowserContext* browser_context,
LazyContextTaskQueue* GetTaskQueueForLazyContextId(
const LazyContextId& context_id) {
if (context_id.is_for_event_page())
if (context_id.IsForBackgroundPage()) {
return LazyBackgroundTaskQueue::Get(context_id.browser_context());
}
DCHECK(context_id.is_for_service_worker());
return GetServiceWorkerTaskQueueForExtensionId(context_id.browser_context(),
context_id.extension_id());
if (context_id.IsForServiceWorker()) {
return GetServiceWorkerTaskQueueForExtensionId(context_id.browser_context(),
context_id.extension_id());
}
return nullptr;
}
void ActivateTaskQueueForExtension(content::BrowserContext* browser_context,

@ -220,6 +220,12 @@ class Extension final : public base::RefCountedThreadSafe<Extension> {
// Returns the base extension url for a given |extension_id|.
static GURL GetBaseURLFromExtensionId(const ExtensionId& extension_id);
// Returns for scope for the extension's service worker.
static GURL GetServiceWorkerScopeFromExtensionId(
const ExtensionId& extension_id) {
return GetBaseURLFromExtensionId(extension_id);
}
// Returns the extension origin for a given |extension_id|.
static url::Origin CreateOriginFromExtensionId(
const ExtensionId& extension_id);

@ -11,8 +11,8 @@
#include "extensions/browser/extension_host.h"
#include "extensions/browser/extension_host_test_helper.h"
#include "extensions/browser/lazy_context_id.h"
#include "extensions/browser/lazy_context_task_queue.h"
#include "extensions/browser/process_manager.h"
#include "extensions/browser/service_worker_task_queue.h"
#include "extensions/common/manifest_handlers/background_info.h"
#include "extensions/common/manifest_handlers/incognito_info.h"
#include "extensions/common/mojom/view_type.mojom.h"
@ -83,10 +83,10 @@ void ExtensionBackgroundPageWaiter::WaitForBackgroundWorkerInitialized() {
[&run_loop](std::unique_ptr<LazyContextTaskQueue::ContextInfo>) {
run_loop.QuitWhenIdle();
};
ServiceWorkerTaskQueue::Get(browser_context_)
->AddPendingTask(
LazyContextId(browser_context_, extension_->id(), extension_->url()),
base::BindLambdaForTesting(quit_loop_adapter));
const auto context_id =
LazyContextId::ForExtension(browser_context_, extension_.get());
context_id.GetTaskQueue()->AddPendingTask(
context_id, base::BindLambdaForTesting(quit_loop_adapter));
run_loop.Run();
}