diff options
author | jyasskin@chromium.org <jyasskin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-29 10:15:12 +0000 |
---|---|---|
committer | jyasskin@chromium.org <jyasskin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-29 10:15:12 +0000 |
commit | f2ccbae41c71d3982c9134a74b4d717d9226f9ca (patch) | |
tree | a0a6992cb6e2ce8c6b7c4b526c695ba0b735a605 /content/browser/service_worker | |
parent | ddaee297f7442191af336357628585b750b5ff03 (diff) | |
download | chromium_src-f2ccbae41c71d3982c9134a74b4d717d9226f9ca.zip chromium_src-f2ccbae41c71d3982c9134a74b4d717d9226f9ca.tar.gz chromium_src-f2ccbae41c71d3982c9134a74b4d717d9226f9ca.tar.bz2 |
Teach EmbeddedWorkerInstance to create a process when it needs one.
This saves the BrowserContext into the ServiceWorkerContextWrapper, and uses it when no process is available. We always choose a renderer process on the UI thread and take a reference to it before asking the renderer to start the SW thread. This also fixes a race where a process might die before we have a chance to use it.
We'll probably need to thread more process IDs through in cases where there might not be a ProviderHost, but that can wait for a subsequent CL.
BUG=359811,362060, 362058
Review URL: https://codereview.chromium.org/238043002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@266825 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/browser/service_worker')
29 files changed, 767 insertions, 260 deletions
diff --git a/content/browser/service_worker/embedded_worker_instance.cc b/content/browser/service_worker/embedded_worker_instance.cc index 6ee4356..8bad509 100644 --- a/content/browser/service_worker/embedded_worker_instance.cc +++ b/content/browser/service_worker/embedded_worker_instance.cc @@ -11,29 +11,34 @@ namespace content { +namespace { +// Functor to sort by the .second element of a struct. +struct SecondGreater { + template <typename Value> + bool operator()(const Value& lhs, const Value& rhs) { + return lhs.second > rhs.second; + } +}; +} // namespace + EmbeddedWorkerInstance::~EmbeddedWorkerInstance() { registry_->RemoveWorker(process_id_, embedded_worker_id_); } -ServiceWorkerStatusCode EmbeddedWorkerInstance::Start( - int64 service_worker_version_id, - const GURL& scope, - const GURL& script_url) { +void EmbeddedWorkerInstance::Start(int64 service_worker_version_id, + const GURL& scope, + const GURL& script_url, + const std::vector<int>& possible_process_ids, + const StatusCallback& callback) { DCHECK(status_ == STOPPED); - if (!ChooseProcess()) - return SERVICE_WORKER_ERROR_PROCESS_NOT_FOUND; status_ = STARTING; - ServiceWorkerStatusCode status = - registry_->StartWorker(process_id_, - embedded_worker_id_, - service_worker_version_id, - scope, - script_url); - if (status != SERVICE_WORKER_OK) { - status_ = STOPPED; - process_id_ = -1; - } - return status; + std::vector<int> ordered_process_ids = SortProcesses(possible_process_ids); + registry_->StartWorker(ordered_process_ids, + embedded_worker_id_, + service_worker_version_id, + scope, + script_url, + callback); } ServiceWorkerStatusCode EmbeddedWorkerInstance::Stop() { @@ -80,6 +85,16 @@ EmbeddedWorkerInstance::EmbeddedWorkerInstance( thread_id_(-1) { } +void EmbeddedWorkerInstance::RecordProcessId(int process_id, + ServiceWorkerStatusCode status) { + DCHECK_EQ(process_id_, -1); + if (status == SERVICE_WORKER_OK) { + process_id_ = process_id; + } else { + status_ = STOPPED; + } +} + void EmbeddedWorkerInstance::OnStarted(int thread_id) { // Stop is requested before OnStarted is sent back from the worker. if (status_ == STOPPING) @@ -138,21 +153,26 @@ void EmbeddedWorkerInstance::RemoveListener(Listener* listener) { listener_list_.RemoveObserver(listener); } -bool EmbeddedWorkerInstance::ChooseProcess() { - DCHECK_EQ(-1, process_id_); - // Naive implementation; chooses a process which has the biggest number of - // associated providers (so that hopefully likely live longer). - ProcessRefMap::iterator max_ref_iter = process_refs_.end(); - for (ProcessRefMap::iterator iter = process_refs_.begin(); - iter != process_refs_.end(); ++iter) { - if (max_ref_iter == process_refs_.end() || - max_ref_iter->second < iter->second) - max_ref_iter = iter; +std::vector<int> EmbeddedWorkerInstance::SortProcesses( + const std::vector<int>& possible_process_ids) const { + // Add the |possible_process_ids| to the existing process_refs_ since each one + // is likely to take a reference once the SW starts up. + ProcessRefMap refs_with_new_ids = process_refs_; + for (std::vector<int>::const_iterator it = possible_process_ids.begin(); + it != possible_process_ids.end(); + ++it) { + refs_with_new_ids[*it]++; } - if (max_ref_iter == process_refs_.end()) - return false; - process_id_ = max_ref_iter->first; - return true; + + std::vector<std::pair<int, int> > counted(refs_with_new_ids.begin(), + refs_with_new_ids.end()); + // Sort descending by the reference count. + std::sort(counted.begin(), counted.end(), SecondGreater()); + + std::vector<int> result(counted.size()); + for (size_t i = 0; i < counted.size(); ++i) + result[i] = counted[i].first; + return result; } } // namespace content diff --git a/content/browser/service_worker/embedded_worker_instance.h b/content/browser/service_worker/embedded_worker_instance.h index b57bc8f..c4ed7fc 100644 --- a/content/browser/service_worker/embedded_worker_instance.h +++ b/content/browser/service_worker/embedded_worker_instance.h @@ -6,6 +6,7 @@ #define CONTENT_BROWSER_SERVICE_WORKER_EMBEDDED_WORKER_INSTANCE_H_ #include <map> +#include <vector> #include "base/basictypes.h" #include "base/callback_forward.h" @@ -33,6 +34,7 @@ struct ServiceWorkerFetchRequest; // AddProcessReference(). class CONTENT_EXPORT EmbeddedWorkerInstance { public: + typedef base::Callback<void(ServiceWorkerStatusCode)> StatusCallback; enum Status { STOPPED, STARTING, @@ -62,11 +64,15 @@ class CONTENT_EXPORT EmbeddedWorkerInstance { ~EmbeddedWorkerInstance(); - // Starts the worker. It is invalid to call this when the worker is - // not in STOPPED status. - ServiceWorkerStatusCode Start(int64 service_worker_version_id, - const GURL& scope, - const GURL& script_url); + // Starts the worker. It is invalid to call this when the worker is not in + // STOPPED status. |callback| is invoked when the worker's process is created + // if necessary and the IPC to evaluate the worker's script is sent. + // Observer::OnStarted() is run when the worker is actually started. + void Start(int64 service_worker_version_id, + const GURL& scope, + const GURL& script_url, + const std::vector<int>& possible_process_ids, + const StatusCallback& callback); // Stops the worker. It is invalid to call this when the worker is // not in STARTING or RUNNING status. @@ -105,6 +111,10 @@ class CONTENT_EXPORT EmbeddedWorkerInstance { EmbeddedWorkerInstance(EmbeddedWorkerRegistry* registry, int embedded_worker_id); + // Called back from EmbeddedWorkerRegistry after Start() passes control to the + // UI thread to acquire a reference to the process. + void RecordProcessId(int process_id, ServiceWorkerStatusCode status); + // Called back from Registry when the worker instance has ack'ed that // its WorkerGlobalScope is actually started on |thread_id| in the // child process. @@ -135,9 +145,10 @@ class CONTENT_EXPORT EmbeddedWorkerInstance { int line_number, const GURL& source_url); - // Chooses a process to start this worker and populate process_id_. - // Returns false when no process is available. - bool ChooseProcess(); + // Chooses a list of processes to try to start this worker in, ordered by how + // many clients are currently in those processes. + std::vector<int> SortProcesses( + const std::vector<int>& possible_process_ids) const; scoped_refptr<EmbeddedWorkerRegistry> registry_; const int embedded_worker_id_; diff --git a/content/browser/service_worker/embedded_worker_instance_unittest.cc b/content/browser/service_worker/embedded_worker_instance_unittest.cc index f78ba81..244b5f9 100644 --- a/content/browser/service_worker/embedded_worker_instance_unittest.cc +++ b/content/browser/service_worker/embedded_worker_instance_unittest.cc @@ -9,6 +9,7 @@ #include "content/browser/service_worker/embedded_worker_registry.h" #include "content/browser/service_worker/embedded_worker_test_helper.h" #include "content/browser/service_worker/service_worker_context_core.h" +#include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/common/service_worker/embedded_worker_messages.h" #include "content/public/test/test_browser_thread_bundle.h" #include "testing/gtest/include/gtest/gtest.h" @@ -23,30 +24,35 @@ class EmbeddedWorkerInstanceTest : public testing::Test { : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {} virtual void SetUp() OVERRIDE { - context_.reset(new ServiceWorkerContextCore(base::FilePath(), NULL, NULL)); - helper_.reset(new EmbeddedWorkerTestHelper( - context_.get(), kRenderProcessId)); + helper_.reset(new EmbeddedWorkerTestHelper(kRenderProcessId)); } virtual void TearDown() OVERRIDE { helper_.reset(); - context_.reset(); } + ServiceWorkerContextCore* context() { return helper_->context(); } + EmbeddedWorkerRegistry* embedded_worker_registry() { - DCHECK(context_); - return context_->embedded_worker_registry(); + DCHECK(context()); + return context()->embedded_worker_registry(); } IPC::TestSink* ipc_sink() { return helper_->ipc_sink(); } TestBrowserThreadBundle thread_bundle_; - scoped_ptr<ServiceWorkerContextCore> context_; scoped_ptr<EmbeddedWorkerTestHelper> helper_; DISALLOW_COPY_AND_ASSIGN(EmbeddedWorkerInstanceTest); }; +static void SaveStatusAndCall(ServiceWorkerStatusCode* out, + const base::Closure& callback, + ServiceWorkerStatusCode status) { + *out = status; + callback.Run(); +} + TEST_F(EmbeddedWorkerInstanceTest, StartAndStop) { scoped_ptr<EmbeddedWorkerInstance> worker = embedded_worker_registry()->CreateWorker(); @@ -57,17 +63,20 @@ TEST_F(EmbeddedWorkerInstanceTest, StartAndStop) { const GURL scope("http://example.com/*"); const GURL url("http://example.com/worker.js"); - // This fails as we have no available process yet. - EXPECT_EQ(SERVICE_WORKER_ERROR_PROCESS_NOT_FOUND, - worker->Start(service_worker_version_id, scope, url)); - EXPECT_EQ(EmbeddedWorkerInstance::STOPPED, worker->status()); - // Simulate adding one process to the worker. helper_->SimulateAddProcessToWorker(embedded_worker_id, kRenderProcessId); // Start should succeed. - EXPECT_EQ(SERVICE_WORKER_OK, - worker->Start(service_worker_version_id, scope, url)); + ServiceWorkerStatusCode status; + base::RunLoop run_loop; + worker->Start( + service_worker_version_id, + scope, + url, + std::vector<int>(), + base::Bind(&SaveStatusAndCall, &status, run_loop.QuitClosure())); + run_loop.Run(); + EXPECT_EQ(SERVICE_WORKER_OK, status); EXPECT_EQ(EmbeddedWorkerInstance::STARTING, worker->status()); base::RunLoop().RunUntilIdle(); @@ -90,6 +99,36 @@ TEST_F(EmbeddedWorkerInstanceTest, StartAndStop) { EmbeddedWorkerMsg_StopWorker::ID)); } +TEST_F(EmbeddedWorkerInstanceTest, InstanceDestroyedBeforeStartFinishes) { + scoped_ptr<EmbeddedWorkerInstance> worker = + embedded_worker_registry()->CreateWorker(); + EXPECT_EQ(EmbeddedWorkerInstance::STOPPED, worker->status()); + + const int64 service_worker_version_id = 55L; + const GURL scope("http://example.com/*"); + const GURL url("http://example.com/worker.js"); + + ServiceWorkerStatusCode status; + base::RunLoop run_loop; + // Begin starting the worker. + std::vector<int> available_process; + available_process.push_back(kRenderProcessId); + worker->Start( + service_worker_version_id, + scope, + url, + available_process, + base::Bind(&SaveStatusAndCall, &status, run_loop.QuitClosure())); + // But destroy it before it gets a chance to complete. + worker.reset(); + run_loop.Run(); + EXPECT_EQ(SERVICE_WORKER_ERROR_ABORT, status); + + // Verify that we didn't send the message to start the worker. + ASSERT_FALSE( + ipc_sink()->GetUniqueMessageMatching(EmbeddedWorkerMsg_StartWorker::ID)); +} + TEST_F(EmbeddedWorkerInstanceTest, ChooseProcess) { scoped_ptr<EmbeddedWorkerInstance> worker = embedded_worker_registry()->CreateWorker(); @@ -106,10 +145,16 @@ TEST_F(EmbeddedWorkerInstanceTest, ChooseProcess) { helper_->SimulateAddProcessToWorker(embedded_worker_id, 3); // Process 3 has the biggest # of references and it should be chosen. - EXPECT_EQ(SERVICE_WORKER_OK, - worker->Start(1L, - GURL("http://example.com/*"), - GURL("http://example.com/worker.js"))); + ServiceWorkerStatusCode status; + base::RunLoop run_loop; + worker->Start( + 1L, + GURL("http://example.com/*"), + GURL("http://example.com/worker.js"), + std::vector<int>(), + base::Bind(&SaveStatusAndCall, &status, run_loop.QuitClosure())); + run_loop.Run(); + EXPECT_EQ(SERVICE_WORKER_OK, status) << ServiceWorkerStatusToString(status); EXPECT_EQ(EmbeddedWorkerInstance::STARTING, worker->status()); EXPECT_EQ(3, worker->process_id()); diff --git a/content/browser/service_worker/embedded_worker_registry.cc b/content/browser/service_worker/embedded_worker_registry.cc index 249eab0..d10f553 100644 --- a/content/browser/service_worker/embedded_worker_registry.cc +++ b/content/browser/service_worker/embedded_worker_registry.cc @@ -4,10 +4,13 @@ #include "content/browser/service_worker/embedded_worker_registry.h" +#include "base/bind_helpers.h" #include "base/stl_util.h" #include "content/browser/service_worker/embedded_worker_instance.h" #include "content/browser/service_worker/service_worker_context_core.h" +#include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/common/service_worker/embedded_worker_messages.h" +#include "content/public/browser/browser_thread.h" #include "ipc/ipc_message.h" #include "ipc/ipc_sender.h" @@ -25,20 +28,34 @@ scoped_ptr<EmbeddedWorkerInstance> EmbeddedWorkerRegistry::CreateWorker() { return worker.Pass(); } -ServiceWorkerStatusCode EmbeddedWorkerRegistry::StartWorker( - int process_id, - int embedded_worker_id, - int64 service_worker_version_id, - const GURL& scope, - const GURL& script_url) { - return Send( - process_id, - new EmbeddedWorkerMsg_StartWorker( - embedded_worker_id, service_worker_version_id, scope, script_url)); +void EmbeddedWorkerRegistry::StartWorker(const std::vector<int>& process_ids, + int embedded_worker_id, + int64 service_worker_version_id, + const GURL& scope, + const GURL& script_url, + const StatusCallback& callback) { + if (!context_) { + callback.Run(SERVICE_WORKER_ERROR_ABORT); + return; + } + context_->process_manager()->AllocateWorkerProcess( + process_ids, + script_url, + base::Bind(&EmbeddedWorkerRegistry::StartWorkerWithProcessId, + this, + embedded_worker_id, + base::Passed(make_scoped_ptr(new EmbeddedWorkerMsg_StartWorker( + embedded_worker_id, + service_worker_version_id, + scope, + script_url))), + callback)); } ServiceWorkerStatusCode EmbeddedWorkerRegistry::StopWorker( int process_id, int embedded_worker_id) { + if (context_) + context_->process_manager()->ReleaseWorkerProcess(process_id); return Send(process_id, new EmbeddedWorkerMsg_StopWorker(embedded_worker_id)); } @@ -55,6 +72,14 @@ bool EmbeddedWorkerRegistry::OnMessageReceived(const IPC::Message& message) { return found->second->OnMessageReceived(message); } +void EmbeddedWorkerRegistry::Shutdown() { + for (WorkerInstanceMap::iterator it = worker_map_.begin(); + it != worker_map_.end(); + ++it) { + it->second->Stop(); + } +} + void EmbeddedWorkerRegistry::OnWorkerStarted( int process_id, int thread_id, int embedded_worker_id) { DCHECK(!ContainsKey(worker_process_map_, process_id) || @@ -143,7 +168,38 @@ EmbeddedWorkerInstance* EmbeddedWorkerRegistry::GetWorker( return found->second; } -EmbeddedWorkerRegistry::~EmbeddedWorkerRegistry() {} +EmbeddedWorkerRegistry::~EmbeddedWorkerRegistry() { + Shutdown(); +} + +void EmbeddedWorkerRegistry::StartWorkerWithProcessId( + int embedded_worker_id, + scoped_ptr<EmbeddedWorkerMsg_StartWorker> message, + const StatusCallback& callback, + ServiceWorkerStatusCode status, + int process_id) { + WorkerInstanceMap::const_iterator worker = + worker_map_.find(embedded_worker_id); + if (worker == worker_map_.end()) { + // The Instance was destroyed before it could finish starting. Undo what + // we've done so far. + if (context_) + context_->process_manager()->ReleaseWorkerProcess(process_id); + callback.Run(SERVICE_WORKER_ERROR_ABORT); + return; + } + worker->second->RecordProcessId(process_id, status); + + if (status != SERVICE_WORKER_OK) { + callback.Run(status); + return; + } + // The ServiceWorkerDispatcherHost is supposed to be created when the process + // is created, and keep an entry in process_sender_map_ for its whole + // lifetime. + DCHECK(ContainsKey(process_sender_map_, process_id)); + callback.Run(Send(process_id, message.release())); +} ServiceWorkerStatusCode EmbeddedWorkerRegistry::Send( int process_id, IPC::Message* message) { diff --git a/content/browser/service_worker/embedded_worker_registry.h b/content/browser/service_worker/embedded_worker_registry.h index 8350f82..5c12111 100644 --- a/content/browser/service_worker/embedded_worker_registry.h +++ b/content/browser/service_worker/embedded_worker_registry.h @@ -7,6 +7,7 @@ #include <map> #include <set> +#include <vector> #include "base/basictypes.h" #include "base/memory/ref_counted.h" @@ -16,6 +17,7 @@ #include "content/common/content_export.h" #include "content/common/service_worker/service_worker_status_code.h" +class EmbeddedWorkerMsg_StartWorker; class GURL; namespace IPC { @@ -36,6 +38,8 @@ class ServiceWorkerContextCore; class CONTENT_EXPORT EmbeddedWorkerRegistry : public NON_EXPORTED_BASE(base::RefCounted<EmbeddedWorkerRegistry>) { public: + typedef base::Callback<void(ServiceWorkerStatusCode)> StatusCallback; + explicit EmbeddedWorkerRegistry( base::WeakPtr<ServiceWorkerContextCore> context); @@ -46,14 +50,18 @@ class CONTENT_EXPORT EmbeddedWorkerRegistry scoped_ptr<EmbeddedWorkerInstance> CreateWorker(); // Called from EmbeddedWorkerInstance, relayed to the child process. - ServiceWorkerStatusCode StartWorker(int process_id, - int embedded_worker_id, - int64 service_worker_version_id, - const GURL& scope, - const GURL& script_url); + void StartWorker(const std::vector<int>& process_ids, + int embedded_worker_id, + int64 service_worker_version_id, + const GURL& scope, + const GURL& script_url, + const StatusCallback& callback); ServiceWorkerStatusCode StopWorker(int process_id, int embedded_worker_id); + // Stop all active workers, even if they're handling events. + void Shutdown(); + // Called back from EmbeddedWorker in the child process, relayed via // ServiceWorkerDispatcherHost. void OnWorkerStarted(int process_id, int thread_id, int embedded_worker_id); @@ -85,6 +93,14 @@ class CONTENT_EXPORT EmbeddedWorkerRegistry typedef std::map<int, IPC::Sender*> ProcessToSenderMap; ~EmbeddedWorkerRegistry(); + + void StartWorkerWithProcessId( + int embedded_worker_id, + scoped_ptr<EmbeddedWorkerMsg_StartWorker> message, + const StatusCallback& callback, + ServiceWorkerStatusCode status, + int process_id); + ServiceWorkerStatusCode Send(int process_id, IPC::Message* message); // RemoveWorker is called when EmbeddedWorkerInstance is destructed. diff --git a/content/browser/service_worker/embedded_worker_test_helper.cc b/content/browser/service_worker/embedded_worker_test_helper.cc index 67ca05a..7501f5a 100644 --- a/content/browser/service_worker/embedded_worker_test_helper.cc +++ b/content/browser/service_worker/embedded_worker_test_helper.cc @@ -8,22 +8,33 @@ #include "content/browser/service_worker/embedded_worker_instance.h" #include "content/browser/service_worker/embedded_worker_registry.h" #include "content/browser/service_worker/service_worker_context_core.h" +#include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/common/service_worker/embedded_worker_messages.h" #include "content/common/service_worker/service_worker_messages.h" #include "testing/gtest/include/gtest/gtest.h" namespace content { -EmbeddedWorkerTestHelper::EmbeddedWorkerTestHelper( - ServiceWorkerContextCore* context, - int mock_render_process_id) - : context_(context->AsWeakPtr()), +static bool AlwaysTrue(int process_id) { + return true; +} + +EmbeddedWorkerTestHelper::EmbeddedWorkerTestHelper(int mock_render_process_id) + : wrapper_(new ServiceWorkerContextWrapper(NULL)), next_thread_id_(0), weak_factory_(this) { + wrapper_->Init(base::FilePath(), NULL); + scoped_ptr<ServiceWorkerProcessManager> process_manager( + new ServiceWorkerProcessManager(wrapper_)); + process_manager->SetProcessRefcountOpsForTest(base::Bind(AlwaysTrue), + base::Bind(AlwaysTrue)); + wrapper_->context()->SetProcessManagerForTest(process_manager.Pass()); registry()->AddChildProcessSender(mock_render_process_id, this); } EmbeddedWorkerTestHelper::~EmbeddedWorkerTestHelper() { + if (wrapper_) + wrapper_->Shutdown(); } void EmbeddedWorkerTestHelper::SimulateAddProcessToWorker( @@ -58,6 +69,15 @@ bool EmbeddedWorkerTestHelper::OnMessageReceived(const IPC::Message& message) { return handled; } +ServiceWorkerContextCore* EmbeddedWorkerTestHelper::context() { + return wrapper_->context(); +} + +void EmbeddedWorkerTestHelper::ShutdownContext() { + wrapper_->Shutdown(); + wrapper_ = NULL; +} + void EmbeddedWorkerTestHelper::OnStartWorker( int embedded_worker_id, int64 service_worker_version_id, @@ -219,8 +239,8 @@ void EmbeddedWorkerTestHelper::OnFetchEventStub( } EmbeddedWorkerRegistry* EmbeddedWorkerTestHelper::registry() { - DCHECK(context_); - return context_->embedded_worker_registry(); + DCHECK(context()); + return context()->embedded_worker_registry(); } } // namespace content diff --git a/content/browser/service_worker/embedded_worker_test_helper.h b/content/browser/service_worker/embedded_worker_test_helper.h index af95f675..27a0416 100644 --- a/content/browser/service_worker/embedded_worker_test_helper.h +++ b/content/browser/service_worker/embedded_worker_test_helper.h @@ -21,12 +21,14 @@ namespace content { class EmbeddedWorkerRegistry; class EmbeddedWorkerTestHelper; class ServiceWorkerContextCore; +class ServiceWorkerContextWrapper; struct ServiceWorkerFetchRequest; // In-Process EmbeddedWorker test helper. // -// Usage: create an instance of this class for a ServiceWorkerContextCore -// to test browser-side embedded worker code without creating a child process. +// Usage: create an instance of this class to test browser-side embedded worker +// code without creating a child process. This class will create a +// ServiceWorkerContextWrapper and ServiceWorkerContextCore for you. // // By default this class just notifies back WorkerStarted and WorkerStopped // for StartWorker and StopWorker requests. The default implementation @@ -42,8 +44,7 @@ class EmbeddedWorkerTestHelper : public IPC::Sender, public: // Initialize this helper for |context|, and enable this as an IPC // sender for |mock_render_process_id|. - EmbeddedWorkerTestHelper(ServiceWorkerContextCore* context, - int mock_render_process_id); + EmbeddedWorkerTestHelper(int mock_render_process_id); virtual ~EmbeddedWorkerTestHelper(); // Call this to simulate add/associate a process to a worker. @@ -61,6 +62,10 @@ class EmbeddedWorkerTestHelper : public IPC::Sender, // Inner IPC sink for script context messages sent via EmbeddedWorker. IPC::TestSink* inner_ipc_sink() { return &inner_sink_; } + ServiceWorkerContextCore* context(); + ServiceWorkerContextWrapper* context_wrapper() { return wrapper_.get(); } + void ShutdownContext(); + protected: // Called when StartWorker, StopWorker and SendMessageToWorker message // is sent to the embedded worker. Override if necessary. By default @@ -112,7 +117,7 @@ class EmbeddedWorkerTestHelper : public IPC::Sender, void OnFetchEventStub(int request_id, const ServiceWorkerFetchRequest& request); - base::WeakPtr<ServiceWorkerContextCore> context_; + scoped_refptr<ServiceWorkerContextWrapper> wrapper_; IPC::TestSink sink_; IPC::TestSink inner_sink_; diff --git a/content/browser/service_worker/service_worker_browsertest.cc b/content/browser/service_worker/service_worker_browsertest.cc index 60a9849..6ab1e7d 100644 --- a/content/browser/service_worker/service_worker_browsertest.cc +++ b/content/browser/service_worker/service_worker_browsertest.cc @@ -164,9 +164,17 @@ class EmbeddedWorkerBrowserTest : public ServiceWorkerBrowserTest, const GURL scope = embedded_test_server()->GetURL("/*"); const GURL script_url = embedded_test_server()->GetURL( "/service_worker/worker.js"); - ServiceWorkerStatusCode status = worker_->Start( - service_worker_version_id, scope, script_url); - + std::vector<int> processes; + processes.push_back( + shell()->web_contents()->GetRenderProcessHost()->GetID()); + worker_->Start( + service_worker_version_id, + scope, + script_url, + processes, + base::Bind(&EmbeddedWorkerBrowserTest::StartOnIOThread2, this)); + } + void StartOnIOThread2(ServiceWorkerStatusCode status) { last_worker_status_ = worker_->status(); EXPECT_EQ(SERVICE_WORKER_OK, status); EXPECT_EQ(EmbeddedWorkerInstance::STARTING, last_worker_status_); @@ -596,7 +604,6 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerBlackBoxBrowserTest, Registration) { public_context()->RegisterServiceWorker( embedded_test_server()->GetURL("/*"), embedded_test_server()->GetURL(kWorkerUrl), - RenderProcessID(), base::Bind(&ServiceWorkerBlackBoxBrowserTest::ExpectResultAndRun, true, run_loop.QuitClosure())); @@ -610,7 +617,6 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerBlackBoxBrowserTest, Registration) { public_context()->RegisterServiceWorker( embedded_test_server()->GetURL("/*"), embedded_test_server()->GetURL(kWorkerUrl), - RenderProcessID(), base::Bind(&ServiceWorkerBlackBoxBrowserTest::ExpectResultAndRun, true, run_loop.QuitClosure())); diff --git a/content/browser/service_worker/service_worker_context_core.cc b/content/browser/service_worker/service_worker_context_core.cc index 9b8a17a..910b33c 100644 --- a/content/browser/service_worker/service_worker_context_core.cc +++ b/content/browser/service_worker/service_worker_context_core.cc @@ -8,8 +8,10 @@ #include "base/strings/string_util.h" #include "content/browser/service_worker/embedded_worker_registry.h" #include "content/browser/service_worker/service_worker_context_observer.h" +#include "content/browser/service_worker/service_worker_context_wrapper.h" #include "content/browser/service_worker/service_worker_info.h" #include "content/browser/service_worker/service_worker_job_coordinator.h" +#include "content/browser/service_worker/service_worker_process_manager.h" #include "content/browser/service_worker/service_worker_provider_host.h" #include "content/browser/service_worker/service_worker_register_job.h" #include "content/browser/service_worker/service_worker_registration.h" @@ -76,13 +78,16 @@ void ServiceWorkerContextCore::ProviderHostIterator::Initialize() { ServiceWorkerContextCore::ServiceWorkerContextCore( const base::FilePath& path, quota::QuotaManagerProxy* quota_manager_proxy, - ObserverListThreadSafe<ServiceWorkerContextObserver>* observer_list) - : storage_(new ServiceWorkerStorage( - path, AsWeakPtr(), quota_manager_proxy)), + ObserverListThreadSafe<ServiceWorkerContextObserver>* observer_list, + scoped_ptr<ServiceWorkerProcessManager> process_manager) + : storage_( + new ServiceWorkerStorage(path, AsWeakPtr(), quota_manager_proxy)), embedded_worker_registry_(new EmbeddedWorkerRegistry(AsWeakPtr())), job_coordinator_(new ServiceWorkerJobCoordinator(AsWeakPtr())), + process_manager_(process_manager.Pass()), next_handle_id_(0), - observer_list_(observer_list) {} + observer_list_(observer_list) { +} ServiceWorkerContextCore::~ServiceWorkerContextCore() { for (VersionMap::iterator it = live_versions_.begin(); diff --git a/content/browser/service_worker/service_worker_context_core.h b/content/browser/service_worker/service_worker_context_core.h index 2fae441..a8cdc41 100644 --- a/content/browser/service_worker/service_worker_context_core.h +++ b/content/browser/service_worker/service_worker_context_core.h @@ -15,6 +15,7 @@ #include "base/memory/weak_ptr.h" #include "base/observer_list_threadsafe.h" #include "content/browser/service_worker/service_worker_info.h" +#include "content/browser/service_worker/service_worker_process_manager.h" #include "content/browser/service_worker/service_worker_provider_host.h" #include "content/browser/service_worker/service_worker_registration_status.h" #include "content/browser/service_worker/service_worker_storage.h" @@ -86,7 +87,8 @@ class CONTENT_EXPORT ServiceWorkerContextCore ServiceWorkerContextCore( const base::FilePath& user_data_directory, quota::QuotaManagerProxy* quota_manager_proxy, - ObserverListThreadSafe<ServiceWorkerContextObserver>* observer_list); + ObserverListThreadSafe<ServiceWorkerContextObserver>* observer_list, + scoped_ptr<ServiceWorkerProcessManager> process_manager); virtual ~ServiceWorkerContextCore(); // ServiceWorkerVersion::Listener overrides. @@ -106,6 +108,9 @@ class CONTENT_EXPORT ServiceWorkerContextCore const GURL& source_url) OVERRIDE; ServiceWorkerStorage* storage() { return storage_.get(); } + ServiceWorkerProcessManager* process_manager() { + return process_manager_.get(); + } EmbeddedWorkerRegistry* embedded_worker_registry() { return embedded_worker_registry_.get(); } @@ -149,6 +154,12 @@ class CONTENT_EXPORT ServiceWorkerContextCore // Returns new context-local unique ID for ServiceWorkerHandle. int GetNewServiceWorkerHandleId(); + // Allows tests to change how processes are created. + void SetProcessManagerForTest( + scoped_ptr<ServiceWorkerProcessManager> new_process_manager) { + process_manager_ = new_process_manager.Pass(); + } + private: typedef std::map<int64, ServiceWorkerRegistration*> RegistrationsMap; typedef std::map<int64, ServiceWorkerVersion*> VersionMap; @@ -167,6 +178,7 @@ class CONTENT_EXPORT ServiceWorkerContextCore scoped_ptr<ServiceWorkerStorage> storage_; scoped_refptr<EmbeddedWorkerRegistry> embedded_worker_registry_; scoped_ptr<ServiceWorkerJobCoordinator> job_coordinator_; + scoped_ptr<ServiceWorkerProcessManager> process_manager_; std::map<int64, ServiceWorkerRegistration*> live_registrations_; std::map<int64, ServiceWorkerVersion*> live_versions_; int next_handle_id_; diff --git a/content/browser/service_worker/service_worker_context_unittest.cc b/content/browser/service_worker/service_worker_context_unittest.cc index 3c7d55f..00e9527 100644 --- a/content/browser/service_worker/service_worker_context_unittest.cc +++ b/content/browser/service_worker/service_worker_context_unittest.cc @@ -83,9 +83,8 @@ void ExpectRegisteredWorkers( class RejectInstallTestHelper : public EmbeddedWorkerTestHelper { public: - RejectInstallTestHelper(ServiceWorkerContextCore* context, - int mock_render_process_id) - : EmbeddedWorkerTestHelper(context, mock_render_process_id) {} + RejectInstallTestHelper(int mock_render_process_id) + : EmbeddedWorkerTestHelper(mock_render_process_id) {} virtual void OnInstallEvent(int embedded_worker_id, int request_id, @@ -99,9 +98,8 @@ class RejectInstallTestHelper : public EmbeddedWorkerTestHelper { class RejectActivateTestHelper : public EmbeddedWorkerTestHelper { public: - RejectActivateTestHelper(ServiceWorkerContextCore* context, - int mock_render_process_id) - : EmbeddedWorkerTestHelper(context, mock_render_process_id) {} + RejectActivateTestHelper(int mock_render_process_id) + : EmbeddedWorkerTestHelper(mock_render_process_id) {} virtual void OnActivateEvent(int embedded_worker_id, int request_id) OVERRIDE { @@ -121,19 +119,17 @@ class ServiceWorkerContextTest : public testing::Test { render_process_id_(99) {} virtual void SetUp() OVERRIDE { - context_.reset(new ServiceWorkerContextCore(base::FilePath(), NULL, NULL)); - helper_.reset(new EmbeddedWorkerTestHelper( - context_.get(), render_process_id_)); + helper_.reset(new EmbeddedWorkerTestHelper(render_process_id_)); } virtual void TearDown() OVERRIDE { helper_.reset(); - context_.reset(); } + ServiceWorkerContextCore* context() { return helper_->context(); } + protected: TestBrowserThreadBundle browser_thread_bundle_; - scoped_ptr<ServiceWorkerContextCore> context_; scoped_ptr<EmbeddedWorkerTestHelper> helper_; const int render_process_id_; }; @@ -143,7 +139,7 @@ TEST_F(ServiceWorkerContextTest, Register) { int64 registration_id = kInvalidServiceWorkerRegistrationId; int64 version_id = kInvalidServiceWorkerVersionId; bool called = false; - context_->RegisterServiceWorker( + context()->RegisterServiceWorker( GURL("http://www.example.com/*"), GURL("http://www.example.com/service_worker.js"), render_process_id_, @@ -162,7 +158,7 @@ TEST_F(ServiceWorkerContextTest, Register) { EXPECT_NE(kInvalidServiceWorkerRegistrationId, registration_id); EXPECT_NE(kInvalidServiceWorkerVersionId, version_id); - context_->storage()->FindRegistrationForId( + context()->storage()->FindRegistrationForId( registration_id, base::Bind(&ExpectRegisteredWorkers, SERVICE_WORKER_OK, @@ -176,12 +172,12 @@ TEST_F(ServiceWorkerContextTest, Register) { // registration callback should indicate success, but there should be no pending // or active worker in the registration. TEST_F(ServiceWorkerContextTest, Register_RejectInstall) { - helper_.reset( - new RejectInstallTestHelper(context_.get(), render_process_id_)); + helper_.reset(); // Make sure the process lookups stay overridden. + helper_.reset(new RejectInstallTestHelper(render_process_id_)); int64 registration_id = kInvalidServiceWorkerRegistrationId; int64 version_id = kInvalidServiceWorkerVersionId; bool called = false; - context_->RegisterServiceWorker( + context()->RegisterServiceWorker( GURL("http://www.example.com/*"), GURL("http://www.example.com/service_worker.js"), render_process_id_, @@ -200,7 +196,7 @@ TEST_F(ServiceWorkerContextTest, Register_RejectInstall) { EXPECT_NE(kInvalidServiceWorkerRegistrationId, registration_id); EXPECT_NE(kInvalidServiceWorkerVersionId, version_id); - context_->storage()->FindRegistrationForId( + context()->storage()->FindRegistrationForId( registration_id, base::Bind(&ExpectRegisteredWorkers, SERVICE_WORKER_ERROR_NOT_FOUND, @@ -214,12 +210,12 @@ TEST_F(ServiceWorkerContextTest, Register_RejectInstall) { // registration callback should indicate success, but there should be no pending // or active worker in the registration. TEST_F(ServiceWorkerContextTest, Register_RejectActivate) { - helper_.reset( - new RejectActivateTestHelper(context_.get(), render_process_id_)); + helper_.reset(); // Make sure the process lookups stay overridden. + helper_.reset(new RejectActivateTestHelper(render_process_id_)); int64 registration_id = kInvalidServiceWorkerRegistrationId; int64 version_id = kInvalidServiceWorkerVersionId; bool called = false; - context_->RegisterServiceWorker( + context()->RegisterServiceWorker( GURL("http://www.example.com/*"), GURL("http://www.example.com/service_worker.js"), render_process_id_, @@ -238,7 +234,7 @@ TEST_F(ServiceWorkerContextTest, Register_RejectActivate) { EXPECT_NE(kInvalidServiceWorkerRegistrationId, registration_id); EXPECT_NE(kInvalidServiceWorkerVersionId, version_id); - context_->storage()->FindRegistrationForId( + context()->storage()->FindRegistrationForId( registration_id, base::Bind(&ExpectRegisteredWorkers, SERVICE_WORKER_ERROR_NOT_FOUND, @@ -255,7 +251,7 @@ TEST_F(ServiceWorkerContextTest, Unregister) { bool called = false; int64 registration_id = kInvalidServiceWorkerRegistrationId; int64 version_id = kInvalidServiceWorkerVersionId; - context_->RegisterServiceWorker( + context()->RegisterServiceWorker( pattern, GURL("http://www.example.com/service_worker.js"), render_process_id_, @@ -269,14 +265,14 @@ TEST_F(ServiceWorkerContextTest, Unregister) { EXPECT_NE(kInvalidServiceWorkerVersionId, version_id); called = false; - context_->UnregisterServiceWorker( + context()->UnregisterServiceWorker( pattern, render_process_id_, NULL, MakeUnregisteredCallback(&called)); ASSERT_FALSE(called); base::RunLoop().RunUntilIdle(); ASSERT_TRUE(called); - context_->storage()->FindRegistrationForId( + context()->storage()->FindRegistrationForId( registration_id, base::Bind(&ExpectRegisteredWorkers, SERVICE_WORKER_ERROR_NOT_FOUND, @@ -294,7 +290,7 @@ TEST_F(ServiceWorkerContextTest, RegisterNewScript) { bool called = false; int64 old_registration_id = kInvalidServiceWorkerRegistrationId; int64 old_version_id = kInvalidServiceWorkerVersionId; - context_->RegisterServiceWorker( + context()->RegisterServiceWorker( pattern, GURL("http://www.example.com/service_worker.js"), render_process_id_, @@ -310,7 +306,7 @@ TEST_F(ServiceWorkerContextTest, RegisterNewScript) { called = false; int64 new_registration_id = kInvalidServiceWorkerRegistrationId; int64 new_version_id = kInvalidServiceWorkerVersionId; - context_->RegisterServiceWorker( + context()->RegisterServiceWorker( pattern, GURL("http://www.example.com/service_worker_new.js"), render_process_id_, @@ -338,7 +334,7 @@ TEST_F(ServiceWorkerContextTest, RegisterDuplicateScript) { bool called = false; int64 old_registration_id = kInvalidServiceWorkerRegistrationId; int64 old_version_id = kInvalidServiceWorkerVersionId; - context_->RegisterServiceWorker( + context()->RegisterServiceWorker( pattern, script_url, render_process_id_, @@ -354,7 +350,7 @@ TEST_F(ServiceWorkerContextTest, RegisterDuplicateScript) { called = false; int64 new_registration_id = kInvalidServiceWorkerRegistrationId; int64 new_version_id = kInvalidServiceWorkerVersionId; - context_->RegisterServiceWorker( + context()->RegisterServiceWorker( pattern, script_url, render_process_id_, diff --git a/content/browser/service_worker/service_worker_context_wrapper.cc b/content/browser/service_worker/service_worker_context_wrapper.cc index 2ccffa1..8f64134 100644 --- a/content/browser/service_worker/service_worker_context_wrapper.cc +++ b/content/browser/service_worker/service_worker_context_wrapper.cc @@ -7,14 +7,18 @@ #include "base/files/file_path.h" #include "content/browser/service_worker/service_worker_context_core.h" #include "content/browser/service_worker/service_worker_context_observer.h" +#include "content/browser/service_worker/service_worker_process_manager.h" #include "content/public/browser/browser_thread.h" #include "webkit/browser/quota/quota_manager_proxy.h" namespace content { -ServiceWorkerContextWrapper::ServiceWorkerContextWrapper() +ServiceWorkerContextWrapper::ServiceWorkerContextWrapper( + BrowserContext* browser_context) : observer_list_( - new ObserverListThreadSafe<ServiceWorkerContextObserver>()) {} + new ObserverListThreadSafe<ServiceWorkerContextObserver>()), + browser_context_(browser_context) { +} ServiceWorkerContextWrapper::~ServiceWorkerContextWrapper() { } @@ -24,19 +28,26 @@ void ServiceWorkerContextWrapper::Init( quota::QuotaManagerProxy* quota_manager_proxy) { if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { BrowserThread::PostTask( - BrowserThread::IO, FROM_HERE, - base::Bind(&ServiceWorkerContextWrapper::Init, this, + BrowserThread::IO, + FROM_HERE, + base::Bind(&ServiceWorkerContextWrapper::Init, + this, user_data_directory, make_scoped_refptr(quota_manager_proxy))); return; } DCHECK(!context_core_); context_core_.reset(new ServiceWorkerContextCore( - user_data_directory, quota_manager_proxy, observer_list_)); + user_data_directory, + quota_manager_proxy, + observer_list_, + make_scoped_ptr(new ServiceWorkerProcessManager(this)))); } void ServiceWorkerContextWrapper::Shutdown() { if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + browser_context_ = NULL; BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, base::Bind(&ServiceWorkerContextWrapper::Shutdown, this)); @@ -65,7 +76,6 @@ static void FinishRegistrationOnIO( void ServiceWorkerContextWrapper::RegisterServiceWorker( const GURL& pattern, const GURL& script_url, - int source_process_id, const ResultCallback& continuation) { if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { BrowserThread::PostTask( @@ -75,7 +85,6 @@ void ServiceWorkerContextWrapper::RegisterServiceWorker( this, pattern, script_url, - source_process_id, continuation)); return; } @@ -83,7 +92,7 @@ void ServiceWorkerContextWrapper::RegisterServiceWorker( context()->RegisterServiceWorker( pattern, script_url, - source_process_id, + -1, NULL /* provider_host */, base::Bind(&FinishRegistrationOnIO, continuation)); } diff --git a/content/browser/service_worker/service_worker_context_wrapper.h b/content/browser/service_worker/service_worker_context_wrapper.h index cea9c06..bd83210 100644 --- a/content/browser/service_worker/service_worker_context_wrapper.h +++ b/content/browser/service_worker/service_worker_context_wrapper.h @@ -5,6 +5,8 @@ #ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CONTEXT_WRAPPER_H_ #define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CONTEXT_WRAPPER_H_ +#include <vector> + #include "base/files/file_path.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" @@ -22,6 +24,8 @@ class QuotaManagerProxy; namespace content { +class BrowserContext; +class ServiceWorkerContextCore; class ServiceWorkerContextObserver; // A refcounted wrapper class for our core object. Higher level content lib @@ -32,7 +36,7 @@ class CONTENT_EXPORT ServiceWorkerContextWrapper : NON_EXPORTED_BASE(public ServiceWorkerContext), public base::RefCountedThreadSafe<ServiceWorkerContextWrapper> { public: - ServiceWorkerContextWrapper(); + ServiceWorkerContextWrapper(BrowserContext* browser_context); // Init and Shutdown are for use on the UI thread when the profile, // storagepartition is being setup and torn down. @@ -44,12 +48,10 @@ class CONTENT_EXPORT ServiceWorkerContextWrapper ServiceWorkerContextCore* context(); // ServiceWorkerContext implementation: - virtual void RegisterServiceWorker(const GURL& pattern, - const GURL& script_url, - int source_process_id, - const ResultCallback& continuation) - OVERRIDE; - + virtual void RegisterServiceWorker( + const GURL& pattern, + const GURL& script_url, + const ResultCallback& continuation) OVERRIDE; virtual void UnregisterServiceWorker(const GURL& pattern, int source_process_id, const ResultCallback& continuation) @@ -60,11 +62,14 @@ class CONTENT_EXPORT ServiceWorkerContextWrapper private: friend class base::RefCountedThreadSafe<ServiceWorkerContextWrapper>; + friend class ServiceWorkerProcessManager; virtual ~ServiceWorkerContextWrapper(); - scoped_ptr<ServiceWorkerContextCore> context_core_; - scoped_refptr<ObserverListThreadSafe<ServiceWorkerContextObserver> > + const scoped_refptr<ObserverListThreadSafe<ServiceWorkerContextObserver> > observer_list_; + // Cleared in Shutdown(): + BrowserContext* browser_context_; + scoped_ptr<ServiceWorkerContextCore> context_core_; }; } // namespace content diff --git a/content/browser/service_worker/service_worker_dispatcher_host.cc b/content/browser/service_worker/service_worker_dispatcher_host.cc index 562fc95..ec6a839 100644 --- a/content/browser/service_worker/service_worker_dispatcher_host.cc +++ b/content/browser/service_worker/service_worker_dispatcher_host.cc @@ -44,7 +44,9 @@ ServiceWorkerDispatcherHost::ServiceWorkerDispatcherHost( : BrowserMessageFilter(kFilteredMessageClasses, arraysize(kFilteredMessageClasses)), render_process_id_(render_process_id), - message_port_message_filter_(message_port_message_filter) {} + message_port_message_filter_(message_port_message_filter), + channel_ready_(false) { +} ServiceWorkerDispatcherHost::~ServiceWorkerDispatcherHost() { if (context_) { @@ -68,6 +70,16 @@ void ServiceWorkerDispatcherHost::Init( render_process_id_, this); } +void ServiceWorkerDispatcherHost::OnFilterAdded(IPC::Channel* channel) { + BrowserMessageFilter::OnFilterAdded(channel); + channel_ready_ = true; + std::vector<IPC::Message*> messages; + pending_messages_.release(&messages); + for (size_t i = 0; i < messages.size(); ++i) { + BrowserMessageFilter::Send(messages[i]); + } +} + void ServiceWorkerDispatcherHost::OnDestruct() const { BrowserThread::DeleteOnIOThread::Destruct(this); } @@ -116,6 +128,17 @@ bool ServiceWorkerDispatcherHost::OnMessageReceived( return handled; } +bool ServiceWorkerDispatcherHost::Send(IPC::Message* message) { + if (channel_ready_) { + BrowserMessageFilter::Send(message); + // Don't bother passing through Send()'s result: it's not reliable. + return true; + } + + pending_messages_.push_back(message); + return true; +} + void ServiceWorkerDispatcherHost::RegisterServiceWorkerHandle( scoped_ptr<ServiceWorkerHandle> handle) { int handle_id = handle->handle_id(); diff --git a/content/browser/service_worker/service_worker_dispatcher_host.h b/content/browser/service_worker/service_worker_dispatcher_host.h index 473f239..97b50bd 100644 --- a/content/browser/service_worker/service_worker_dispatcher_host.h +++ b/content/browser/service_worker/service_worker_dispatcher_host.h @@ -31,11 +31,20 @@ class CONTENT_EXPORT ServiceWorkerDispatcherHost : public BrowserMessageFilter { void Init(ServiceWorkerContextWrapper* context_wrapper); - // BrowserIOMessageFilter implementation + // BrowserMessageFilter implementation + virtual void OnFilterAdded(IPC::Channel* channel) OVERRIDE; virtual void OnDestruct() const OVERRIDE; virtual bool OnMessageReceived(const IPC::Message& message, bool* message_was_ok) OVERRIDE; + // IPC::Sender implementation + + // Send() queues the message until the underlying channel is ready. This + // class assumes that Send() can only fail after that when the renderer + // process has terminated, at which point the whole instance will eventually + // be destroyed. + virtual bool Send(IPC::Message* message) OVERRIDE; + void RegisterServiceWorkerHandle(scoped_ptr<ServiceWorkerHandle> handle); protected: @@ -98,6 +107,9 @@ class CONTENT_EXPORT ServiceWorkerDispatcherHost : public BrowserMessageFilter { IDMap<ServiceWorkerHandle, IDMapOwnPointer> handles_; + bool channel_ready_; // True after BrowserMessageFilter::channel_ != NULL. + ScopedVector<IPC::Message> pending_messages_; + DISALLOW_COPY_AND_ASSIGN(ServiceWorkerDispatcherHost); }; diff --git a/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc b/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc index 8a407ef..9c74777 100644 --- a/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc +++ b/content/browser/service_worker/service_worker_dispatcher_host_unittest.cc @@ -29,23 +29,19 @@ class ServiceWorkerDispatcherHostTest : public testing::Test { : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {} virtual void SetUp() { - context_wrapper_ = new ServiceWorkerContextWrapper; - context_wrapper_->Init(base::FilePath(), NULL); - helper_.reset(new EmbeddedWorkerTestHelper(context(), kRenderProcessId)); + helper_.reset(new EmbeddedWorkerTestHelper(kRenderProcessId)); } virtual void TearDown() { helper_.reset(); - if (context_wrapper_) { - context_wrapper_->Shutdown(); - context_wrapper_ = NULL; - } } - ServiceWorkerContextCore* context() { return context_wrapper_->context(); } + ServiceWorkerContextCore* context() { return helper_->context(); } + ServiceWorkerContextWrapper* context_wrapper() { + return helper_->context_wrapper(); + } TestBrowserThreadBundle browser_thread_bundle_; - scoped_refptr<ServiceWorkerContextWrapper> context_wrapper_; scoped_ptr<EmbeddedWorkerTestHelper> helper_; }; @@ -85,7 +81,7 @@ TEST_F(ServiceWorkerDispatcherHostTest, DisabledCausesError) { scoped_refptr<TestingServiceWorkerDispatcherHost> dispatcher_host = new TestingServiceWorkerDispatcherHost( - kRenderProcessId, context_wrapper_.get(), helper_.get()); + kRenderProcessId, context_wrapper(), helper_.get()); bool handled; dispatcher_host->OnMessageReceived( @@ -112,7 +108,7 @@ TEST_F(ServiceWorkerDispatcherHostTest, DISABLED_Enabled) { scoped_refptr<TestingServiceWorkerDispatcherHost> dispatcher_host = new TestingServiceWorkerDispatcherHost( - kRenderProcessId, context_wrapper_.get(), helper_.get()); + kRenderProcessId, context_wrapper(), helper_.get()); bool handled; dispatcher_host->OnMessageReceived( @@ -137,10 +133,9 @@ TEST_F(ServiceWorkerDispatcherHostTest, EarlyContextDeletion) { scoped_refptr<TestingServiceWorkerDispatcherHost> dispatcher_host = new TestingServiceWorkerDispatcherHost( - kRenderProcessId, context_wrapper_.get(), helper_.get()); + kRenderProcessId, context_wrapper(), helper_.get()); - context_wrapper_->Shutdown(); - context_wrapper_ = NULL; + helper_->ShutdownContext(); bool handled; dispatcher_host->OnMessageReceived( @@ -157,7 +152,7 @@ TEST_F(ServiceWorkerDispatcherHostTest, EarlyContextDeletion) { TEST_F(ServiceWorkerDispatcherHostTest, ProviderCreatedAndDestroyed) { scoped_refptr<TestingServiceWorkerDispatcherHost> dispatcher_host = new TestingServiceWorkerDispatcherHost( - kRenderProcessId, context_wrapper_.get(), helper_.get()); + kRenderProcessId, context_wrapper(), helper_.get()); const int kProviderId = 1001; // Test with a value != kRenderProcessId. diff --git a/content/browser/service_worker/service_worker_handle_unittest.cc b/content/browser/service_worker/service_worker_handle_unittest.cc index ebc3c745..70c4456 100644 --- a/content/browser/service_worker/service_worker_handle_unittest.cc +++ b/content/browser/service_worker/service_worker_handle_unittest.cc @@ -44,17 +44,15 @@ class ServiceWorkerHandleTest : public testing::Test { : browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {} virtual void SetUp() OVERRIDE { - context_.reset(new ServiceWorkerContextCore(base::FilePath(), NULL, NULL)); - helper_.reset(new EmbeddedWorkerTestHelper(context_.get(), - kRenderProcessId)); + helper_.reset(new EmbeddedWorkerTestHelper(kRenderProcessId)); registration_ = new ServiceWorkerRegistration( GURL("http://www.example.com/*"), GURL("http://www.example.com/service_worker.js"), - 1L, context_->AsWeakPtr()); + 1L, + helper_->context()->AsWeakPtr()); version_ = new ServiceWorkerVersion( - registration_, - 1L, context_->AsWeakPtr()); + registration_, 1L, helper_->context()->AsWeakPtr()); // Simulate adding one process to the worker. int embedded_worker_id = version_->embedded_worker()->embedded_worker_id(); @@ -65,13 +63,11 @@ class ServiceWorkerHandleTest : public testing::Test { registration_ = NULL; version_ = NULL; helper_.reset(); - context_.reset(); } IPC::TestSink* ipc_sink() { return helper_->ipc_sink(); } TestBrowserThreadBundle browser_thread_bundle_; - scoped_ptr<ServiceWorkerContextCore> context_; scoped_ptr<EmbeddedWorkerTestHelper> helper_; scoped_refptr<ServiceWorkerRegistration> registration_; scoped_refptr<ServiceWorkerVersion> version_; @@ -79,8 +75,11 @@ class ServiceWorkerHandleTest : public testing::Test { }; TEST_F(ServiceWorkerHandleTest, OnVersionStateChanged) { - scoped_ptr<ServiceWorkerHandle> handle = ServiceWorkerHandle::Create( - context_->AsWeakPtr(), helper_.get(), 1 /* thread_id */, version_); + scoped_ptr<ServiceWorkerHandle> handle = + ServiceWorkerHandle::Create(helper_->context()->AsWeakPtr(), + helper_.get(), + 1 /* thread_id */, + version_); // Start the worker, and then... ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED; diff --git a/content/browser/service_worker/service_worker_job_unittest.cc b/content/browser/service_worker/service_worker_job_unittest.cc index de5e612..35c368e 100644 --- a/content/browser/service_worker/service_worker_job_unittest.cc +++ b/content/browser/service_worker/service_worker_job_unittest.cc @@ -94,24 +94,22 @@ class ServiceWorkerJobTest : public testing::Test { render_process_id_(88) {} virtual void SetUp() OVERRIDE { - context_.reset(new ServiceWorkerContextCore(base::FilePath(), NULL, NULL)); - helper_.reset(new EmbeddedWorkerTestHelper(context_.get(), - render_process_id_)); + helper_.reset(new EmbeddedWorkerTestHelper(render_process_id_)); } virtual void TearDown() OVERRIDE { helper_.reset(); - context_.reset(); } + ServiceWorkerContextCore* context() const { return helper_->context(); } + ServiceWorkerJobCoordinator* job_coordinator() const { - return context_->job_coordinator(); + return context()->job_coordinator(); } - ServiceWorkerStorage* storage() const { return context_->storage(); } + ServiceWorkerStorage* storage() const { return context()->storage(); } protected: TestBrowserThreadBundle browser_thread_bundle_; - scoped_ptr<ServiceWorkerContextCore> context_; scoped_ptr<EmbeddedWorkerTestHelper> helper_; int render_process_id_; @@ -398,9 +396,8 @@ TEST_F(ServiceWorkerJobTest, RegisterDuplicateScript) { class FailToStartWorkerTestHelper : public EmbeddedWorkerTestHelper { public: - FailToStartWorkerTestHelper(ServiceWorkerContextCore* context, - int mock_render_process_id) - : EmbeddedWorkerTestHelper(context, mock_render_process_id) {} + FailToStartWorkerTestHelper(int mock_render_process_id) + : EmbeddedWorkerTestHelper(mock_render_process_id) {} virtual void OnStartWorker(int embedded_worker_id, int64 service_worker_version_id, @@ -413,8 +410,7 @@ class FailToStartWorkerTestHelper : public EmbeddedWorkerTestHelper { }; TEST_F(ServiceWorkerJobTest, Register_FailToStartWorker) { - helper_.reset( - new FailToStartWorkerTestHelper(context_.get(), render_process_id_)); + helper_.reset(new FailToStartWorkerTestHelper(render_process_id_)); bool called = false; scoped_refptr<ServiceWorkerRegistration> registration; diff --git a/content/browser/service_worker/service_worker_process_manager.cc b/content/browser/service_worker/service_worker_process_manager.cc new file mode 100644 index 0000000..b6a2891 --- /dev/null +++ b/content/browser/service_worker/service_worker_process_manager.cc @@ -0,0 +1,145 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "content/browser/service_worker/service_worker_process_manager.h" + +#include "content/browser/renderer_host/render_process_host_impl.h" +#include "content/browser/service_worker/service_worker_context_wrapper.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/site_instance.h" +#include "url/gurl.h" + +namespace content { + +ServiceWorkerProcessManager::ServiceWorkerProcessManager( + ServiceWorkerContextWrapper* context_wrapper) + : context_wrapper_(context_wrapper), + weak_this_factory_(this), + weak_this_(weak_this_factory_.GetWeakPtr()) { +} + +ServiceWorkerProcessManager::~ServiceWorkerProcessManager() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); +} + +void ServiceWorkerProcessManager::AllocateWorkerProcess( + const std::vector<int>& process_ids, + const GURL& script_url, + const base::Callback<void(ServiceWorkerStatusCode, int process_id)>& + callback) const { + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&ServiceWorkerProcessManager::AllocateWorkerProcess, + weak_this_, + process_ids, + script_url, + callback)); + return; + } + + for (std::vector<int>::const_iterator it = process_ids.begin(); + it != process_ids.end(); + ++it) { + if (IncrementWorkerRefcountByPid(*it)) { + BrowserThread::PostTask(BrowserThread::IO, + FROM_HERE, + base::Bind(callback, SERVICE_WORKER_OK, *it)); + return; + } + } + + if (!context_wrapper_->browser_context_) { + // Shutdown has started. + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + base::Bind(callback, SERVICE_WORKER_ERROR_START_WORKER_FAILED, -1)); + return; + } + // No existing processes available; start a new one. + scoped_refptr<SiteInstance> site_instance = SiteInstance::CreateForURL( + context_wrapper_->browser_context_, script_url); + RenderProcessHost* rph = site_instance->GetProcess(); + // This Init() call posts a task to the IO thread that adds the RPH's + // ServiceWorkerDispatcherHost to the + // EmbeddedWorkerRegistry::process_sender_map_. + if (!rph->Init()) { + LOG(ERROR) << "Couldn't start a new process!"; + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + base::Bind(callback, SERVICE_WORKER_ERROR_START_WORKER_FAILED, -1)); + return; + } + + static_cast<RenderProcessHostImpl*>(rph)->IncrementWorkerRefCount(); + BrowserThread::PostTask( + BrowserThread::IO, + FROM_HERE, + base::Bind(callback, SERVICE_WORKER_OK, rph->GetID())); +} + +void ServiceWorkerProcessManager::ReleaseWorkerProcess(int process_id) { + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&ServiceWorkerProcessManager::ReleaseWorkerProcess, + weak_this_, + process_id)); + return; + } + if (!DecrementWorkerRefcountByPid(process_id)) { + DCHECK(false) << "DecrementWorkerRef(" << process_id + << ") doesn't match a previous IncrementWorkerRef"; + } +} + +void ServiceWorkerProcessManager::SetProcessRefcountOpsForTest( + const base::Callback<bool(int)>& increment_for_test, + const base::Callback<bool(int)>& decrement_for_test) { + increment_for_test_ = increment_for_test; + decrement_for_test_ = decrement_for_test; +} + +bool ServiceWorkerProcessManager::IncrementWorkerRefcountByPid( + int process_id) const { + if (!increment_for_test_.is_null()) + return increment_for_test_.Run(process_id); + + RenderProcessHost* rph = RenderProcessHost::FromID(process_id); + if (rph && !rph->FastShutdownStarted()) { + static_cast<RenderProcessHostImpl*>(rph)->IncrementWorkerRefCount(); + return true; + } + + return false; +} + +bool ServiceWorkerProcessManager::DecrementWorkerRefcountByPid( + int process_id) const { + if (!decrement_for_test_.is_null()) + return decrement_for_test_.Run(process_id); + + RenderProcessHost* rph = RenderProcessHost::FromID(process_id); + if (rph) + static_cast<RenderProcessHostImpl*>(rph)->DecrementWorkerRefCount(); + + return rph != NULL; +} + +} // namespace content + +namespace base { +// Destroying ServiceWorkerProcessManagers only on the UI thread allows the +// member WeakPtr to safely guard the object's lifetime when used on that +// thread. +void DefaultDeleter<content::ServiceWorkerProcessManager>::operator()( + content::ServiceWorkerProcessManager* ptr) const { + content::BrowserThread::DeleteSoon( + content::BrowserThread::UI, FROM_HERE, ptr); +} +} // namespace base diff --git a/content/browser/service_worker/service_worker_process_manager.h b/content/browser/service_worker/service_worker_process_manager.h new file mode 100644 index 0000000..c856a52 --- /dev/null +++ b/content/browser/service_worker/service_worker_process_manager.h @@ -0,0 +1,84 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_PROCESS_MANAGER_H_ +#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_PROCESS_MANAGER_H_ + +#include <vector> + +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "content/common/service_worker/service_worker_status_code.h" + +class GURL; + +namespace content { + +class BrowserContext; +class ServiceWorkerContextWrapper; + +// Interacts with the UI thread to keep RenderProcessHosts alive while the +// ServiceWorker system is using them. Each instance of +// ServiceWorkerProcessManager is destroyed on the UI thread shortly after its +// ServiceWorkerContextCore is destroyed on the IO thread. +class CONTENT_EXPORT ServiceWorkerProcessManager { + public: + // |*this| must be owned by |context_wrapper|->context(). + explicit ServiceWorkerProcessManager( + ServiceWorkerContextWrapper* context_wrapper); + + ~ServiceWorkerProcessManager(); + + // Returns a reference to a running process suitable for starting the Service + // Worker at |script_url|. Processes in |process_ids| will be checked in order + // for existence, and if none exist, then a new process will be created. Posts + // |callback| to the IO thread to indicate whether creation succeeded and the + // process ID that has a new reference. + // + // Allocation can fail with SERVICE_WORKER_ERROR_START_WORKER_FAILED if + // RenderProcessHost::Init fails. + void AllocateWorkerProcess( + const std::vector<int>& process_ids, + const GURL& script_url, + const base::Callback<void(ServiceWorkerStatusCode, int process_id)>& + callback) const; + + // Drops a reference to a process that was running a Service Worker. This + // must match a call to AllocateWorkerProcess. + void ReleaseWorkerProcess(int process_id); + + // |increment_for_test| and |decrement_for_test| define how to look up a + // process by ID and increment or decrement its worker reference count. This + // must be called before any reference to this object escapes to another + // thread, and is considered part of construction. + void SetProcessRefcountOpsForTest( + const base::Callback<bool(int)>& increment_for_test, + const base::Callback<bool(int)>& decrement_for_test); + + private: + bool IncrementWorkerRefcountByPid(int process_id) const; + bool DecrementWorkerRefcountByPid(int process_id) const; + + // These fields are only accessed on the UI thread after construction. + scoped_refptr<ServiceWorkerContextWrapper> context_wrapper_; + base::Callback<bool(int)> increment_for_test_; + base::Callback<bool(int)> decrement_for_test_; + + // Used to double-check that we don't access *this after it's destroyed. + base::WeakPtrFactory<ServiceWorkerProcessManager> weak_this_factory_; + base::WeakPtr<ServiceWorkerProcessManager> weak_this_; +}; + +} // namespace content + +namespace base { +// Specialized to post the deletion to the UI thread. +template <> +struct CONTENT_EXPORT DefaultDeleter<content::ServiceWorkerProcessManager> { + void operator()(content::ServiceWorkerProcessManager* ptr) const; +}; +} // namespace base + +#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_PROCESS_MANAGER_H_ diff --git a/content/browser/service_worker/service_worker_provider_host_unittest.cc b/content/browser/service_worker/service_worker_provider_host_unittest.cc index 5a60295..03c513a 100644 --- a/content/browser/service_worker/service_worker_provider_host_unittest.cc +++ b/content/browser/service_worker/service_worker_provider_host_unittest.cc @@ -23,7 +23,11 @@ class ServiceWorkerProviderHostTest : public testing::Test { virtual ~ServiceWorkerProviderHostTest() {} virtual void SetUp() OVERRIDE { - context_.reset(new ServiceWorkerContextCore(base::FilePath(), NULL, NULL)); + context_.reset(new ServiceWorkerContextCore( + base::FilePath(), + NULL, + NULL, + scoped_ptr<ServiceWorkerProcessManager>())); scope_ = GURL("http://www.example.com/*"); script_url_ = GURL("http://www.example.com/service_worker.js"); diff --git a/content/browser/service_worker/service_worker_register_job.cc b/content/browser/service_worker/service_worker_register_job.cc index 27b687eca..6819305 100644 --- a/content/browser/service_worker/service_worker_register_job.cc +++ b/content/browser/service_worker/service_worker_register_job.cc @@ -43,14 +43,10 @@ ServiceWorkerRegisterJob::~ServiceWorkerRegisterJob() { void ServiceWorkerRegisterJob::AddCallback(const RegistrationCallback& callback, int process_id) { - DCHECK_NE(-1, process_id); - if (phase_ >= UPDATE && pending_version()) - pending_version()->AddProcessToWorker(process_id); - else - pending_process_ids_.push_back(process_id); - if (!is_promise_resolved_) { callbacks_.push_back(callback); + if (process_id != -1 && (phase_ < UPDATE || !pending_version())) + pending_process_ids_.push_back(process_id); return; } RunSoon(base::Bind( @@ -228,15 +224,11 @@ void ServiceWorkerRegisterJob::UpdateAndContinue( // the worker. set_pending_version(new ServiceWorkerVersion( registration(), context_->storage()->NewVersionId(), context_)); - for (std::vector<int>::const_iterator it = pending_process_ids_.begin(); - it != pending_process_ids_.end(); - ++it) { - pending_version()->AddProcessToWorker(*it); - } // TODO(michaeln): Start the worker into a paused state where the // script resource is downloaded but not yet evaluated. - pending_version()->StartWorker( + pending_version()->StartWorkerWithCandidateProcesses( + pending_process_ids_, base::Bind(&ServiceWorkerRegisterJob::OnStartWorkerFinished, weak_factory_.GetWeakPtr())); } diff --git a/content/browser/service_worker/service_worker_register_job.h b/content/browser/service_worker/service_worker_register_job.h index 5b02039..0a0c198 100644 --- a/content/browser/service_worker/service_worker_register_job.h +++ b/content/browser/service_worker/service_worker_register_job.h @@ -45,9 +45,10 @@ class ServiceWorkerRegisterJob : public ServiceWorkerRegisterJobBase { virtual ~ServiceWorkerRegisterJob(); // Registers a callback to be called when the promise would resolve (whether - // successfully or not). Multiple callbacks may be registered. |process_id| is - // added via AddProcessToWorker to the ServiceWorkerVersion created by the - // registration job. + // successfully or not). Multiple callbacks may be registered. If |process_id| + // is not -1, it's added to the existing clients when deciding in which + // process to create the Service Worker instance. If there are no existing + // clients, a new RenderProcessHost will be created. void AddCallback(const RegistrationCallback& callback, int process_id); // ServiceWorkerRegisterJobBase implementation: diff --git a/content/browser/service_worker/service_worker_registration_unittest.cc b/content/browser/service_worker/service_worker_registration_unittest.cc index 23f190e..d1015b2 100644 --- a/content/browser/service_worker/service_worker_registration_unittest.cc +++ b/content/browser/service_worker/service_worker_registration_unittest.cc @@ -21,7 +21,11 @@ class ServiceWorkerRegistrationTest : public testing::Test { : io_thread_(BrowserThread::IO, &message_loop_) {} virtual void SetUp() OVERRIDE { - context_.reset(new ServiceWorkerContextCore(base::FilePath(), NULL, NULL)); + context_.reset(new ServiceWorkerContextCore( + base::FilePath(), + NULL, + NULL, + scoped_ptr<ServiceWorkerProcessManager>())); context_ptr_ = context_->AsWeakPtr(); } diff --git a/content/browser/service_worker/service_worker_storage_unittest.cc b/content/browser/service_worker/service_worker_storage_unittest.cc index ed2268a..80ee8af 100644 --- a/content/browser/service_worker/service_worker_storage_unittest.cc +++ b/content/browser/service_worker/service_worker_storage_unittest.cc @@ -72,7 +72,11 @@ class ServiceWorkerStorageTest : public testing::Test { } virtual void SetUp() OVERRIDE { - context_.reset(new ServiceWorkerContextCore(base::FilePath(), NULL, NULL)); + context_.reset(new ServiceWorkerContextCore( + base::FilePath(), + NULL, + NULL, + scoped_ptr<ServiceWorkerProcessManager>())); context_ptr_ = context_->AsWeakPtr(); storage()->simulated_lazy_initted_ = true; } diff --git a/content/browser/service_worker/service_worker_url_request_job_unittest.cc b/content/browser/service_worker/service_worker_url_request_job_unittest.cc index 63e57d6..931cace 100644 --- a/content/browser/service_worker/service_worker_url_request_job_unittest.cc +++ b/content/browser/service_worker/service_worker_url_request_job_unittest.cc @@ -89,34 +89,32 @@ class ServiceWorkerURLRequestJobTest : public testing::Test { virtual ~ServiceWorkerURLRequestJobTest() {} virtual void SetUp() OVERRIDE { - context_.reset(new ServiceWorkerContextCore(base::FilePath(), NULL, NULL)); - helper_.reset(new EmbeddedWorkerTestHelper(context_.get(), kProcessID)); + helper_.reset(new EmbeddedWorkerTestHelper(kProcessID)); registration_ = new ServiceWorkerRegistration( GURL("http://example.com/*"), GURL("http://example.com/service_worker.js"), - 1L, context_->AsWeakPtr()); + 1L, + helper_->context()->AsWeakPtr()); version_ = new ServiceWorkerVersion( - registration_, - 1L, context_->AsWeakPtr()); + registration_, 1L, helper_->context()->AsWeakPtr()); scoped_ptr<ServiceWorkerProviderHost> provider_host( - new ServiceWorkerProviderHost(kProcessID, kProviderID, - context_->AsWeakPtr(), NULL)); + new ServiceWorkerProviderHost( + kProcessID, kProviderID, helper_->context()->AsWeakPtr(), NULL)); provider_host->SetActiveVersion(version_.get()); url_request_job_factory_.SetProtocolHandler( "http", new MockProtocolHandler(provider_host->AsWeakPtr())); url_request_context_.set_job_factory(&url_request_job_factory_); - context_->AddProviderHost(provider_host.Pass()); + helper_->context()->AddProviderHost(provider_host.Pass()); } virtual void TearDown() OVERRIDE { version_ = NULL; registration_ = NULL; helper_.reset(); - context_.reset(); } void TestRequest() { @@ -137,7 +135,6 @@ class ServiceWorkerURLRequestJobTest : public testing::Test { TestBrowserThreadBundle thread_bundle_; - scoped_ptr<ServiceWorkerContextCore> context_; scoped_ptr<EmbeddedWorkerTestHelper> helper_; scoped_refptr<ServiceWorkerRegistration> registration_; scoped_refptr<ServiceWorkerVersion> version_; diff --git a/content/browser/service_worker/service_worker_version.cc b/content/browser/service_worker/service_worker_version.cc index 8cb4d12..592f88b 100644 --- a/content/browser/service_worker/service_worker_version.cc +++ b/content/browser/service_worker/service_worker_version.cc @@ -139,24 +139,34 @@ ServiceWorkerVersionInfo ServiceWorkerVersion::GetInfo() { } void ServiceWorkerVersion::StartWorker(const StatusCallback& callback) { + StartWorkerWithCandidateProcesses(std::vector<int>(), callback); +} + +void ServiceWorkerVersion::StartWorkerWithCandidateProcesses( + const std::vector<int>& possible_process_ids, + const StatusCallback& callback) { DCHECK(embedded_worker_); - if (running_status() == RUNNING) { - RunSoon(base::Bind(callback, SERVICE_WORKER_OK)); - return; - } - if (running_status() == STOPPING) { - RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_START_WORKER_FAILED)); - return; - } - if (start_callbacks_.empty()) { - ServiceWorkerStatusCode status = embedded_worker_->Start( - version_id_, scope_, script_url_); - if (status != SERVICE_WORKER_OK) { - RunSoon(base::Bind(callback, status)); + switch (running_status()) { + case RUNNING: + RunSoon(base::Bind(callback, SERVICE_WORKER_OK)); + return; + case STOPPING: + RunSoon(base::Bind(callback, SERVICE_WORKER_ERROR_START_WORKER_FAILED)); + return; + case STOPPED: + case STARTING: + start_callbacks_.push_back(callback); + if (running_status() == STOPPED) { + embedded_worker_->Start( + version_id_, + scope_, + script_url_, + possible_process_ids, + base::Bind(&ServiceWorkerVersion::RunStartWorkerCallbacksOnError, + weak_factory_.GetWeakPtr())); + } return; - } } - start_callbacks_.push_back(callback); } void ServiceWorkerVersion::StopWorker(const StatusCallback& callback) { @@ -196,48 +206,39 @@ void ServiceWorkerVersion::DispatchInstallEvent( int active_version_id, const StatusCallback& callback) { DCHECK_EQ(NEW, status()) << status(); + SetStatus(INSTALLING); if (running_status() != RUNNING) { // Schedule calling this method after starting the worker. - StartWorker(base::Bind(&RunTaskAfterStartWorker, - weak_factory_.GetWeakPtr(), callback, - base::Bind(&self::DispatchInstallEvent, - weak_factory_.GetWeakPtr(), - active_version_id, callback))); - return; - } - - SetStatus(INSTALLING); - int request_id = install_callbacks_.Add(new StatusCallback(callback)); - ServiceWorkerStatusCode status = embedded_worker_->SendMessage( - ServiceWorkerMsg_InstallEvent(request_id, active_version_id)); - if (status != SERVICE_WORKER_OK) { - install_callbacks_.Remove(request_id); - RunSoon(base::Bind(callback, status)); + StartWorker( + base::Bind(&RunTaskAfterStartWorker, + weak_factory_.GetWeakPtr(), + callback, + base::Bind(&self::DispatchInstallEventAfterStartWorker, + weak_factory_.GetWeakPtr(), + active_version_id, + callback))); + } else { + DispatchInstallEventAfterStartWorker(active_version_id, callback); } } void ServiceWorkerVersion::DispatchActivateEvent( const StatusCallback& callback) { DCHECK_EQ(INSTALLED, status()) << status(); + SetStatus(ACTIVATING); if (running_status() != RUNNING) { // Schedule calling this method after starting the worker. - StartWorker(base::Bind(&RunTaskAfterStartWorker, - weak_factory_.GetWeakPtr(), callback, - base::Bind(&self::DispatchActivateEvent, - weak_factory_.GetWeakPtr(), - callback))); - return; - } - - SetStatus(ACTIVATING); - int request_id = activate_callbacks_.Add(new StatusCallback(callback)); - ServiceWorkerStatusCode status = embedded_worker_->SendMessage( - ServiceWorkerMsg_ActivateEvent(request_id)); - if (status != SERVICE_WORKER_OK) { - activate_callbacks_.Remove(request_id); - RunSoon(base::Bind(callback, status)); + StartWorker( + base::Bind(&RunTaskAfterStartWorker, + weak_factory_.GetWeakPtr(), + callback, + base::Bind(&self::DispatchActivateEventAfterStartWorker, + weak_factory_.GetWeakPtr(), + callback))); + } else { + DispatchActivateEventAfterStartWorker(callback); } } @@ -433,6 +434,39 @@ bool ServiceWorkerVersion::OnMessageReceived(const IPC::Message& message) { return handled; } +void ServiceWorkerVersion::RunStartWorkerCallbacksOnError( + ServiceWorkerStatusCode status) { + if (status != SERVICE_WORKER_OK) + RunCallbacks(this, &start_callbacks_, status); +} + +void ServiceWorkerVersion::DispatchInstallEventAfterStartWorker( + int active_version_id, + const StatusCallback& callback) { + DCHECK_EQ(RUNNING, running_status()) + << "Worker stopped too soon after it was started."; + int request_id = install_callbacks_.Add(new StatusCallback(callback)); + ServiceWorkerStatusCode status = embedded_worker_->SendMessage( + ServiceWorkerMsg_InstallEvent(request_id, active_version_id)); + if (status != SERVICE_WORKER_OK) { + install_callbacks_.Remove(request_id); + RunSoon(base::Bind(callback, status)); + } +} + +void ServiceWorkerVersion::DispatchActivateEventAfterStartWorker( + const StatusCallback& callback) { + DCHECK_EQ(RUNNING, running_status()) + << "Worker stopped too soon after it was started."; + int request_id = activate_callbacks_.Add(new StatusCallback(callback)); + ServiceWorkerStatusCode status = + embedded_worker_->SendMessage(ServiceWorkerMsg_ActivateEvent(request_id)); + if (status != SERVICE_WORKER_OK) { + activate_callbacks_.Remove(request_id); + RunSoon(base::Bind(callback, status)); + } +} + void ServiceWorkerVersion::OnGetClientDocuments(int request_id) { std::vector<int> client_ids; ControlleeByIDMap::iterator it(&controllee_by_id_); diff --git a/content/browser/service_worker/service_worker_version.h b/content/browser/service_worker/service_worker_version.h index dcdf1b6..e0db0ff 100644 --- a/content/browser/service_worker/service_worker_version.h +++ b/content/browser/service_worker/service_worker_version.h @@ -118,6 +118,14 @@ class CONTENT_EXPORT ServiceWorkerVersion void StartWorker(const StatusCallback& callback); // Starts an embedded worker for this version. + // |potential_process_ids| is a list of processes in which to start the + // worker. + // This returns OK (success) if the worker is already running. + void StartWorkerWithCandidateProcesses( + const std::vector<int>& potential_process_ids, + const StatusCallback& callback); + + // Starts an embedded worker for this version. // This returns OK (success) if the worker is already stopped. void StopWorker(const StatusCallback& callback); @@ -212,6 +220,12 @@ class CONTENT_EXPORT ServiceWorkerVersion virtual ~ServiceWorkerVersion(); + void RunStartWorkerCallbacksOnError(ServiceWorkerStatusCode status); + + void DispatchInstallEventAfterStartWorker(int active_version_id, + const StatusCallback& callback); + void DispatchActivateEventAfterStartWorker(const StatusCallback& callback); + // Message handlers. void OnGetClientDocuments(int request_id); void OnActivateEventFinished(int request_id, diff --git a/content/browser/service_worker/service_worker_version_unittest.cc b/content/browser/service_worker/service_worker_version_unittest.cc index 9cea769..6622e5a 100644 --- a/content/browser/service_worker/service_worker_version_unittest.cc +++ b/content/browser/service_worker/service_worker_version_unittest.cc @@ -33,8 +33,8 @@ static const int kRenderProcessId = 1; class MessageReceiver : public EmbeddedWorkerTestHelper { public: - MessageReceiver(ServiceWorkerContextCore* context) - : EmbeddedWorkerTestHelper(context, kRenderProcessId), + MessageReceiver() + : EmbeddedWorkerTestHelper(kRenderProcessId), current_embedded_worker_id_(0) {} virtual ~MessageReceiver() {} @@ -117,16 +117,15 @@ class ServiceWorkerVersionTest : public testing::Test { : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {} virtual void SetUp() OVERRIDE { - context_.reset(new ServiceWorkerContextCore(base::FilePath(), NULL, NULL)); - helper_.reset(new MessageReceiver(context_.get())); + helper_.reset(new MessageReceiver()); registration_ = new ServiceWorkerRegistration( GURL("http://www.example.com/*"), GURL("http://www.example.com/service_worker.js"), - 1L, context_->AsWeakPtr()); + 1L, + helper_->context()->AsWeakPtr()); version_ = new ServiceWorkerVersion( - registration_, - 1L, context_->AsWeakPtr()); + registration_, 1L, helper_->context()->AsWeakPtr()); // Simulate adding one process to the worker. int embedded_worker_id = version_->embedded_worker()->embedded_worker_id(); @@ -138,11 +137,9 @@ class ServiceWorkerVersionTest : public testing::Test { version_ = 0; registration_ = 0; helper_.reset(); - context_.reset(); } TestBrowserThreadBundle thread_bundle_; - scoped_ptr<ServiceWorkerContextCore> context_; scoped_ptr<MessageReceiver> helper_; scoped_refptr<ServiceWorkerRegistration> registration_; scoped_refptr<ServiceWorkerVersion> version_; |