Commit a9692f8f authored by jyasskin@chromium.org's avatar jyasskin@chromium.org

Add the beginning of a public Service Worker API.

This supports registration and unregistration from the UI thread.

BUG=355131

Review URL: https://codereview.chromium.org/208843004

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@260338 0039d316-1c4b-4281-b951-d872f2087c98
parent 6d4fa5f6
......@@ -187,8 +187,7 @@ class TestStoragePartition : public StoragePartition {
virtual content::IndexedDBContext* GetIndexedDBContext() OVERRIDE {
return NULL;
}
virtual content::ServiceWorkerContextWrapper*
GetServiceWorkerContext() OVERRIDE {
virtual content::ServiceWorkerContext* GetServiceWorkerContext() OVERRIDE {
return NULL;
}
......
......@@ -53,6 +53,20 @@ void RunOnIOThread(const base::Closure& closure) {
run_loop.Run();
}
void RunOnIOThread(
const base::Callback<void(const base::Closure& continuation)>& closure) {
base::RunLoop run_loop;
base::Closure quit_on_original_thread =
base::Bind(base::IgnoreResult(&base::MessageLoopProxy::PostTask),
base::MessageLoopProxy::current().get(),
FROM_HERE,
run_loop.QuitClosure());
BrowserThread::PostTask(BrowserThread::IO,
FROM_HERE,
base::Bind(closure, quit_on_original_thread));
run_loop.Run();
}
// Contrary to the style guide, the output parameter of this function comes
// before input parameters so Bind can be used on it to create a FetchCallback
// to pass to DispatchFetchEvent.
......@@ -90,7 +104,8 @@ class ServiceWorkerBrowserTest : public ContentBrowserTest {
ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
StoragePartition* partition = BrowserContext::GetDefaultStoragePartition(
shell()->web_contents()->GetBrowserContext());
wrapper_ = partition->GetServiceWorkerContext();
wrapper_ = static_cast<ServiceWorkerContextWrapper*>(
partition->GetServiceWorkerContext());
// Navigate to the page to set up a renderer page (where we can embed
// a worker).
......@@ -110,6 +125,7 @@ class ServiceWorkerBrowserTest : public ContentBrowserTest {
virtual void TearDownOnIOThread() {}
ServiceWorkerContextWrapper* wrapper() { return wrapper_.get(); }
ServiceWorkerContext* public_context() { return wrapper(); }
void AssociateRendererProcessToWorker(EmbeddedWorkerInstance* worker) {
worker->AddProcessReference(
......@@ -416,4 +432,96 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerVersionBrowserTest, FetchEvent_Rejected) {
ASSERT_EQ(SERVICE_WORKER_FETCH_EVENT_RESULT_FALLBACK, result);
}
class ServiceWorkerBlackBoxBrowserTest : public ServiceWorkerBrowserTest {
public:
typedef ServiceWorkerBlackBoxBrowserTest self;
static void ExpectResultAndRun(bool expected,
const base::Closure& continuation,
bool actual) {
EXPECT_EQ(expected, actual);
continuation.Run();
}
int RenderProcessID() {
return shell()->web_contents()->GetRenderProcessHost()->GetID();
}
void FindRegistrationOnIO(const GURL& document_url,
ServiceWorkerStatusCode* status,
GURL* script_url,
const base::Closure& continuation) {
wrapper()->context()->storage()->FindRegistrationForDocument(
document_url,
base::Bind(&ServiceWorkerBlackBoxBrowserTest::FindRegistrationOnIO2,
this,
status,
script_url,
continuation));
}
void FindRegistrationOnIO2(
ServiceWorkerStatusCode* out_status,
GURL* script_url,
const base::Closure& continuation,
ServiceWorkerStatusCode status,
const scoped_refptr<ServiceWorkerRegistration>& registration) {
*out_status = status;
if (registration) {
*script_url = registration->script_url();
} else {
EXPECT_NE(SERVICE_WORKER_OK, status);
}
continuation.Run();
}
};
IN_PROC_BROWSER_TEST_F(ServiceWorkerBlackBoxBrowserTest, Registration) {
const std::string kWorkerUrl = "/service_worker/fetch_event.js";
{
base::RunLoop run_loop;
public_context()->RegisterServiceWorker(
embedded_test_server()->GetURL("/*"),
embedded_test_server()->GetURL(kWorkerUrl),
RenderProcessID(),
base::Bind(&ServiceWorkerBlackBoxBrowserTest::ExpectResultAndRun,
true,
run_loop.QuitClosure()));
run_loop.Run();
}
{
ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
GURL script_url;
RunOnIOThread(
base::Bind(&ServiceWorkerBlackBoxBrowserTest::FindRegistrationOnIO,
this,
embedded_test_server()->GetURL("/service_worker/empty.html"),
&status,
&script_url));
EXPECT_EQ(SERVICE_WORKER_OK, status);
EXPECT_EQ(embedded_test_server()->GetURL(kWorkerUrl), script_url);
}
{
base::RunLoop run_loop;
public_context()->UnregisterServiceWorker(
embedded_test_server()->GetURL("/*"),
RenderProcessID(),
base::Bind(&ServiceWorkerBlackBoxBrowserTest::ExpectResultAndRun,
true,
run_loop.QuitClosure()));
run_loop.Run();
}
{
ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
GURL script_url;
RunOnIOThread(
base::Bind(&ServiceWorkerBlackBoxBrowserTest::FindRegistrationOnIO,
this,
embedded_test_server()->GetURL("/service_worker/empty.html"),
&status,
&script_url));
EXPECT_EQ(SERVICE_WORKER_ERROR_NOT_FOUND, status);
}
}
} // namespace content
// Copyright 2013 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_CONTEXT_H_
#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CONTEXT_H_
#include "base/basictypes.h"
namespace content {
// Represents the per-BrowserContext ServiceWorker data.
class ServiceWorkerContext {
public:
// TODO(michaeln): This class is a place holder for content/public api
// which will come later. Promote this class when we get there.
protected:
ServiceWorkerContext() {}
virtual ~ServiceWorkerContext() {}
private:
DISALLOW_COPY_AND_ASSIGN(ServiceWorkerContext);
};
} // namespace content
#endif // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_CONTEXT_H_
......@@ -2,7 +2,7 @@
// 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_context.h"
#include "content/public/browser/service_worker_context.h"
#include "base/files/scoped_temp_dir.h"
#include "base/logging.h"
......
......@@ -45,8 +45,76 @@ void ServiceWorkerContextWrapper::Shutdown() {
}
ServiceWorkerContextCore* ServiceWorkerContextWrapper::context() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK_CURRENTLY_ON(BrowserThread::IO);
return context_core_.get();
}
static void FinishRegistrationOnIO(
const ServiceWorkerContext::ResultCallback& continuation,
ServiceWorkerStatusCode status,
int64 registration_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(continuation, status == SERVICE_WORKER_OK));
}
void ServiceWorkerContextWrapper::RegisterServiceWorker(
const GURL& pattern,
const GURL& script_url,
int source_process_id,
const ResultCallback& continuation) {
if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(&ServiceWorkerContextWrapper::RegisterServiceWorker,
this,
pattern,
script_url,
source_process_id,
continuation));
return;
}
context()->RegisterServiceWorker(
pattern,
script_url,
source_process_id,
base::Bind(&FinishRegistrationOnIO, continuation));
}
static void FinishUnregistrationOnIO(
const ServiceWorkerContext::ResultCallback& continuation,
ServiceWorkerStatusCode status) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(continuation, status == SERVICE_WORKER_OK));
}
void ServiceWorkerContextWrapper::UnregisterServiceWorker(
const GURL& pattern,
int source_process_id,
const ResultCallback& continuation) {
if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) {
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
base::Bind(&ServiceWorkerContextWrapper::UnregisterServiceWorker,
this,
pattern,
source_process_id,
continuation));
return;
}
context()->UnregisterServiceWorker(
pattern,
source_process_id,
base::Bind(&FinishUnregistrationOnIO, continuation));
}
} // namespace content
......@@ -8,8 +8,9 @@
#include "base/files/file_path.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "content/browser/service_worker/service_worker_context.h"
#include "content/browser/service_worker/service_worker_context_core.h"
#include "content/common/content_export.h"
#include "content/public/browser/service_worker_context.h"
namespace base {
class FilePath;
......@@ -42,6 +43,18 @@ class CONTENT_EXPORT ServiceWorkerContextWrapper
// The core context is only for use on the IO thread.
ServiceWorkerContextCore* context();
// ServiceWorkerContext implementation:
virtual void RegisterServiceWorker(const GURL& pattern,
const GURL& script_url,
int source_process_id,
const ResultCallback& continuation)
OVERRIDE;
virtual void UnregisterServiceWorker(const GURL& pattern,
int source_process_id,
const ResultCallback& continuation)
OVERRIDE;
private:
friend class base::RefCountedThreadSafe<ServiceWorkerContextWrapper>;
virtual ~ServiceWorkerContextWrapper();
......
......@@ -124,7 +124,8 @@ void ServiceWorkerInternalsUI::GetAllRegistrations(const ListValue* args) {
void ServiceWorkerInternalsUI::AddContextFromStoragePartition(
StoragePartition* partition) {
scoped_refptr<ServiceWorkerContextWrapper> context =
partition->GetServiceWorkerContext();
static_cast<ServiceWorkerContextWrapper*>(
partition->GetServiceWorkerContext());
BrowserThread::PostTask(
BrowserThread::IO,
FROM_HERE,
......@@ -142,7 +143,8 @@ void FindContext(const base::FilePath& partition_path,
StoragePartition* storage_partition) {
if (storage_partition->GetPath() == partition_path) {
*result_partition = storage_partition;
*result_context = storage_partition->GetServiceWorkerContext();
*result_context = static_cast<ServiceWorkerContextWrapper*>(
storage_partition->GetServiceWorkerContext());
}
}
} // namespace
......
......@@ -181,6 +181,7 @@
'public/browser/resource_request_info.h',
'public/browser/resource_throttle.h',
'public/browser/save_page_type.h',
'public/browser/service_worker_context.h',
'public/browser/session_storage_namespace.h',
'public/browser/session_storage_usage_info.h',
'public/browser/signed_certificate_timestamp_store.h',
......@@ -1153,7 +1154,6 @@
'browser/service_worker/embedded_worker_instance.h',
'browser/service_worker/embedded_worker_registry.cc',
'browser/service_worker/embedded_worker_registry.h',
'browser/service_worker/service_worker_context.h',
'browser/service_worker/service_worker_context_core.cc',
'browser/service_worker/service_worker_context_core.h',
'browser/service_worker/service_worker_context_wrapper.cc',
......
// 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_PUBLIC_BROWSER_SERVICE_WORKER_CONTEXT_H_
#define CONTENT_PUBLIC_BROWSER_SERVICE_WORKER_CONTEXT_H_
#include "base/basictypes.h"
#include "base/callback_forward.h"
#include "url/gurl.h"
namespace content {
// Represents the per-StoragePartition ServiceWorker data. Must be used from
// the UI thread.
class ServiceWorkerContext {
public:
// https://rawgithub.com/slightlyoff/ServiceWorker/master/spec/service_worker/index.html#url-scope:
// roughly, must be of the form "<origin>/<path>/*".
typedef GURL Scope;
typedef base::Callback<void(bool success)> ResultCallback;
// Equivalent to calling navigator.serviceWorker.register(script_url, {scope:
// pattern}) from a renderer in |source_process_id|, except that |pattern| is
// an absolute URL instead of relative to some current origin. |callback| is
// passed true when the JS promise is fulfilled or false when the JS
// promise is rejected.
//
// The registration can fail if:
// * |script_url| is on a different origin from |pattern|
// * Fetching |script_url| fails.
// * |script_url| fails to parse or its top-level execution fails.
// TODO: The error message for this needs to be available to developers.
// * Something unexpected goes wrong, like a renderer crash or a full disk.
virtual void RegisterServiceWorker(const Scope& pattern,
const GURL& script_url,
int source_process_id,
const ResultCallback& callback) = 0;
// Equivalent to calling navigator.serviceWorker.unregister(pattern) from a
// renderer in |source_process_id|, except that |pattern| is an absolute URL
// instead of relative to some current origin. |callback| is passed true
// when the JS promise is fulfilled or false when the JS promise is rejected.
//
// Unregistration can fail if:
// * No Service Worker was registered for |pattern|.
// * Something unexpected goes wrong, like a renderer crash.
virtual void UnregisterServiceWorker(const Scope& pattern,
int source_process_id,
const ResultCallback& callback) = 0;
// TODO(jyasskin): Provide a way to SendMessage to a Scope.
protected:
ServiceWorkerContext() {}
virtual ~ServiceWorkerContext() {}
};
} // namespace content
#endif // CONTENT_PUBLIC_BROWSER_SERVICE_WORKER_CONTEXT_H_
......@@ -38,7 +38,7 @@ namespace content {
class BrowserContext;
class IndexedDBContext;
class DOMStorageContext;
class ServiceWorkerContextWrapper;
class ServiceWorkerContext;
// Defines what persistent state a child process can access.
//
......@@ -57,7 +57,7 @@ class StoragePartition {
virtual webkit_database::DatabaseTracker* GetDatabaseTracker() = 0;
virtual DOMStorageContext* GetDOMStorageContext() = 0;
virtual IndexedDBContext* GetIndexedDBContext() = 0;
virtual ServiceWorkerContextWrapper* GetServiceWorkerContext() = 0;
virtual ServiceWorkerContext* GetServiceWorkerContext() = 0;
enum RemoveDataMask {
REMOVE_DATA_MASK_APPCACHE = 1 << 0,
......
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