blob: f04e575f6d796abb069c3a92ce3c36f1d9b980ef [file] [log] [blame]
// 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.
#include "content/public/browser/service_worker_context.h"
#include "base/files/scoped_temp_dir.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "content/browser/browser_thread_impl.h"
#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_registration.h"
#include "content/browser/service_worker/service_worker_storage.h"
#include "content/common/service_worker/embedded_worker_messages.h"
#include "content/common/service_worker/service_worker_messages.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace {
void SaveResponseCallback(bool* called,
int64* store_registration_id,
ServiceWorkerStatusCode status,
int64 registration_id) {
EXPECT_EQ(SERVICE_WORKER_OK, status) << ServiceWorkerStatusToString(status);
*called = true;
*store_registration_id = registration_id;
}
ServiceWorkerContextCore::RegistrationCallback MakeRegisteredCallback(
bool* called,
int64* store_registration_id) {
return base::Bind(&SaveResponseCallback, called, store_registration_id);
}
void CallCompletedCallback(bool* called, ServiceWorkerStatusCode) {
*called = true;
}
ServiceWorkerContextCore::UnregistrationCallback MakeUnregisteredCallback(
bool* called) {
return base::Bind(&CallCompletedCallback, called);
}
void ExpectRegisteredWorkers(
ServiceWorkerStatusCode expect_status,
bool expect_waiting,
bool expect_active,
ServiceWorkerStatusCode status,
const scoped_refptr<ServiceWorkerRegistration>& registration) {
ASSERT_EQ(expect_status, status);
if (status != SERVICE_WORKER_OK) {
EXPECT_FALSE(registration.get());
return;
}
if (expect_waiting) {
EXPECT_TRUE(registration->waiting_version());
} else {
EXPECT_FALSE(registration->waiting_version());
}
if (expect_active) {
EXPECT_TRUE(registration->active_version());
} else {
EXPECT_FALSE(registration->active_version());
}
}
class RejectInstallTestHelper : public EmbeddedWorkerTestHelper {
public:
explicit RejectInstallTestHelper(int mock_render_process_id)
: EmbeddedWorkerTestHelper(mock_render_process_id) {}
void OnInstallEvent(int embedded_worker_id,
int request_id,
int active_version_id) override {
SimulateSend(
new ServiceWorkerHostMsg_InstallEventFinished(
embedded_worker_id, request_id,
blink::WebServiceWorkerEventResultRejected));
}
};
class RejectActivateTestHelper : public EmbeddedWorkerTestHelper {
public:
explicit RejectActivateTestHelper(int mock_render_process_id)
: EmbeddedWorkerTestHelper(mock_render_process_id) {}
void OnActivateEvent(int embedded_worker_id, int request_id) override {
SimulateSend(
new ServiceWorkerHostMsg_ActivateEventFinished(
embedded_worker_id, request_id,
blink::WebServiceWorkerEventResultRejected));
}
};
} // namespace
class ServiceWorkerContextTest : public testing::Test {
public:
ServiceWorkerContextTest()
: browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
render_process_id_(99) {}
virtual void SetUp() override {
helper_.reset(new EmbeddedWorkerTestHelper(render_process_id_));
}
virtual void TearDown() override {
helper_.reset();
}
ServiceWorkerContextCore* context() { return helper_->context(); }
protected:
TestBrowserThreadBundle browser_thread_bundle_;
scoped_ptr<EmbeddedWorkerTestHelper> helper_;
const int render_process_id_;
};
// Make sure basic registration is working.
TEST_F(ServiceWorkerContextTest, Register) {
int64 registration_id = kInvalidServiceWorkerRegistrationId;
bool called = false;
context()->RegisterServiceWorker(
GURL("http://www.example.com/"),
GURL("http://www.example.com/service_worker.js"),
NULL,
MakeRegisteredCallback(&called, &registration_id));
ASSERT_FALSE(called);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(called);
EXPECT_EQ(4UL, helper_->ipc_sink()->message_count());
EXPECT_TRUE(helper_->ipc_sink()->GetUniqueMessageMatching(
EmbeddedWorkerMsg_StartWorker::ID));
EXPECT_TRUE(helper_->inner_ipc_sink()->GetUniqueMessageMatching(
ServiceWorkerMsg_InstallEvent::ID));
EXPECT_TRUE(helper_->inner_ipc_sink()->GetUniqueMessageMatching(
ServiceWorkerMsg_ActivateEvent::ID));
EXPECT_TRUE(helper_->ipc_sink()->GetUniqueMessageMatching(
EmbeddedWorkerMsg_StopWorker::ID));
EXPECT_NE(kInvalidServiceWorkerRegistrationId, registration_id);
context()->storage()->FindRegistrationForId(
registration_id,
GURL("http://www.example.com"),
base::Bind(&ExpectRegisteredWorkers,
SERVICE_WORKER_OK,
false /* expect_waiting */,
true /* expect_active */));
base::RunLoop().RunUntilIdle();
}
// Test registration when the service worker rejects the install event. The
// registration callback should indicate success, but there should be no waiting
// or active worker in the registration.
TEST_F(ServiceWorkerContextTest, Register_RejectInstall) {
helper_.reset(); // Make sure the process lookups stay overridden.
helper_.reset(new RejectInstallTestHelper(render_process_id_));
int64 registration_id = kInvalidServiceWorkerRegistrationId;
bool called = false;
context()->RegisterServiceWorker(
GURL("http://www.example.com/"),
GURL("http://www.example.com/service_worker.js"),
NULL,
MakeRegisteredCallback(&called, &registration_id));
ASSERT_FALSE(called);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(called);
EXPECT_EQ(3UL, helper_->ipc_sink()->message_count());
EXPECT_TRUE(helper_->ipc_sink()->GetUniqueMessageMatching(
EmbeddedWorkerMsg_StartWorker::ID));
EXPECT_TRUE(helper_->inner_ipc_sink()->GetUniqueMessageMatching(
ServiceWorkerMsg_InstallEvent::ID));
EXPECT_FALSE(helper_->inner_ipc_sink()->GetUniqueMessageMatching(
ServiceWorkerMsg_ActivateEvent::ID));
EXPECT_TRUE(helper_->ipc_sink()->GetUniqueMessageMatching(
EmbeddedWorkerMsg_StopWorker::ID));
EXPECT_NE(kInvalidServiceWorkerRegistrationId, registration_id);
context()->storage()->FindRegistrationForId(
registration_id,
GURL("http://www.example.com"),
base::Bind(&ExpectRegisteredWorkers,
SERVICE_WORKER_ERROR_NOT_FOUND,
false /* expect_waiting */,
false /* expect_active */));
base::RunLoop().RunUntilIdle();
}
// Test registration when the service worker rejects the activate event. The
// registration callback should indicate success, but there should be no waiting
// or active worker in the registration.
TEST_F(ServiceWorkerContextTest, Register_RejectActivate) {
helper_.reset(); // Make sure the process lookups stay overridden.
helper_.reset(new RejectActivateTestHelper(render_process_id_));
int64 registration_id = kInvalidServiceWorkerRegistrationId;
bool called = false;
context()->RegisterServiceWorker(
GURL("http://www.example.com/"),
GURL("http://www.example.com/service_worker.js"),
NULL,
MakeRegisteredCallback(&called, &registration_id));
ASSERT_FALSE(called);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(called);
EXPECT_EQ(4UL, helper_->ipc_sink()->message_count());
EXPECT_TRUE(helper_->ipc_sink()->GetUniqueMessageMatching(
EmbeddedWorkerMsg_StartWorker::ID));
EXPECT_TRUE(helper_->inner_ipc_sink()->GetUniqueMessageMatching(
ServiceWorkerMsg_InstallEvent::ID));
EXPECT_TRUE(helper_->inner_ipc_sink()->GetUniqueMessageMatching(
ServiceWorkerMsg_ActivateEvent::ID));
EXPECT_TRUE(helper_->ipc_sink()->GetUniqueMessageMatching(
EmbeddedWorkerMsg_StopWorker::ID));
EXPECT_NE(kInvalidServiceWorkerRegistrationId, registration_id);
context()->storage()->FindRegistrationForId(
registration_id,
GURL("http://www.example.com"),
base::Bind(&ExpectRegisteredWorkers,
SERVICE_WORKER_ERROR_NOT_FOUND,
false /* expect_waiting */,
false /* expect_active */));
base::RunLoop().RunUntilIdle();
}
// Make sure registrations are cleaned up when they are unregistered.
TEST_F(ServiceWorkerContextTest, Unregister) {
GURL pattern("http://www.example.com/");
bool called = false;
int64 registration_id = kInvalidServiceWorkerRegistrationId;
context()->RegisterServiceWorker(
pattern,
GURL("http://www.example.com/service_worker.js"),
NULL,
MakeRegisteredCallback(&called, &registration_id));
ASSERT_FALSE(called);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(called);
EXPECT_NE(kInvalidServiceWorkerRegistrationId, registration_id);
called = false;
context()->UnregisterServiceWorker(pattern,
MakeUnregisteredCallback(&called));
ASSERT_FALSE(called);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(called);
context()->storage()->FindRegistrationForId(
registration_id,
pattern.GetOrigin(),
base::Bind(&ExpectRegisteredWorkers,
SERVICE_WORKER_ERROR_NOT_FOUND,
false /* expect_waiting */,
false /* expect_active */));
base::RunLoop().RunUntilIdle();
}
// Make sure registrations are cleaned up when they are unregistered in bulk.
TEST_F(ServiceWorkerContextTest, UnregisterMultiple) {
GURL origin1_p1("http://www.example.com/test");
GURL origin1_p2("http://www.example.com/hello");
GURL origin2_p1("http://www.example.com:8080/again");
GURL origin3_p1("http://www.other.com/");
bool called = false;
int64 registration_id1 = kInvalidServiceWorkerRegistrationId;
int64 registration_id2 = kInvalidServiceWorkerRegistrationId;
int64 registration_id3 = kInvalidServiceWorkerRegistrationId;
int64 registration_id4 = kInvalidServiceWorkerRegistrationId;
context()->RegisterServiceWorker(
origin1_p1,
GURL("http://www.example.com/service_worker.js"),
NULL,
MakeRegisteredCallback(&called, &registration_id1));
context()->RegisterServiceWorker(
origin1_p2,
GURL("http://www.example.com/service_worker2.js"),
NULL,
MakeRegisteredCallback(&called, &registration_id2));
context()->RegisterServiceWorker(
origin2_p1,
GURL("http://www.example.com:8080/service_worker3.js"),
NULL,
MakeRegisteredCallback(&called, &registration_id3));
context()->RegisterServiceWorker(
origin3_p1,
GURL("http://www.other.com/service_worker4.js"),
NULL,
MakeRegisteredCallback(&called, &registration_id4));
ASSERT_FALSE(called);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(called);
EXPECT_NE(kInvalidServiceWorkerRegistrationId, registration_id1);
EXPECT_NE(kInvalidServiceWorkerRegistrationId, registration_id2);
EXPECT_NE(kInvalidServiceWorkerRegistrationId, registration_id3);
EXPECT_NE(kInvalidServiceWorkerRegistrationId, registration_id4);
called = false;
context()->UnregisterServiceWorkers(origin1_p1.GetOrigin(),
MakeUnregisteredCallback(&called));
ASSERT_FALSE(called);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(called);
context()->storage()->FindRegistrationForId(
registration_id1,
origin1_p1.GetOrigin(),
base::Bind(&ExpectRegisteredWorkers,
SERVICE_WORKER_ERROR_NOT_FOUND,
false /* expect_waiting */,
false /* expect_active */));
context()->storage()->FindRegistrationForId(
registration_id2,
origin1_p2.GetOrigin(),
base::Bind(&ExpectRegisteredWorkers,
SERVICE_WORKER_ERROR_NOT_FOUND,
false /* expect_waiting */,
false /* expect_active */));
context()->storage()->FindRegistrationForId(
registration_id3,
origin2_p1.GetOrigin(),
base::Bind(&ExpectRegisteredWorkers,
SERVICE_WORKER_OK,
false /* expect_waiting */,
true /* expect_active */));
context()->storage()->FindRegistrationForId(
registration_id4,
origin3_p1.GetOrigin(),
base::Bind(&ExpectRegisteredWorkers,
SERVICE_WORKER_OK,
false /* expect_waiting */,
true /* expect_active */));
base::RunLoop().RunUntilIdle();
}
// Make sure registering a new script shares an existing registration.
TEST_F(ServiceWorkerContextTest, RegisterNewScript) {
GURL pattern("http://www.example.com/");
bool called = false;
int64 old_registration_id = kInvalidServiceWorkerRegistrationId;
context()->RegisterServiceWorker(
pattern,
GURL("http://www.example.com/service_worker.js"),
NULL,
MakeRegisteredCallback(&called, &old_registration_id));
ASSERT_FALSE(called);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(called);
EXPECT_NE(kInvalidServiceWorkerRegistrationId, old_registration_id);
called = false;
int64 new_registration_id = kInvalidServiceWorkerRegistrationId;
context()->RegisterServiceWorker(
pattern,
GURL("http://www.example.com/service_worker_new.js"),
NULL,
MakeRegisteredCallback(&called, &new_registration_id));
ASSERT_FALSE(called);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(called);
EXPECT_NE(kInvalidServiceWorkerRegistrationId, new_registration_id);
EXPECT_EQ(old_registration_id, new_registration_id);
}
// Make sure that when registering a duplicate pattern+script_url
// combination, that the same registration is used.
TEST_F(ServiceWorkerContextTest, RegisterDuplicateScript) {
GURL pattern("http://www.example.com/");
GURL script_url("http://www.example.com/service_worker.js");
bool called = false;
int64 old_registration_id = kInvalidServiceWorkerRegistrationId;
context()->RegisterServiceWorker(
pattern,
script_url,
NULL,
MakeRegisteredCallback(&called, &old_registration_id));
ASSERT_FALSE(called);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(called);
EXPECT_NE(kInvalidServiceWorkerRegistrationId, old_registration_id);
called = false;
int64 new_registration_id = kInvalidServiceWorkerRegistrationId;
context()->RegisterServiceWorker(
pattern,
script_url,
NULL,
MakeRegisteredCallback(&called, &new_registration_id));
ASSERT_FALSE(called);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(called);
EXPECT_EQ(old_registration_id, new_registration_id);
}
// TODO(nhiroki): Test this for on-disk storage.
TEST_F(ServiceWorkerContextTest, DeleteAndStartOver) {
int64 registration_id = kInvalidServiceWorkerRegistrationId;
bool called = false;
context()->RegisterServiceWorker(
GURL("http://www.example.com/"),
GURL("http://www.example.com/service_worker.js"),
NULL,
MakeRegisteredCallback(&called, &registration_id));
ASSERT_FALSE(called);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(called);
context()->storage()->FindRegistrationForId(
registration_id,
GURL("http://www.example.com"),
base::Bind(&ExpectRegisteredWorkers,
SERVICE_WORKER_OK,
false /* expect_waiting */,
true /* expect_active */));
base::RunLoop().RunUntilIdle();
// Next handle ids should be 0 (the next call should return 1).
EXPECT_EQ(0, context()->GetNewServiceWorkerHandleId());
EXPECT_EQ(0, context()->GetNewRegistrationHandleId());
context()->ScheduleDeleteAndStartOver();
// The storage is disabled while the recovery process is running, so the
// operation should be failed.
context()->storage()->FindRegistrationForId(
registration_id,
GURL("http://www.example.com"),
base::Bind(&ExpectRegisteredWorkers,
SERVICE_WORKER_ERROR_FAILED,
false /* expect_waiting */,
true /* expect_active */));
base::RunLoop().RunUntilIdle();
// The context started over and the storage was re-initialized, so the
// registration should not be found.
context()->storage()->FindRegistrationForId(
registration_id,
GURL("http://www.example.com"),
base::Bind(&ExpectRegisteredWorkers,
SERVICE_WORKER_ERROR_NOT_FOUND,
false /* expect_waiting */,
true /* expect_active */));
base::RunLoop().RunUntilIdle();
called = false;
context()->RegisterServiceWorker(
GURL("http://www.example.com/"),
GURL("http://www.example.com/service_worker.js"),
NULL,
MakeRegisteredCallback(&called, &registration_id));
ASSERT_FALSE(called);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(called);
context()->storage()->FindRegistrationForId(
registration_id,
GURL("http://www.example.com"),
base::Bind(&ExpectRegisteredWorkers,
SERVICE_WORKER_OK,
false /* expect_waiting */,
true /* expect_active */));
base::RunLoop().RunUntilIdle();
// The new context should take over next handle ids.
EXPECT_EQ(1, context()->GetNewServiceWorkerHandleId());
EXPECT_EQ(1, context()->GetNewRegistrationHandleId());
}
} // namespace content