Commit 4b70a70d authored by Istiaque Ahmed's avatar Istiaque Ahmed Committed by Commit Bot

Extension SW: Run all PendingTasks once a lazy context's worker is ready.

For running PendingTasks for a particular ContextId through
SWTQ::AddPendingTask(), call StartWorker* exactly once instead of calling it
each time for each AddPendingTask call. This has two advantages:
  - We don't call StartWorker* more than once when we don't need to.
  - Extension messaging subtly depends on running these tasks once. This is
    because how LazyBackgroundTaskQueue behaves in terms of multiple pending
    tasks and we need some sort of parity in SWTQ for future worker messaging
    implementation.

Detailed changes:
- ServiceWorkerTaskQueue: Remove the queue for registration
  (old |pending_tasks_|) and worker start (|waiting_did_start_worker_tasks_|)
  and merge them to a single queue (|pending_tasks_). Run these tasks
  once registration, service worker start (DidStartWorkerForScope) and
  service worker loadstop (DidStartServiceWorkerContext) completes.
- IPC: Add thread_id and SW scope to DidStartServiceWorkerContext IPC
  (loadstop), as PendingTasks that were pending after DidStartWorkerForScope
  but before the SW context has seen loadstop can use these thread_id and
  scope to build LazyContextTaskQueue::ContextInfo to run those tasks.
  Previously we used to remember these two pieces of information in
  WaitingDidStartWorkerTask. This also simplifies the queue in SWTQ.
- IPC: Add thread_id and SW scope to DidStopServiceWorkerContext IPC
  to clean up the task containers on worker shutdown. This will also
  be necessary for multiple worker support within extension in future.

