summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIstiaque Ahmed <lazyboy@chromium.org>2019-12-31 00:32:49 +0000
committerMichal Klocek <michal.klocek@qt.io>2020-04-22 18:10:21 +0000
commit5a7809f25c4e10895eb166a8e9d6301c37aea1cb (patch)
treea45e03835b4c59d2bbb16ce0056afadcd141eebb
parent6f916658d9ff036f97fa21e287b543a7acd90e00 (diff)
downloadqtwebengine-chromium-5a7809f25c4e10895eb166a8e9d6301c37aea1cb.tar.gz
[Backport] CVE-2020-6454 1/2
Extension SW: make renderer aware of ActivationSequence. This CL sends ActivationSequence info on extension load (through ExtensionMsg_Load IPC) to renderers. Therefore, worker notifications from renderer now carry the sequence back to browser process / ServiceWorkerTaskQueue. This makes renderer notifications deterministic-ally map-able to an extension load sequence, as opposed to guessing an ActivationSequence (esp. when an extension is deactivated and then activated). This CL also drops the knowledge of a WorkerState upon extension deactivation as we can safely ignore expired renderer notifications arriving after that. RendererExtensionRegistry stores ActivationSequences for all worker based extensions. Bug: 1022247, 1003244 Change-Id: I2e8d99b16f2e611dda02b1f9c62b90d8325b7ff8 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
-rw-r--r--chromium/extensions/browser/extension_registrar.cc8
-rw-r--r--chromium/extensions/browser/extension_service_worker_message_filter.cc6
-rw-r--r--chromium/extensions/browser/extension_service_worker_message_filter.h2
-rw-r--r--chromium/extensions/browser/renderer_startup_helper.cc21
-rw-r--r--chromium/extensions/browser/service_worker/worker_id.cc4
-rw-r--r--chromium/extensions/browser/service_worker/worker_id.h1
-rw-r--r--chromium/extensions/browser/service_worker_task_queue.cc291
-rw-r--r--chromium/extensions/browser/service_worker_task_queue.h50
-rw-r--r--chromium/extensions/common/extension_messages.cc8
-rw-r--r--chromium/extensions/common/extension_messages.h13
-rw-r--r--chromium/extensions/common/extension_messages_unittest.cc2
-rw-r--r--chromium/extensions/renderer/dispatcher.cc9
-rw-r--r--chromium/extensions/renderer/renderer_extension_registry.cc21
-rw-r--r--chromium/extensions/renderer/renderer_extension_registry.h16
-rw-r--r--chromium/extensions/renderer/service_worker_data.cc2
-rw-r--r--chromium/extensions/renderer/service_worker_data.h3
-rw-r--r--chromium/extensions/renderer/worker_thread_dispatcher.cc14
-rw-r--r--chromium/extensions/renderer/worker_thread_dispatcher.h1
18 files changed, 291 insertions, 181 deletions
diff --git a/chromium/extensions/browser/extension_registrar.cc b/chromium/extensions/browser/extension_registrar.cc
index cd41cfda08f..55a5cbae2a9 100644
--- a/chromium/extensions/browser/extension_registrar.cc
+++ b/chromium/extensions/browser/extension_registrar.cc
@@ -434,13 +434,17 @@ void ExtensionRegistrar::ActivateExtension(const Extension* extension,
base::Bind(&ExtensionRegistrar::OnExtensionRegisteredWithRequestContexts,
weak_factory_.GetWeakPtr(), WrapRefCounted(extension)));
- renderer_helper_->OnExtensionLoaded(*extension);
-
+ // Activate the extension before calling
+ // RendererStartupHelper::OnExtensionLoaded() below, so that we have
+ // activation information ready while we send ExtensionMsg_Load IPC.
+ //
// TODO(lazyboy): We should move all logic that is required to start up an
// extension to a separate class, instead of calling adhoc methods like
// service worker ones below.
ActivateTaskQueueForExtension(browser_context_, extension);
+ renderer_helper_->OnExtensionLoaded(*extension);
+
// Tell subsystems that use the ExtensionRegistryObserver::OnExtensionLoaded
// about the new extension.
//
diff --git a/chromium/extensions/browser/extension_service_worker_message_filter.cc b/chromium/extensions/browser/extension_service_worker_message_filter.cc
index 33f883da4da..cb246ae3e1e 100644
--- a/chromium/extensions/browser/extension_service_worker_message_filter.cc
+++ b/chromium/extensions/browser/extension_service_worker_message_filter.cc
@@ -146,6 +146,7 @@ void ExtensionServiceWorkerMessageFilter::OnDidInitializeServiceWorkerContext(
void ExtensionServiceWorkerMessageFilter::OnDidStartServiceWorkerContext(
const ExtensionId& extension_id,
+ int activation_sequence,
const GURL& service_worker_scope,
int64_t service_worker_version_id,
int thread_id) {
@@ -161,12 +162,13 @@ void ExtensionServiceWorkerMessageFilter::OnDidStartServiceWorkerContext(
ServiceWorkerTaskQueue::Get(browser_context_)
->DidStartServiceWorkerContext(render_process_id_, extension_id,
- service_worker_scope,
+ activation_sequence, service_worker_scope,
service_worker_version_id, thread_id);
}
void ExtensionServiceWorkerMessageFilter::OnDidStopServiceWorkerContext(
const ExtensionId& extension_id,
+ int activation_sequence,
const GURL& service_worker_scope,
int64_t service_worker_version_id,
int thread_id) {
@@ -182,7 +184,7 @@ void ExtensionServiceWorkerMessageFilter::OnDidStopServiceWorkerContext(
ServiceWorkerTaskQueue::Get(browser_context_)
->DidStopServiceWorkerContext(render_process_id_, extension_id,
- service_worker_scope,
+ activation_sequence, service_worker_scope,
service_worker_version_id, thread_id);
}
diff --git a/chromium/extensions/browser/extension_service_worker_message_filter.h b/chromium/extensions/browser/extension_service_worker_message_filter.h
index 049d5771c6b..07a72ab13b5 100644
--- a/chromium/extensions/browser/extension_service_worker_message_filter.h
+++ b/chromium/extensions/browser/extension_service_worker_message_filter.h
@@ -56,10 +56,12 @@ class ExtensionServiceWorkerMessageFilter
int64_t service_worker_version_id,
int thread_id);
void OnDidStartServiceWorkerContext(const ExtensionId& extension_id,
+ int activation_sequence,
const GURL& service_worker_scope,
int64_t service_worker_version_id,
int thread_id);
void OnDidStopServiceWorkerContext(const ExtensionId& extension_id,
+ int activation_sequence,
const GURL& service_worker_scope,
int64_t service_worker_version_id,
int thread_id);
diff --git a/chromium/extensions/browser/renderer_startup_helper.cc b/chromium/extensions/browser/renderer_startup_helper.cc
index ff445f550d9..0c05644d32a 100644
--- a/chromium/extensions/browser/renderer_startup_helper.cc
+++ b/chromium/extensions/browser/renderer_startup_helper.cc
@@ -23,12 +23,14 @@
#include "extensions/browser/extension_util.h"
#include "extensions/browser/extensions_browser_client.h"
#include "extensions/browser/guest_view/web_view/web_view_guest.h"
+#include "extensions/browser/service_worker_task_queue.h"
#include "extensions/common/cors_util.h"
#include "extensions/common/extension_messages.h"
#include "extensions/common/extension_set.h"
#include "extensions/common/extensions_client.h"
#include "extensions/common/features/feature_channel.h"
#include "extensions/common/features/feature_session_type.h"
+#include "extensions/common/manifest_handlers/background_info.h"
#include "extensions/common/permissions/permissions_data.h"
#include "ui/base/webui/web_ui_util.h"
#include "url/origin.h"
@@ -56,6 +58,17 @@ bool IsExtensionVisibleToContext(const Extension& extension,
util::IsIncognitoEnabled(extension.id(), browser_context);
}
+// Returns the current ActivationSequence of |extension| if the extension is
+// Service Worker-based, otherwise returns base::nullopt.
+base::Optional<int> GetWorkerActivationSequence(BrowserContext* browser_context,
+ const Extension& extension) {
+ if (BackgroundInfo::IsServiceWorkerBased(&extension)) {
+ return ServiceWorkerTaskQueue::Get(browser_context)
+ ->GetCurrentSequence(extension.id());
+ }
+ return base::nullopt;
+}
+
} // namespace
RendererStartupHelper::RendererStartupHelper(BrowserContext* browser_context)
@@ -153,8 +166,9 @@ void RendererStartupHelper::InitializeProcess(
// I am not sure this is possible to know this here, at such a low
// level of the stack. Perhaps site isolation can help.
bool include_tab_permissions = true;
- loaded_extensions.push_back(
- ExtensionMsg_Loaded_Params(ext.get(), include_tab_permissions));
+ loaded_extensions.push_back(ExtensionMsg_Loaded_Params(
+ ext.get(), include_tab_permissions,
+ GetWorkerActivationSequence(renderer_context, *ext)));
extension_process_map_[ext->id()].insert(process);
}
@@ -248,7 +262,8 @@ void RendererStartupHelper::OnExtensionLoaded(const Extension& extension) {
// Uninitialized renderers will be informed of the extension load during the
// first batch of messages.
std::vector<ExtensionMsg_Loaded_Params> params;
- params.emplace_back(&extension, false /* no tab permissions */);
+ params.emplace_back(&extension, false /* no tab permissions */,
+ GetWorkerActivationSequence(browser_context_, extension));
for (content::RenderProcessHost* process : initialized_processes_) {
if (!IsExtensionVisibleToContext(extension, process->GetBrowserContext()))
diff --git a/chromium/extensions/browser/service_worker/worker_id.cc b/chromium/extensions/browser/service_worker/worker_id.cc
index bdfa21d8334..7e66ff0ad1c 100644
--- a/chromium/extensions/browser/service_worker/worker_id.cc
+++ b/chromium/extensions/browser/service_worker/worker_id.cc
@@ -24,4 +24,8 @@ bool WorkerId::operator==(const WorkerId& other) const {
version_id == other.version_id && thread_id == other.thread_id;
}
+bool WorkerId::operator!=(const WorkerId& other) const {
+ return !this->operator==(other);
+}
+
} // namespace extensions
diff --git a/chromium/extensions/browser/service_worker/worker_id.h b/chromium/extensions/browser/service_worker/worker_id.h
index 21f91c6c9a0..5ce79cdfab5 100644
--- a/chromium/extensions/browser/service_worker/worker_id.h
+++ b/chromium/extensions/browser/service_worker/worker_id.h
@@ -20,6 +20,7 @@ struct WorkerId {
bool operator<(const WorkerId& other) const;
bool operator==(const WorkerId& other) const;
+ bool operator!=(const WorkerId& other) const;
};
} // namespace extensions
diff --git a/chromium/extensions/browser/service_worker_task_queue.cc b/chromium/extensions/browser/service_worker_task_queue.cc
index dedb8c39f7d..087cdb0d519 100644
--- a/chromium/extensions/browser/service_worker_task_queue.cc
+++ b/chromium/extensions/browser/service_worker_task_queue.cc
@@ -40,6 +40,36 @@ const char kServiceWorkerVersion[] = "version";
ServiceWorkerTaskQueue::TestObserver* g_test_observer = nullptr;
+// ServiceWorkerRegistration state of an activated extension.
+enum class RegistrationState {
+ // Not registered.
+ kNotRegistered,
+ // Registration is inflight.
+ kPending,
+ // Registration is complete.
+ kRegistered,
+};
+
+// Browser process worker state of an activated extension.
+enum class BrowserState {
+ // Initial state, not started.
+ kInitial,
+ // Worker is in the process of starting from the browser process.
+ kStarting,
+ // Worker has completed starting (i.e. has seen DidStartWorkerForScope).
+ kStarted,
+};
+
+// Render process worker state of an activated extension.
+enum class RendererState {
+ // Initial state, neither started nor stopped.
+ kInitial,
+ // Worker thread has started.
+ kStarted,
+ // Worker thread has not started or has been stopped.
+ kStopped,
+};
+
} // namespace
ServiceWorkerTaskQueue::ServiceWorkerTaskQueue(BrowserContext* browser_context)
@@ -108,19 +138,44 @@ void ServiceWorkerTaskQueue::StartServiceWorkerOnCoreThreadToRunTasks(
context_id, task_queue_weak));
}
-// The current state of a worker.
-struct ServiceWorkerTaskQueue::WorkerState {
- // Whether or not worker has completed starting (DidStartWorkerForScope).
- bool browser_ready = false;
+// The current worker related state of an activated extension.
+class ServiceWorkerTaskQueue::WorkerState {
+ public:
+ WorkerState() = default;
+
+ WorkerState(const WorkerState&) = delete;
+ WorkerState& operator=(const WorkerState&) = delete;
- // Whether or not worker is ready in the renderer
- // (DidStartServiceWorkerContext).
- bool renderer_ready = false;
+ void SetWorkerId(const WorkerId& worker_id, ProcessManager* process_manager) {
+ if (worker_id_ && *worker_id_ != worker_id) {
+ // Sanity check that the old worker is gone.
+ DCHECK(!process_manager->HasServiceWorker(*worker_id_));
+ // Clear stale renderer state if there's any.
+ renderer_state_ = RendererState::kInitial;
+ }
+ worker_id_ = worker_id;
+ }
- // If |browser_ready| = true, this is the ActivationSequence of the worker.
- base::Optional<ActivationSequence> sequence;
+ bool ready() const {
+ return registration_state_ == RegistrationState::kRegistered &&
+ browser_state_ == BrowserState::kStarted &&
+ renderer_state_ == RendererState::kStarted && worker_id_.has_value();
+ }
+ bool has_pending_tasks() const { return !pending_tasks_.empty(); }
- WorkerState() = default;
+ private:
+ friend class ServiceWorkerTaskQueue;
+
+ RegistrationState registration_state_ = RegistrationState::kNotRegistered;
+ BrowserState browser_state_ = BrowserState::kInitial;
+ RendererState renderer_state_ = RendererState::kInitial;
+
+ // Pending tasks that will be run once the worker becomes ready.
+ std::vector<PendingTask> pending_tasks_;
+
+ // Contains the worker's WorkerId associated with this WorkerState, once we
+ // have discovered info about the worker.
+ base::Optional<WorkerId> worker_id_;
};
void ServiceWorkerTaskQueue::DidStartWorkerForScope(
@@ -135,11 +190,12 @@ void ServiceWorkerTaskQueue::DidStartWorkerForScope(
// Extension run with |sequence| was already deactivated.
// TODO(lazyboy): Add a DCHECK that the worker in question is actually
// shutting down soon.
- DCHECK(!base::Contains(pending_tasks_, context_id));
+ DCHECK(!GetWorkerState(context_id));
return;
}
- const LazyContextId& lazy_context_id = context_id.first;
+ WorkerState* worker_state = GetWorkerState(context_id);
+ DCHECK(worker_state);
const WorkerId worker_id = {extension_id, process_id, version_id, thread_id};
// Note: If the worker has already stopped on worker thread
@@ -150,15 +206,12 @@ void ServiceWorkerTaskQueue::DidStartWorkerForScope(
// renderer before we execute tasks in the browser process. This will also
// avoid holding the worker in |worker_state_map_| until deactivation as noted
// above.
- WorkerState* worker_state =
- GetOrCreateWorkerState(WorkerKey(lazy_context_id, worker_id));
- DCHECK(worker_state);
- DCHECK(!worker_state->browser_ready) << "Worker was already loaded";
- worker_state->browser_ready = true;
- worker_state->sequence = sequence;
+ DCHECK_NE(BrowserState::kStarted, worker_state->browser_state_)
+ << "Worker was already loaded";
+ worker_state->SetWorkerId(worker_id, ProcessManager::Get(browser_context_));
+ worker_state->browser_state_ = BrowserState::kStarted;
- RunPendingTasksIfWorkerReady(lazy_context_id, version_id, process_id,
- thread_id);
+ RunPendingTasksIfWorkerReady(context_id);
}
void ServiceWorkerTaskQueue::DidStartWorkerFail(
@@ -167,9 +220,10 @@ void ServiceWorkerTaskQueue::DidStartWorkerFail(
if (!IsCurrentSequence(context_id.first.extension_id(), context_id.second)) {
// This can happen is when the registration got unregistered right before we
// tried to start it. See crbug.com/999027 for details.
- DCHECK(!base::Contains(pending_tasks_, context_id));
+ DCHECK(!GetWorkerState(context_id));
return;
}
+
// TODO(lazyboy): Handle failure cases.
DCHECK(false) << "DidStartWorkerFail: " << context_id.first.extension_id();
}
@@ -188,50 +242,70 @@ void ServiceWorkerTaskQueue::DidInitializeServiceWorkerContext(
void ServiceWorkerTaskQueue::DidStartServiceWorkerContext(
int render_process_id,
const ExtensionId& extension_id,
+ int activation_sequence,
const GURL& service_worker_scope,
int64_t service_worker_version_id,
int thread_id) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- LazyContextId context_id(browser_context_, extension_id,
- service_worker_scope);
+ if (!IsCurrentSequence(extension_id, activation_sequence))
+ return;
+
+ SequencedContextId context_id(
+ LazyContextId(browser_context_, extension_id, service_worker_scope),
+ activation_sequence);
const WorkerId worker_id = {extension_id, render_process_id,
service_worker_version_id, thread_id};
- WorkerState* worker_state =
- GetOrCreateWorkerState(WorkerKey(context_id, worker_id));
- DCHECK(!worker_state->renderer_ready) << "Worker already started";
- worker_state->renderer_ready = true;
-
- RunPendingTasksIfWorkerReady(context_id, service_worker_version_id,
- render_process_id, thread_id);
+ WorkerState* worker_state = GetWorkerState(context_id);
+ DCHECK(worker_state);
+ // If |worker_state| had a worker running previously, for which we didn't
+ // see DidStopServiceWorkerContext notification (typically happens on render
+ // process shutdown), then we'd preserve stale state in |renderer_state_|.
+ //
+ // This isn't a problem because the next browser process readiness
+ // (DidStartWorkerForScope) or the next renderer process readiness
+ // (DidStartServiceWorkerContext) will clear the state, whichever happens
+ // first.
+ //
+ // TODO(lazyboy): Update the renderer state in RenderProcessExited() and
+ // uncomment the following DCHECK:
+ // DCHECK_NE(RendererState::kStarted, worker_state->renderer_state_)
+ // << "Worker already started";
+ worker_state->SetWorkerId(worker_id, ProcessManager::Get(browser_context_));
+ worker_state->renderer_state_ = RendererState::kStarted;
+
+ RunPendingTasksIfWorkerReady(context_id);
}
void ServiceWorkerTaskQueue::DidStopServiceWorkerContext(
int render_process_id,
const ExtensionId& extension_id,
+ int activation_sequence,
const GURL& service_worker_scope,
int64_t service_worker_version_id,
int thread_id) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
+ if (!IsCurrentSequence(extension_id, activation_sequence))
+ return;
+
const WorkerId worker_id = {extension_id, render_process_id,
service_worker_version_id, thread_id};
ProcessManager::Get(browser_context_)->UnregisterServiceWorker(worker_id);
- LazyContextId context_id(browser_context_, extension_id,
- service_worker_scope);
+ SequencedContextId context_id(
+ LazyContextId(browser_context_, extension_id, service_worker_scope),
+ activation_sequence);
- WorkerKey worker_key(context_id, worker_id);
- WorkerState* worker_state = GetWorkerState(worker_key);
- if (!worker_state) {
+ WorkerState* worker_state = GetWorkerState(context_id);
+ DCHECK(worker_state);
+
+ if (worker_state->worker_id_ != worker_id) {
// We can see DidStopServiceWorkerContext right after DidInitialize and
// without DidStartServiceWorkerContext.
return;
}
- // Clean up both the renderer and browser readiness states.
- // One caveat is that although this is renderer notification, we also clear
- // the browser readiness state, this is because a worker can be
- // |browser_ready| and was waiting for DidStartServiceWorkerContext, but
- // instead received DidStopServiceWorkerContext.
- worker_state_map_.erase(worker_key);
+ DCHECK_NE(RendererState::kStopped, worker_state->renderer_state_);
+ worker_state->renderer_state_ = RendererState::kStopped;
+ worker_state->worker_id_ = base::nullopt;
}
// static
@@ -258,11 +332,13 @@ void ServiceWorkerTaskQueue::AddPendingTask(
DCHECK(sequence) << "Trying to add pending task to an inactive extension: "
<< lazy_context_id.extension_id();
const SequencedContextId context_id(lazy_context_id, *sequence);
- auto& tasks = pending_tasks_[context_id];
+ WorkerState* worker_state = GetWorkerState(context_id);
+ DCHECK(worker_state);
+ auto& tasks = worker_state->pending_tasks_;
bool needs_start_worker = tasks.empty();
tasks.push_back(std::move(task));
- if (pending_registrations_.count(context_id) > 0) {
+ if (worker_state->registration_state_ != RegistrationState::kRegistered) {
// If the worker hasn't finished registration, wait for it to complete.
// DidRegisterServiceWorker will Start worker to run |task| later.
return;
@@ -280,6 +356,11 @@ void ServiceWorkerTaskQueue::ActivateExtension(const Extension* extension) {
const ExtensionId extension_id = extension->id();
ActivationSequence current_sequence = ++next_activation_sequence_;
activation_sequences_[extension_id] = current_sequence;
+ SequencedContextId context_id(
+ LazyContextId(browser_context_, extension_id, extension->url()),
+ current_sequence);
+ DCHECK(!base::Contains(worker_state_map_, context_id));
+ WorkerState& worker_state = worker_state_map_[context_id];
// Note: version.IsValid() = false implies we didn't have any prefs stored.
base::Version version = RetrieveRegisteredServiceWorkerVersion(extension_id);
@@ -291,15 +372,13 @@ void ServiceWorkerTaskQueue::ActivateExtension(const Extension* extension) {
}
if (service_worker_already_registered) {
+ worker_state.registration_state_ = RegistrationState::kRegistered;
// TODO(https://crbug.com/901101): We should kick off an async check to see
// if the registration is *actually* there and re-register if necessary.
return;
}
- SequencedContextId context_id(
- LazyContextId(browser_context_, extension_id, extension->url()),
- current_sequence);
- pending_registrations_.insert(context_id);
+ worker_state.registration_state_ = RegistrationState::kPending;
GURL script_url = extension->GetResourceURL(
BackgroundInfo::GetBackgroundServiceWorkerScript(extension));
blink::mojom::ServiceWorkerRegistrationOptions option;
@@ -328,16 +407,13 @@ void ServiceWorkerTaskQueue::DeactivateExtension(const Extension* extension) {
SequencedContextId context_id(
LazyContextId(browser_context_, extension_id, extension->url()),
*sequence);
- ClearPendingTasks(context_id);
-
- // Clear loaded worker if it was waiting for start.
- // Note that we don't clear the entire state here as we expect the renderer to
- // stop shortly after this and its notification will clear the state.
- ClearBrowserReadyForWorkers(
- LazyContextId(browser_context_, extension_id, extension->url()),
- *sequence);
+ WorkerState* worker_state = GetWorkerState(context_id);
+ DCHECK(worker_state);
+ // TODO(lazyboy): Run orphaned tasks with nullptr ContextInfo.
+ worker_state->pending_tasks_.clear();
+ worker_state_map_.erase(context_id);
- content::BrowserContext::GetStoragePartitionForSite(browser_context_,
+ content::BrowserContext::GetStoragePartitionForSite(browser_context_,
extension->url())
->GetServiceWorkerContext()
->UnregisterServiceWorker(
@@ -354,6 +430,9 @@ void ServiceWorkerTaskQueue::RunTasksAfterStartWorker(
if (lazy_context_id.browser_context() != browser_context_)
return;
+ WorkerState* worker_state = GetWorkerState(context_id);
+ DCHECK_NE(BrowserState::kStarted, worker_state->browser_state_);
+
content::StoragePartition* partition =
BrowserContext::GetStoragePartitionForSite(
lazy_context_id.browser_context(),
@@ -377,35 +456,30 @@ void ServiceWorkerTaskQueue::RunTasksAfterStartWorker(
void ServiceWorkerTaskQueue::DidRegisterServiceWorker(
const SequencedContextId& context_id,
bool success) {
- pending_registrations_.erase(context_id);
ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_);
const ExtensionId& extension_id = context_id.first.extension_id();
DCHECK(registry);
const Extension* extension =
registry->enabled_extensions().GetByID(extension_id);
if (!extension) {
- // DeactivateExtension must have cleared |pending_tasks_| already.
- DCHECK(!base::Contains(pending_tasks_, context_id));
return;
}
+ if (!IsCurrentSequence(extension_id, context_id.second))
+ return;
+
+ WorkerState* worker_state = GetWorkerState(context_id);
+ DCHECK(worker_state);
if (!success) {
- if (!IsCurrentSequence(extension_id, context_id.second)) {
- // DeactivateExtension must have cleared |pending_tasks_| already.
- DCHECK(!base::Contains(pending_tasks_, context_id));
- return;
- }
// TODO(lazyboy): Handle failure case thoroughly.
DCHECK(false) << "Failed to register Service Worker";
return;
}
+ worker_state->registration_state_ = RegistrationState::kRegistered;
SetRegisteredServiceWorkerInfo(extension->id(), extension->version());
- auto pending_tasks_iter = pending_tasks_.find(context_id);
- const bool has_pending_tasks = pending_tasks_iter != pending_tasks_.end() &&
- pending_tasks_iter->second.size() > 0u;
- if (has_pending_tasks) {
+ if (worker_state->has_pending_tasks()) {
// TODO(lazyboy): If worker for |context_id| is already running, consider
// not calling StartWorker. This isn't straightforward as service worker's
// internal state is mostly on the core thread.
@@ -467,45 +541,35 @@ void ServiceWorkerTaskQueue::RemoveRegisteredServiceWorkerInfo(
}
void ServiceWorkerTaskQueue::RunPendingTasksIfWorkerReady(
- const LazyContextId& context_id,
- int64_t version_id,
- int process_id,
- int thread_id) {
- WorkerState* worker_state = GetWorkerState(WorkerKey(
- context_id,
- {context_id.extension_id(), process_id, version_id, thread_id}));
+ const SequencedContextId& context_id) {
+ WorkerState* worker_state = GetWorkerState(context_id);
DCHECK(worker_state);
- if (!worker_state->browser_ready || !worker_state->renderer_ready) {
+ if (!worker_state->ready()) {
// Worker isn't ready yet, wait for next event and run the tasks then.
return;
}
- base::Optional<int> sequence = worker_state->sequence;
- DCHECK(sequence.has_value());
// Running |pending_tasks_[context_id]| marks the completion of
// DidStartWorkerForScope, clean up |browser_ready| state of the worker so
// that new tasks can be queued up.
- worker_state->browser_ready = false;
-
- auto iter = pending_tasks_.find(SequencedContextId(context_id, *sequence));
- DCHECK(iter != pending_tasks_.end()) << "Worker ready, but no tasks to run!";
- std::vector<PendingTask> tasks = std::move(iter->second);
- pending_tasks_.erase(iter);
+ worker_state->browser_state_ = BrowserState::kInitial;
+
+ DCHECK(worker_state->has_pending_tasks())
+ << "Worker ready, but no tasks to run!";
+ std::vector<PendingTask> tasks;
+ std::swap(worker_state->pending_tasks_, tasks);
+ DCHECK(worker_state->worker_id_);
+ const auto& worker_id = *worker_state->worker_id_;
for (auto& task : tasks) {
auto context_info = std::make_unique<LazyContextTaskQueue::ContextInfo>(
- context_id.extension_id(),
- content::RenderProcessHost::FromID(process_id), version_id, thread_id,
- context_id.service_worker_scope());
+ context_id.first.extension_id(),
+ content::RenderProcessHost::FromID(worker_id.render_process_id),
+ worker_id.version_id, worker_id.thread_id,
+ context_id.first.service_worker_scope());
std::move(task).Run(std::move(context_info));
}
}
-void ServiceWorkerTaskQueue::ClearPendingTasks(
- const SequencedContextId& context_id) {
- // TODO(lazyboy): Run orphaned tasks with nullptr ContextInfo.
- pending_tasks_.erase(context_id);
-}
-
bool ServiceWorkerTaskQueue::IsCurrentSequence(
const ExtensionId& extension_id,
ActivationSequence sequence) const {
@@ -522,44 +586,11 @@ ServiceWorkerTaskQueue::GetCurrentSequence(
return iter->second;
}
-ServiceWorkerTaskQueue::WorkerState*
-ServiceWorkerTaskQueue::GetOrCreateWorkerState(const WorkerKey& worker_key) {
- auto iter = worker_state_map_.find(worker_key);
- if (iter == worker_state_map_.end())
- iter = worker_state_map_.emplace(worker_key, WorkerState()).first;
- return &(iter->second);
-}
-
ServiceWorkerTaskQueue::WorkerState* ServiceWorkerTaskQueue::GetWorkerState(
- const WorkerKey& worker_key) {
- auto iter = worker_state_map_.find(worker_key);
- if (iter == worker_state_map_.end())
- return nullptr;
- return &(iter->second);
-}
-
-void ServiceWorkerTaskQueue::ClearBrowserReadyForWorkers(
- const LazyContextId& context_id,
- ActivationSequence sequence) {
- // TODO(lazyboy): We could use |worker_state_map_|.lower_bound() to avoid
- // iterating over all workers. Note that it would require creating artificial
- // WorkerKey with |context_id|.
- for (auto iter = worker_state_map_.begin();
- iter != worker_state_map_.end();) {
- if (iter->first.first != context_id || iter->second.sequence != sequence) {
- ++iter;
- continue;
- }
-
- iter->second.browser_ready = false;
- iter->second.sequence = base::nullopt;
-
- // Clean up stray entries if renderer readiness was also gone.
- if (!iter->second.renderer_ready)
- iter = worker_state_map_.erase(iter);
- else
- ++iter;
- }
+ const SequencedContextId& context_id) {
+ auto worker_iter = worker_state_map_.find(context_id);
+ return worker_iter == worker_state_map_.end() ? nullptr
+ : &worker_iter->second;
}
} // namespace extensions
diff --git a/chromium/extensions/browser/service_worker_task_queue.h b/chromium/extensions/browser/service_worker_task_queue.h
index 2e5e215bd19..76b22858cc5 100644
--- a/chromium/extensions/browser/service_worker_task_queue.h
+++ b/chromium/extensions/browser/service_worker_task_queue.h
@@ -72,6 +72,12 @@ class Extension;
class ServiceWorkerTaskQueue : public KeyedService,
public LazyContextTaskQueue {
public:
+ // Unique identifier for an extension's activation->deactivation span.
+ // TODO(lazyboy): Move this under extensions/common/ for consistency, so that
+ // renderer process can use this instead of using "int" directly. We'd also
+ // want StrongAlias for this.
+ using ActivationSequence = int;
+
explicit ServiceWorkerTaskQueue(content::BrowserContext* browser_context);
~ServiceWorkerTaskQueue() override;
@@ -102,16 +108,24 @@ class ServiceWorkerTaskQueue : public KeyedService,
// has completed executing.
void DidStartServiceWorkerContext(int render_process_id,
const ExtensionId& extension_id,
+ int activation_sequence,
const GURL& service_worker_scope,
int64_t service_worker_version_id,
int thread_id);
// Called once an extension Service Worker was destroyed.
void DidStopServiceWorkerContext(int render_process_id,
const ExtensionId& extension_id,
+ int activation_sequence,
const GURL& service_worker_scope,
int64_t service_worker_version_id,
int thread_id);
+ // Returns the current ActivationSequence for an extension, if the extension
+ // is currently activated. Returns base::nullopt if the extension isn't
+ // activated.
+ base::Optional<ActivationSequence> GetCurrentSequence(
+ const ExtensionId& extension_id) const;
+
class TestObserver {
public:
TestObserver();
@@ -130,14 +144,9 @@ class ServiceWorkerTaskQueue : public KeyedService,
static void SetObserverForTest(TestObserver* observer);
private:
- // Unique identifier for an extension's activation->deactivation span.
- using ActivationSequence = int;
using SequencedContextId = std::pair<LazyContextId, ActivationSequence>;
- // Key used to identify a WorkerState within the worker container.
- using WorkerKey = std::pair<LazyContextId, WorkerId>;
-
- struct WorkerState;
+ class WorkerState;
static void DidStartWorkerForScopeOnCoreThread(
const SequencedContextId& context_id,
@@ -184,40 +193,19 @@ class ServiceWorkerTaskQueue : public KeyedService,
// If the worker with |context_id| has seen worker start
// (DidStartWorkerForScope) and load (DidStartServiceWorkerContext) then runs
// all pending tasks for that worker.
- void RunPendingTasksIfWorkerReady(const LazyContextId& context_id,
- int64_t version_id,
- int process_id,
- int thread_id);
-
- void ClearPendingTasks(const SequencedContextId& context_id);
+ void RunPendingTasksIfWorkerReady(const SequencedContextId& context_id);
// Returns true if |sequence| is the current activation sequence for
// |extension_id|.
bool IsCurrentSequence(const ExtensionId& extension_id,
ActivationSequence sequence) const;
- // Returns the current ActivationSequence for an extension, if the extension
- // is currently activated. Returns base::nullopt if the extension isn't
- // activated.
- base::Optional<ActivationSequence> GetCurrentSequence(
- const ExtensionId& extension_id) const;
-
- WorkerState* GetOrCreateWorkerState(const WorkerKey& worker_key);
- WorkerState* GetWorkerState(const WorkerKey& worker_key);
- void ClearBrowserReadyForWorkers(const LazyContextId& context_id,
- ActivationSequence sequence);
+ WorkerState* GetWorkerState(const SequencedContextId& context_id);
ActivationSequence next_activation_sequence_ = 0;
- // Set of extension ids that hasn't completed Service Worker registration.
- std::set<SequencedContextId> pending_registrations_;
-
- // The state of each workers we know about.
- std::map<WorkerKey, WorkerState> worker_state_map_;
-
- // Pending tasks for a |LazyContextId| with an ActivationSequence.
- // These tasks will be run once the corresponding worker becomes ready.
- std::map<SequencedContextId, std::vector<PendingTask>> pending_tasks_;
+ // The state of worker of each activated extension.
+ std::map<SequencedContextId, WorkerState> worker_state_map_;
content::BrowserContext* const browser_context_ = nullptr;
diff --git a/chromium/extensions/common/extension_messages.cc b/chromium/extensions/common/extension_messages.cc
index 1edd698679e..9f8d1b048c4 100644
--- a/chromium/extensions/common/extension_messages.cc
+++ b/chromium/extensions/common/extension_messages.cc
@@ -61,7 +61,8 @@ ExtensionMsg_Loaded_Params::~ExtensionMsg_Loaded_Params() {}
ExtensionMsg_Loaded_Params::ExtensionMsg_Loaded_Params(
const Extension* extension,
- bool include_tab_permissions)
+ bool include_tab_permissions,
+ base::Optional<int> worker_activation_sequence)
: manifest(static_cast<base::DictionaryValue&&>(
extension->manifest()->value()->Clone())),
location(extension->location()),
@@ -76,6 +77,7 @@ ExtensionMsg_Loaded_Params::ExtensionMsg_Loaded_Params(
uses_default_policy_blocked_allowed_hosts(
extension->permissions_data()->UsesDefaultPolicyHostRestrictions()),
id(extension->id()),
+ worker_activation_sequence(worker_activation_sequence),
creation_flags(extension->creation_flags()) {
if (include_tab_permissions) {
for (const auto& pair :
@@ -327,6 +329,7 @@ void ParamTraits<ExtensionMsg_Loaded_Params>::Write(base::Pickle* m,
WriteParam(m, p.policy_blocked_hosts);
WriteParam(m, p.policy_allowed_hosts);
WriteParam(m, p.uses_default_policy_blocked_allowed_hosts);
+ WriteParam(m, p.worker_activation_sequence);
}
bool ParamTraits<ExtensionMsg_Loaded_Params>::Read(const base::Pickle* m,
@@ -341,7 +344,8 @@ bool ParamTraits<ExtensionMsg_Loaded_Params>::Read(const base::Pickle* m,
ReadParam(m, iter, &p->tab_specific_permissions) &&
ReadParam(m, iter, &p->policy_blocked_hosts) &&
ReadParam(m, iter, &p->policy_allowed_hosts) &&
- ReadParam(m, iter, &p->uses_default_policy_blocked_allowed_hosts);
+ ReadParam(m, iter, &p->uses_default_policy_blocked_allowed_hosts) &&
+ ReadParam(m, iter, &p->worker_activation_sequence);
}
void ParamTraits<ExtensionMsg_Loaded_Params>::Log(const param_type& p,
diff --git a/chromium/extensions/common/extension_messages.h b/chromium/extensions/common/extension_messages.h
index 632f9846d82..f207638c2fa 100644
--- a/chromium/extensions/common/extension_messages.h
+++ b/chromium/extensions/common/extension_messages.h
@@ -356,7 +356,8 @@ struct ExtensionMsg_Loaded_Params {
ExtensionMsg_Loaded_Params();
~ExtensionMsg_Loaded_Params();
ExtensionMsg_Loaded_Params(const extensions::Extension* extension,
- bool include_tab_permissions);
+ bool include_tab_permissions,
+ base::Optional<int> worker_activation_sequence);
ExtensionMsg_Loaded_Params(ExtensionMsg_Loaded_Params&& other);
ExtensionMsg_Loaded_Params& operator=(ExtensionMsg_Loaded_Params&& other);
@@ -391,6 +392,10 @@ struct ExtensionMsg_Loaded_Params {
// We keep this separate so that it can be used in logging.
std::string id;
+ // If this extension is Service Worker based, then this contains the
+ // activation sequence of the extension.
+ base::Optional<int> worker_activation_sequence;
+
// Send creation flags so extension is initialized identically.
int creation_flags;
@@ -1060,16 +1065,18 @@ IPC_MESSAGE_CONTROL3(ExtensionHostMsg_DidInitializeServiceWorkerContext,
// straightforward as it changes SW IPC ordering with respect of rest of
// Chrome.
// See https://crbug.com/879015#c4 for details.
-IPC_MESSAGE_CONTROL4(ExtensionHostMsg_DidStartServiceWorkerContext,
+IPC_MESSAGE_CONTROL5(ExtensionHostMsg_DidStartServiceWorkerContext,
std::string /* extension_id */,
+ int /* activation_sequence */,
GURL /* service_worker_scope */,
int64_t /* service_worker_version_id */,
int /* worker_thread_id */)
// Tells the browser that an extension service worker context has been
// destroyed.
-IPC_MESSAGE_CONTROL4(ExtensionHostMsg_DidStopServiceWorkerContext,
+IPC_MESSAGE_CONTROL5(ExtensionHostMsg_DidStopServiceWorkerContext,
std::string /* extension_id */,
+ int /* activation_sequence */,
GURL /* service_worker_scope */,
int64_t /* service_worker_version_id */,
int /* worker_thread_id */)
diff --git a/chromium/extensions/common/extension_messages_unittest.cc b/chromium/extensions/common/extension_messages_unittest.cc
index 208436da1fc..f99d59e6c3e 100644
--- a/chromium/extensions/common/extension_messages_unittest.cc
+++ b/chromium/extensions/common/extension_messages_unittest.cc
@@ -81,7 +81,7 @@ TEST(ExtensionMessageTypesTest, TestLoadedParams) {
extension->permissions_data()->SetPolicyHostRestrictions(
runtime_blocked_hosts, runtime_allowed_hosts);
- ExtensionMsg_Loaded_Params params_in(extension.get(), true);
+ ExtensionMsg_Loaded_Params params_in(extension.get(), true, base::nullopt);
EXPECT_EQ(extension->id(), params_in.id);
{
diff --git a/chromium/extensions/renderer/dispatcher.cc b/chromium/extensions/renderer/dispatcher.cc
index f4c7df774de..456e51a3aba 100644
--- a/chromium/extensions/renderer/dispatcher.cc
+++ b/chromium/extensions/renderer/dispatcher.cc
@@ -451,8 +451,11 @@ void Dispatcher::WillEvaluateServiceWorkerOnWorkerThread(
std::unique_ptr<IPCMessageSender> ipc_sender =
IPCMessageSender::CreateWorkerThreadIPCMessageSender(
worker_dispatcher, service_worker_version_id);
+ int worker_activation_sequence =
+ *RendererExtensionRegistry::Get()->GetWorkerActivationSequence(
+ extension->id());
worker_dispatcher->AddWorkerData(
- service_worker_version_id, context,
+ service_worker_version_id, worker_activation_sequence, context,
CreateBindingsSystem(std::move(ipc_sender)));
worker_thread_util::SetWorkerContextProxy(context_proxy);
@@ -986,6 +989,10 @@ void Dispatcher::OnLoaded(
// consider making this a release CHECK.
NOTREACHED();
}
+ if (param.worker_activation_sequence) {
+ extension_registry->SetWorkerActivationSequence(
+ extension, *param.worker_activation_sequence);
+ }
if (param.uses_default_policy_blocked_allowed_hosts) {
extension->permissions_data()->SetUsesDefaultHostRestrictions();
} else {
diff --git a/chromium/extensions/renderer/renderer_extension_registry.cc b/chromium/extensions/renderer/renderer_extension_registry.cc
index 4fb14dde74b..93936af5c24 100644
--- a/chromium/extensions/renderer/renderer_extension_registry.cc
+++ b/chromium/extensions/renderer/renderer_extension_registry.cc
@@ -7,6 +7,7 @@
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "content/public/renderer/render_thread.h"
+#include "extensions/common/manifest_handlers/background_info.h"
namespace extensions {
@@ -102,4 +103,24 @@ bool RendererExtensionRegistry::ExtensionBindingsAllowed(
return extensions_.ExtensionBindingsAllowed(url);
}
+void RendererExtensionRegistry::SetWorkerActivationSequence(
+ const scoped_refptr<const Extension>& extension,
+ int worker_activation_sequence) {
+ DCHECK(content::RenderThread::Get());
+ DCHECK(Contains(extension->id()));
+ DCHECK(BackgroundInfo::IsServiceWorkerBased(extension.get()));
+
+ base::AutoLock lock(lock_);
+ worker_activation_sequences_[extension->id()] = worker_activation_sequence;
+}
+
+base::Optional<int> RendererExtensionRegistry::GetWorkerActivationSequence(
+ const ExtensionId& extension_id) const {
+ base::AutoLock lock(lock_);
+ auto iter = worker_activation_sequences_.find(extension_id);
+ if (iter == worker_activation_sequences_.end())
+ return base::nullopt;
+ return iter->second;
+}
+
} // namespace extensions
diff --git a/chromium/extensions/renderer/renderer_extension_registry.h b/chromium/extensions/renderer/renderer_extension_registry.h
index 97c24bbf535..a62d26c9b17 100644
--- a/chromium/extensions/renderer/renderer_extension_registry.h
+++ b/chromium/extensions/renderer/renderer_extension_registry.h
@@ -10,7 +10,9 @@
#include <string>
#include "base/macros.h"
+#include "base/optional.h"
#include "base/synchronization/lock.h"
+#include "extensions/common/extension_id.h"
#include "extensions/common/extension_set.h"
class GURL;
@@ -49,9 +51,23 @@ class RendererExtensionRegistry {
ExtensionIdSet GetIDs() const;
bool ExtensionBindingsAllowed(const GURL& url) const;
+ // ActivationSequence related methods.
+ //
+ // Sets ActivationSequence for a Service Worker based |extension|.
+ void SetWorkerActivationSequence(
+ const scoped_refptr<const Extension>& extension,
+ int worker_activation_sequence);
+ // Returns the current activation sequence for worker based extension with
+ // |extension_id|. Returns base::nullopt otherwise.
+ base::Optional<int> GetWorkerActivationSequence(
+ const ExtensionId& extension_id) const;
+
private:
ExtensionSet extensions_;
+ // Maps extension id to ActivationSequence, for worker based extensions.
+ std::map<ExtensionId, int> worker_activation_sequences_;
+
mutable base::Lock lock_;
DISALLOW_COPY_AND_ASSIGN(RendererExtensionRegistry);
diff --git a/chromium/extensions/renderer/service_worker_data.cc b/chromium/extensions/renderer/service_worker_data.cc
index 3c96f099e00..1f95bd2bb50 100644
--- a/chromium/extensions/renderer/service_worker_data.cc
+++ b/chromium/extensions/renderer/service_worker_data.cc
@@ -10,9 +10,11 @@ namespace extensions {
ServiceWorkerData::ServiceWorkerData(
int64_t service_worker_version_id,
+ int activation_sequence,
ScriptContext* context,
std::unique_ptr<NativeExtensionBindingsSystem> bindings_system)
: service_worker_version_id_(service_worker_version_id),
+ activation_sequence_(activation_sequence),
context_(context),
v8_schema_registry_(new V8SchemaRegistry),
bindings_system_(std::move(bindings_system)) {}
diff --git a/chromium/extensions/renderer/service_worker_data.h b/chromium/extensions/renderer/service_worker_data.h
index 1ee28d193a1..5cd152a378e 100644
--- a/chromium/extensions/renderer/service_worker_data.h
+++ b/chromium/extensions/renderer/service_worker_data.h
@@ -20,6 +20,7 @@ class ServiceWorkerData {
public:
ServiceWorkerData(
int64_t service_worker_version_id,
+ int activation_sequence,
ScriptContext* context,
std::unique_ptr<NativeExtensionBindingsSystem> bindings_system);
~ServiceWorkerData();
@@ -31,10 +32,12 @@ class ServiceWorkerData {
int64_t service_worker_version_id() const {
return service_worker_version_id_;
}
+ int activation_sequence() const { return activation_sequence_; }
ScriptContext* context() const { return context_; }
private:
const int64_t service_worker_version_id_;
+ const int activation_sequence_;
ScriptContext* const context_ = nullptr;
std::unique_ptr<V8SchemaRegistry> v8_schema_registry_;
diff --git a/chromium/extensions/renderer/worker_thread_dispatcher.cc b/chromium/extensions/renderer/worker_thread_dispatcher.cc
index efbcdc376f9..9906e300109 100644
--- a/chromium/extensions/renderer/worker_thread_dispatcher.cc
+++ b/chromium/extensions/renderer/worker_thread_dispatcher.cc
@@ -264,12 +264,14 @@ void WorkerThreadDispatcher::OnDispatchOnDisconnect(
void WorkerThreadDispatcher::AddWorkerData(
int64_t service_worker_version_id,
+ int activation_sequence,
ScriptContext* script_context,
std::unique_ptr<NativeExtensionBindingsSystem> bindings_system) {
ServiceWorkerData* data = g_data_tls.Pointer()->Get();
if (!data) {
- ServiceWorkerData* new_data = new ServiceWorkerData(
- service_worker_version_id, script_context, std::move(bindings_system));
+ ServiceWorkerData* new_data =
+ new ServiceWorkerData(service_worker_version_id, activation_sequence,
+ script_context, std::move(bindings_system));
g_data_tls.Pointer()->Set(new_data);
}
@@ -300,8 +302,8 @@ void WorkerThreadDispatcher::DidStartContext(
const int thread_id = content::WorkerThread::GetCurrentId();
DCHECK_NE(thread_id, kMainThreadId);
Send(new ExtensionHostMsg_DidStartServiceWorkerContext(
- data->context()->GetExtensionID(), service_worker_scope,
- service_worker_version_id, thread_id));
+ data->context()->GetExtensionID(), data->activation_sequence(),
+ service_worker_scope, service_worker_version_id, thread_id));
}
void WorkerThreadDispatcher::DidStopContext(const GURL& service_worker_scope,
@@ -311,8 +313,8 @@ void WorkerThreadDispatcher::DidStopContext(const GURL& service_worker_scope,
DCHECK_NE(thread_id, kMainThreadId);
DCHECK_EQ(service_worker_version_id, data->service_worker_version_id());
Send(new ExtensionHostMsg_DidStopServiceWorkerContext(
- data->context()->GetExtensionID(), service_worker_scope,
- service_worker_version_id, thread_id));
+ data->context()->GetExtensionID(), data->activation_sequence(),
+ service_worker_scope, service_worker_version_id, thread_id));
}
void WorkerThreadDispatcher::RemoveWorkerData(
diff --git a/chromium/extensions/renderer/worker_thread_dispatcher.h b/chromium/extensions/renderer/worker_thread_dispatcher.h
index dc341bb6db1..f13874ef9e8 100644
--- a/chromium/extensions/renderer/worker_thread_dispatcher.h
+++ b/chromium/extensions/renderer/worker_thread_dispatcher.h
@@ -63,6 +63,7 @@ class WorkerThreadDispatcher : public content::RenderThreadObserver,
void AddWorkerData(
int64_t service_worker_version_id,
+ int activation_sequence,
ScriptContext* script_context,
std::unique_ptr<NativeExtensionBindingsSystem> bindings_system);
void RemoveWorkerData(int64_t service_worker_version_id);