Bug: 925927
Test: Service worker within extension internal change, no visible effect.
Change-Id: I06581f821550154ebca3ee6d9aa803953abf7d86
Reviewed-on: https://chromium-review.googlesource.com/c/1481580Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarDevlin <rdevlin.cronin@chromium.org>
Commit-Queue: Istiaque Ahmed <lazyboy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#636239}
parent 3d5f06ed
...@@ -104,28 +104,44 @@ void ExtensionServiceWorkerMessageFilter::OnEventAckWorker( ...@@ -104,28 +104,44 @@ void ExtensionServiceWorkerMessageFilter::OnEventAckWorker(
void ExtensionServiceWorkerMessageFilter::OnDidStartServiceWorkerContext( void ExtensionServiceWorkerMessageFilter::OnDidStartServiceWorkerContext(
const std::string& extension_id, const std::string& extension_id,
int64_t service_worker_version_id) { const GURL& service_worker_scope,
int64_t service_worker_version_id,
int thread_id) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK_NE(kMainThreadId, thread_id);
if (!ProcessMap::Get(browser_context_) if (!ProcessMap::Get(browser_context_)
->Contains(extension_id, render_process_id_)) { ->Contains(extension_id, render_process_id_)) {
// We can legitimately get here if the extension was already unloaded. // We can legitimately get here if the extension was already unloaded.
return; return;
} }
CHECK(service_worker_scope.SchemeIs(kExtensionScheme) &&
extension_id == service_worker_scope.host_piece());
ServiceWorkerTaskQueue::Get(browser_context_) ServiceWorkerTaskQueue::Get(browser_context_)
->DidStartServiceWorkerContext(extension_id, service_worker_version_id); ->DidStartServiceWorkerContext(render_process_id_, extension_id,
service_worker_scope,
service_worker_version_id, thread_id);
} }
void ExtensionServiceWorkerMessageFilter::OnDidStopServiceWorkerContext( void ExtensionServiceWorkerMessageFilter::OnDidStopServiceWorkerContext(
const std::string& extension_id, const std::string& extension_id,
int64_t service_worker_version_id) { const GURL& service_worker_scope,
int64_t service_worker_version_id,
int thread_id) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK_NE(kMainThreadId, thread_id);
if (!ProcessMap::Get(browser_context_) if (!ProcessMap::Get(browser_context_)
->Contains(extension_id, render_process_id_)) { ->Contains(extension_id, render_process_id_)) {
// We can legitimately get here if the extension was already unloaded. // We can legitimately get here if the extension was already unloaded.
return; return;
} }
CHECK(service_worker_scope.SchemeIs(kExtensionScheme) &&
extension_id == service_worker_scope.host_piece());
ServiceWorkerTaskQueue::Get(browser_context_) ServiceWorkerTaskQueue::Get(browser_context_)
->DidStopServiceWorkerContext(extension_id, service_worker_version_id); ->DidStopServiceWorkerContext(render_process_id_, extension_id,
service_worker_scope,
service_worker_version_id, thread_id);
} }
void ExtensionServiceWorkerMessageFilter::DidFailDecrementInflightEvent() { void ExtensionServiceWorkerMessageFilter::DidFailDecrementInflightEvent() {
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "extensions/common/extension_id.h" #include "extensions/common/extension_id.h"
class GURL;
struct ExtensionHostMsg_Request_Params; struct ExtensionHostMsg_Request_Params;
namespace content { namespace content {
...@@ -46,9 +47,13 @@ class ExtensionServiceWorkerMessageFilter ...@@ -46,9 +47,13 @@ class ExtensionServiceWorkerMessageFilter
const std::string& request_uuid); const std::string& request_uuid);
void OnEventAckWorker(int64_t service_worker_version_id, int event_id); void OnEventAckWorker(int64_t service_worker_version_id, int event_id);
void OnDidStartServiceWorkerContext(const ExtensionId& extension_id, void OnDidStartServiceWorkerContext(const ExtensionId& extension_id,
int64_t service_worker_version_id); const GURL& service_worker_scope,
int64_t service_worker_version_id,
int thread_id);
void OnDidStopServiceWorkerContext(const ExtensionId& extension_id, void OnDidStopServiceWorkerContext(const ExtensionId& extension_id,
int64_t service_worker_version_id); const GURL& service_worker_scope,
int64_t service_worker_version_id,
int thread_id);
void DidFailDecrementInflightEvent(); void DidFailDecrementInflightEvent();
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <map> #include <map>
#include <set> #include <set>
#include <vector>
#include "base/memory/weak_ptr.h" #include "base/memory/weak_ptr.h"
#include "base/version.h" #include "base/version.h"
...@@ -25,6 +26,10 @@ class Extension; ...@@ -25,6 +26,10 @@ class Extension;
class LazyContextId; class LazyContextId;
// TODO(lazyboy): Clean up queue when extension is unloaded/uninstalled. // TODO(lazyboy): Clean up queue when extension is unloaded/uninstalled.
// TODO(lazyboy): Describe the flow of a task in this queue: i.e. when
// a worker receives DidRegisterServiceWorker, DidStartWorkerForScope and
// DidStartServiceWorkerContext events and how queued tasks react to these
// events.
class ServiceWorkerTaskQueue : public KeyedService, class ServiceWorkerTaskQueue : public KeyedService,
public LazyContextTaskQueue { public LazyContextTaskQueue {
public: public:
...@@ -48,11 +53,19 @@ class ServiceWorkerTaskQueue : public KeyedService, ...@@ -48,11 +53,19 @@ class ServiceWorkerTaskQueue : public KeyedService,
void DeactivateExtension(const Extension* extension); void DeactivateExtension(const Extension* extension);
// Called once an extension Service Worker started running. // Called once an extension Service Worker started running.
void DidStartServiceWorkerContext(const ExtensionId& extension_id, // This can be thought as "loadstop", i.e. the global JS script of the worker
int64_t service_worker_version_id); // has completed executing.
void DidStartServiceWorkerContext(int render_process_id,
const ExtensionId& extension_id,
const GURL& service_worker_scope,
int64_t service_worker_version_id,
int thread_id);
// Called once an extension Service Worker was destroyed. // Called once an extension Service Worker was destroyed.
void DidStopServiceWorkerContext(const ExtensionId& extension_id, void DidStopServiceWorkerContext(int render_process_id,
int64_t service_worker_version_id); const ExtensionId& extension_id,
const GURL& service_worker_scope,
int64_t service_worker_version_id,
int thread_id);
class TestObserver { class TestObserver {
public: public:
...@@ -72,31 +85,24 @@ class ServiceWorkerTaskQueue : public KeyedService, ...@@ -72,31 +85,24 @@ class ServiceWorkerTaskQueue : public KeyedService,
static void SetObserverForTest(TestObserver* observer); static void SetObserverForTest(TestObserver* observer);
private: private:
struct TaskInfo;
static void DidStartWorkerForScopeOnIO( static void DidStartWorkerForScopeOnIO(
PendingTask task, const LazyContextId& context_id,
const ExtensionId& extension_id,
base::WeakPtr<ServiceWorkerTaskQueue> task_queue, base::WeakPtr<ServiceWorkerTaskQueue> task_queue,
int64_t version_id, int64_t version_id,
int process_id, int process_id,
int thread_id); int thread_id);
static void StartServiceWorkerOnIOToRunTask( static void StartServiceWorkerOnIOToRunTasks(
base::WeakPtr<ServiceWorkerTaskQueue> task_queue_weak, base::WeakPtr<ServiceWorkerTaskQueue> task_queue_weak,
const GURL& scope, const LazyContextId& context_id,
const ExtensionId& extension_id, content::ServiceWorkerContext* service_worker_context);
content::ServiceWorkerContext* service_worker_context,
PendingTask task);
void RunTaskAfterStartWorker(const LazyContextId& context_id, void RunTasksAfterStartWorker(const LazyContextId& context_id);
PendingTask task);
void DidRegisterServiceWorker(const ExtensionId& extension_id, bool success); void DidRegisterServiceWorker(const ExtensionId& extension_id, bool success);
void DidUnregisterServiceWorker(const ExtensionId& extension_id, void DidUnregisterServiceWorker(const ExtensionId& extension_id,
bool success); bool success);
void DidStartWorkerForScope(PendingTask task, void DidStartWorkerForScope(const LazyContextId& context_id,
const ExtensionId& extension_id,
int64_t version_id, int64_t version_id,
int process_id, int process_id,
int thread_id); int thread_id);
...@@ -116,22 +122,26 @@ class ServiceWorkerTaskQueue : public KeyedService, ...@@ -116,22 +122,26 @@ class ServiceWorkerTaskQueue : public KeyedService,
// |extension_id|. // |extension_id|.
void RemoveRegisteredServiceWorkerInfo(const ExtensionId& extension_id); void RemoveRegisteredServiceWorkerInfo(const ExtensionId& extension_id);
// 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);
// Set of extension ids that hasn't completed Service Worker registration. // Set of extension ids that hasn't completed Service Worker registration.
std::set<ExtensionId> pending_registrations_; std::set<ExtensionId> pending_registrations_;
// Map of extension id -> pending tasks. These are run once the Service Worker // Set of workers that has seen DidStartWorkerForScope.
// registration of the extension completes. std::set<LazyContextId> started_workers_;
std::map<ExtensionId, std::vector<TaskInfo>> pending_tasks_;
struct WaitingDidStartWorkerTask; // Set of workers that has seen DidStartServiceWorkerContext.
std::set<LazyContextId> loaded_workers_;
using WaitingDidStartWorkerTaskKey = // Pending tasks for a |LazyContextId|.
std::pair<ExtensionId, int64_t /* service_worker_version_id */>; // These tasks will be run once the corresponding worker becomes ready.
// All service workers that are currently loaded. std::map<LazyContextId, std::vector<PendingTask>> pending_tasks_;
std::set<WaitingDidStartWorkerTaskKey> running_service_worker_contexts_;
// All service worker tasks that are waiting for the worker to start.
std::map<WaitingDidStartWorkerTaskKey, std::vector<WaitingDidStartWorkerTask>>
waiting_did_start_worker_tasks_;
content::BrowserContext* const browser_context_ = nullptr; content::BrowserContext* const browser_context_ = nullptr;
......
...@@ -1058,15 +1058,19 @@ IPC_MESSAGE_CONTROL2(ExtensionHostMsg_EventAckWorker, ...@@ -1058,15 +1058,19 @@ IPC_MESSAGE_CONTROL2(ExtensionHostMsg_EventAckWorker,
// straightforward as it changes SW IPC ordering with respect of rest of // straightforward as it changes SW IPC ordering with respect of rest of
// Chrome. // Chrome.
// See https://crbug.com/879015#c4 for details. // See https://crbug.com/879015#c4 for details.
IPC_MESSAGE_CONTROL2(ExtensionHostMsg_DidStartServiceWorkerContext, IPC_MESSAGE_CONTROL4(ExtensionHostMsg_DidStartServiceWorkerContext,
std::string /* extension_id */, std::string /* extension_id */,
int64_t /* service_worker_version_id */) 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 // Tells the browser that an extension service worker context has been
// destroyed. // destroyed.
IPC_MESSAGE_CONTROL2(ExtensionHostMsg_DidStopServiceWorkerContext, IPC_MESSAGE_CONTROL4(ExtensionHostMsg_DidStopServiceWorkerContext,
std::string /* extension_id */, std::string /* extension_id */,
int64_t /* service_worker_version_id */) GURL /* service_worker_scope */,
int64_t /* service_worker_version_id */,
int /* worker_thread_id */)
IPC_STRUCT_TRAITS_BEGIN(ui::AXNodeData) IPC_STRUCT_TRAITS_BEGIN(ui::AXNodeData)
IPC_STRUCT_TRAITS_MEMBER(id) IPC_STRUCT_TRAITS_MEMBER(id)
......
...@@ -498,7 +498,8 @@ void Dispatcher::DidStartServiceWorkerContextOnWorkerThread( ...@@ -498,7 +498,8 @@ void Dispatcher::DidStartServiceWorkerContextOnWorkerThread(
return; return;
DCHECK_NE(content::WorkerThread::GetCurrentId(), kMainThreadId); DCHECK_NE(content::WorkerThread::GetCurrentId(), kMainThreadId);
WorkerThreadDispatcher::Get()->DidStartContext(service_worker_version_id); WorkerThreadDispatcher::Get()->DidStartContext(service_worker_scope,
service_worker_version_id);
} }
// static // static
...@@ -519,7 +520,8 @@ void Dispatcher::WillDestroyServiceWorkerContextOnWorkerThread( ...@@ -519,7 +520,8 @@ void Dispatcher::WillDestroyServiceWorkerContextOnWorkerThread(
ExtensionBindingsSystem* worker_bindings_system = ExtensionBindingsSystem* worker_bindings_system =
WorkerThreadDispatcher::GetBindingsSystem(); WorkerThreadDispatcher::GetBindingsSystem();
worker_bindings_system->WillReleaseScriptContext(script_context); worker_bindings_system->WillReleaseScriptContext(script_context);
WorkerThreadDispatcher::Get()->DidStopContext(service_worker_version_id); WorkerThreadDispatcher::Get()->DidStopContext(service_worker_scope,
service_worker_version_id);
// Note: we have to remove the context (and thus perform invalidation on // Note: we have to remove the context (and thus perform invalidation on
// the native handlers) prior to removing the worker data, which destroys // the native handlers) prior to removing the worker data, which destroys
// the associated bindings system. // the associated bindings system.
......
...@@ -169,18 +169,26 @@ void WorkerThreadDispatcher::AddWorkerData( ...@@ -169,18 +169,26 @@ void WorkerThreadDispatcher::AddWorkerData(
} }
void WorkerThreadDispatcher::DidStartContext( void WorkerThreadDispatcher::DidStartContext(
const GURL& service_worker_scope,
int64_t service_worker_version_id) { int64_t service_worker_version_id) {
ServiceWorkerData* data = g_data_tls.Pointer()->Get(); ServiceWorkerData* data = g_data_tls.Pointer()->Get();
DCHECK_EQ(service_worker_version_id, data->service_worker_version_id()); DCHECK_EQ(service_worker_version_id, data->service_worker_version_id());
const int thread_id = content::WorkerThread::GetCurrentId();
DCHECK_NE(thread_id, kMainThreadId);
Send(new ExtensionHostMsg_DidStartServiceWorkerContext( Send(new ExtensionHostMsg_DidStartServiceWorkerContext(
data->context()->GetExtensionID(), service_worker_version_id)); data->context()->GetExtensionID(), service_worker_scope,
service_worker_version_id, thread_id));
} }
void WorkerThreadDispatcher::DidStopContext(int64_t service_worker_version_id) { void WorkerThreadDispatcher::DidStopContext(const GURL& service_worker_scope,
int64_t service_worker_version_id) {
ServiceWorkerData* data = g_data_tls.Pointer()->Get(); ServiceWorkerData* data = g_data_tls.Pointer()->Get();
const int thread_id = content::WorkerThread::GetCurrentId();
DCHECK_NE(thread_id, kMainThreadId);
DCHECK_EQ(service_worker_version_id, data->service_worker_version_id()); DCHECK_EQ(service_worker_version_id, data->service_worker_version_id());
Send(new ExtensionHostMsg_DidStopServiceWorkerContext( Send(new ExtensionHostMsg_DidStopServiceWorkerContext(
data->context()->GetExtensionID(), service_worker_version_id)); data->context()->GetExtensionID(), service_worker_scope,
service_worker_version_id, thread_id));
} }
void WorkerThreadDispatcher::RemoveWorkerData( void WorkerThreadDispatcher::RemoveWorkerData(
......
...@@ -57,9 +57,11 @@ class WorkerThreadDispatcher : public content::RenderThreadObserver, ...@@ -57,9 +57,11 @@ class WorkerThreadDispatcher : public content::RenderThreadObserver,
EventBookkeeper* event_bookkeeper() { return &event_bookkeeper_; } EventBookkeeper* event_bookkeeper() { return &event_bookkeeper_; }
// Called when a service worker context started running. // Called when a service worker context started running.
void DidStartContext(int64_t service_worker_version_id); void DidStartContext(const GURL& service_worker_scope,
int64_t service_worker_version_id);
// Called when a service worker context was destroyed. // Called when a service worker context was destroyed.
void DidStopContext(int64_t service_worker_version_id); void DidStopContext(const GURL& service_worker_scope,
int64_t service_worker_version_id);
// content::RenderThreadObserver: // content::RenderThreadObserver:
bool OnControlMessageReceived(const IPC::Message& message) override; bool OnControlMessageReceived(const IPC::Message& message) override;
......
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