blob: 3aec7f1151fecc2917f14396749cb05ecfada710 [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/browser/service_worker/service_worker_dispatcher_host.h"
#include <stdint.h>
#include <utility>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "content/browser/browser_thread_impl.h"
#include "content/browser/message_port_service.h"
#include "content/browser/service_worker/embedded_worker_instance.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_context_wrapper.h"
#include "content/browser/service_worker/service_worker_handle.h"
#include "content/common/service_worker/embedded_worker_messages.h"
#include "content/common/service_worker/service_worker_messages.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/mock_resource_context.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/test/test_content_browser_client.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace {
static void SaveStatusCallback(bool* called,
ServiceWorkerStatusCode* out,
ServiceWorkerStatusCode status) {
*called = true;
*out = status;
}
void SetUpDummyMessagePort(std::vector<int>* ports) {
int port_id = -1;
MessagePortService::GetInstance()->Create(MSG_ROUTING_NONE, nullptr,
&port_id);
ports->push_back(port_id);
}
} // namespace
static const int kRenderFrameId = 1;
class TestingServiceWorkerDispatcherHost : public ServiceWorkerDispatcherHost {
public:
TestingServiceWorkerDispatcherHost(
int process_id,
ServiceWorkerContextWrapper* context_wrapper,
ResourceContext* resource_context,
EmbeddedWorkerTestHelper* helper)
: ServiceWorkerDispatcherHost(process_id, NULL, resource_context),
bad_messages_received_count_(0),
helper_(helper) {
Init(context_wrapper);
}
bool Send(IPC::Message* message) override { return helper_->Send(message); }
IPC::TestSink* ipc_sink() { return helper_->ipc_sink(); }
void ShutdownForBadMessage() override { ++bad_messages_received_count_; }
int bad_messages_received_count_;
protected:
EmbeddedWorkerTestHelper* helper_;
~TestingServiceWorkerDispatcherHost() override {}
};
class FailToStartWorkerTestHelper : public EmbeddedWorkerTestHelper {
public:
FailToStartWorkerTestHelper() : EmbeddedWorkerTestHelper(base::FilePath()) {}
void OnStartWorker(int embedded_worker_id,
int64_t service_worker_version_id,
const GURL& scope,
const GURL& script_url,
bool pause_after_download) override {
EmbeddedWorkerInstance* worker = registry()->GetWorker(embedded_worker_id);
registry()->OnWorkerStopped(worker->process_id(), embedded_worker_id);
}
};
class ServiceWorkerDispatcherHostTest : public testing::Test {
protected:
ServiceWorkerDispatcherHostTest()
: browser_thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP) {}
void SetUp() override {
Initialize(
base::WrapUnique(new EmbeddedWorkerTestHelper(base::FilePath())));
}
void TearDown() override {
version_ = nullptr;
registration_ = nullptr;
helper_.reset();
}
ServiceWorkerContextCore* context() { return helper_->context(); }
ServiceWorkerContextWrapper* context_wrapper() {
return helper_->context_wrapper();
}
void Initialize(std::unique_ptr<EmbeddedWorkerTestHelper> helper) {
helper_.reset(helper.release());
dispatcher_host_ = new TestingServiceWorkerDispatcherHost(
helper_->mock_render_process_id(), context_wrapper(),
&resource_context_, helper_.get());
}
void SetUpRegistration(const GURL& scope, const GURL& script_url) {
registration_ = new ServiceWorkerRegistration(
scope, 1L, helper_->context()->AsWeakPtr());
version_ = new ServiceWorkerVersion(registration_.get(), script_url, 1L,
helper_->context()->AsWeakPtr());
std::vector<ServiceWorkerDatabase::ResourceRecord> records;
records.push_back(
ServiceWorkerDatabase::ResourceRecord(10, version_->script_url(), 100));
version_->script_cache_map()->SetResources(records);
// Make the registration findable via storage functions.
helper_->context()->storage()->LazyInitialize(base::Bind(&base::DoNothing));
base::RunLoop().RunUntilIdle();
bool called = false;
ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_MAX_VALUE;
helper_->context()->storage()->StoreRegistration(
registration_.get(), version_.get(),
base::Bind(&SaveStatusCallback, &called, &status));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(called);
EXPECT_EQ(SERVICE_WORKER_OK, status);
}
void SendSetHostedVersionId(int provider_id, int64_t version_id) {
dispatcher_host_->OnMessageReceived(
ServiceWorkerHostMsg_SetVersionId(provider_id, version_id));
}
void SendProviderCreated(ServiceWorkerProviderType type,
const GURL& pattern) {
const int64_t kProviderId = 99;
dispatcher_host_->OnMessageReceived(ServiceWorkerHostMsg_ProviderCreated(
kProviderId, MSG_ROUTING_NONE, type,
true /* is_parent_frame_secure */));
helper_->SimulateAddProcessToPattern(pattern,
helper_->mock_render_process_id());
provider_host_ = context()->GetProviderHost(
helper_->mock_render_process_id(), kProviderId);
}
void SendRegister(int64_t provider_id, GURL pattern, GURL worker_url) {
dispatcher_host_->OnMessageReceived(
ServiceWorkerHostMsg_RegisterServiceWorker(
-1, -1, provider_id, pattern, worker_url));
base::RunLoop().RunUntilIdle();
}
void Register(int64_t provider_id,
GURL pattern,
GURL worker_url,
uint32_t expected_message) {
SendRegister(provider_id, pattern, worker_url);
EXPECT_TRUE(dispatcher_host_->ipc_sink()->GetUniqueMessageMatching(
expected_message));
dispatcher_host_->ipc_sink()->ClearMessages();
}
void SendUnregister(int64_t provider_id, int64_t registration_id) {
dispatcher_host_->OnMessageReceived(
ServiceWorkerHostMsg_UnregisterServiceWorker(-1, -1, provider_id,
registration_id));
base::RunLoop().RunUntilIdle();
}
void Unregister(int64_t provider_id,
int64_t registration_id,
uint32_t expected_message) {
SendUnregister(provider_id, registration_id);
EXPECT_TRUE(dispatcher_host_->ipc_sink()->GetUniqueMessageMatching(
expected_message));
dispatcher_host_->ipc_sink()->ClearMessages();
}
void SendGetRegistration(int64_t provider_id, GURL document_url) {
dispatcher_host_->OnMessageReceived(
ServiceWorkerHostMsg_GetRegistration(
-1, -1, provider_id, document_url));
base::RunLoop().RunUntilIdle();
}
void GetRegistration(int64_t provider_id,
GURL document_url,
uint32_t expected_message) {
SendGetRegistration(provider_id, document_url);
EXPECT_TRUE(dispatcher_host_->ipc_sink()->GetUniqueMessageMatching(
expected_message));
dispatcher_host_->ipc_sink()->ClearMessages();
}
void SendGetRegistrations(int64_t provider_id) {
dispatcher_host_->OnMessageReceived(
ServiceWorkerHostMsg_GetRegistrations(-1, -1, provider_id));
base::RunLoop().RunUntilIdle();
}
void GetRegistrations(int64_t provider_id, uint32_t expected_message) {
SendGetRegistrations(provider_id);
EXPECT_TRUE(dispatcher_host_->ipc_sink()->GetUniqueMessageMatching(
expected_message));
dispatcher_host_->ipc_sink()->ClearMessages();
}
void DispatchExtendableMessageEvent(
scoped_refptr<ServiceWorkerVersion> worker,
const base::string16& message,
const url::Origin& source_origin,
const std::vector<int>& sent_message_ports,
ServiceWorkerProviderHost* sender_provider_host,
const ServiceWorkerDispatcherHost::StatusCallback& callback) {
dispatcher_host_->DispatchExtendableMessageEvent(
std::move(worker), message, source_origin, sent_message_ports,
sender_provider_host, callback);
}
ServiceWorkerProviderHost* CreateServiceWorkerProviderHost(int provider_id) {
return new ServiceWorkerProviderHost(
helper_->mock_render_process_id(), kRenderFrameId, provider_id,
SERVICE_WORKER_PROVIDER_FOR_WINDOW,
ServiceWorkerProviderHost::FrameSecurityLevel::SECURE,
context()->AsWeakPtr(), dispatcher_host_.get());
}
TestBrowserThreadBundle browser_thread_bundle_;
content::MockResourceContext resource_context_;
std::unique_ptr<EmbeddedWorkerTestHelper> helper_;
scoped_refptr<TestingServiceWorkerDispatcherHost> dispatcher_host_;
scoped_refptr<ServiceWorkerRegistration> registration_;
scoped_refptr<ServiceWorkerVersion> version_;
ServiceWorkerProviderHost* provider_host_;
};
class ServiceWorkerTestContentBrowserClient : public TestContentBrowserClient {
public:
ServiceWorkerTestContentBrowserClient() {}
bool AllowServiceWorker(const GURL& scope,
const GURL& first_party,
content::ResourceContext* context,
int render_process_id,
int render_frame_id) override {
return false;
}
};
TEST_F(ServiceWorkerDispatcherHostTest,
Register_ContentSettingsDisallowsServiceWorker) {
ServiceWorkerTestContentBrowserClient test_browser_client;
ContentBrowserClient* old_browser_client =
SetBrowserClientForTesting(&test_browser_client);
const int64_t kProviderId = 99; // Dummy value
std::unique_ptr<ServiceWorkerProviderHost> host(
CreateServiceWorkerProviderHost(kProviderId));
host->SetDocumentUrl(GURL("https://www.example.com/foo"));
context()->AddProviderHost(std::move(host));
Register(kProviderId,
GURL("https://www.example.com/"),
GURL("https://www.example.com/bar"),
ServiceWorkerMsg_ServiceWorkerRegistrationError::ID);
GetRegistration(kProviderId,
GURL("https://www.example.com/"),
ServiceWorkerMsg_ServiceWorkerGetRegistrationError::ID);
GetRegistrations(kProviderId,
ServiceWorkerMsg_ServiceWorkerGetRegistrationsError::ID);
// Add a registration into a live registration map so that Unregister() can
// find it.
const int64_t kRegistrationId = 999; // Dummy value
scoped_refptr<ServiceWorkerRegistration> registration(
new ServiceWorkerRegistration(GURL("https://www.example.com/"),
kRegistrationId, context()->AsWeakPtr()));
Unregister(kProviderId, kRegistrationId,
ServiceWorkerMsg_ServiceWorkerUnregistrationError::ID);
SetBrowserClientForTesting(old_browser_client);
}
TEST_F(ServiceWorkerDispatcherHostTest, Register_HTTPS) {
const int64_t kProviderId = 99; // Dummy value
std::unique_ptr<ServiceWorkerProviderHost> host(
CreateServiceWorkerProviderHost(kProviderId));
host->SetDocumentUrl(GURL("https://www.example.com/foo"));
context()->AddProviderHost(std::move(host));
Register(kProviderId,
GURL("https://www.example.com/"),
GURL("https://www.example.com/bar"),
ServiceWorkerMsg_ServiceWorkerRegistered::ID);
}
TEST_F(ServiceWorkerDispatcherHostTest, Register_NonSecureTransportLocalhost) {
const int64_t kProviderId = 99; // Dummy value
std::unique_ptr<ServiceWorkerProviderHost> host(
CreateServiceWorkerProviderHost(kProviderId));
host->SetDocumentUrl(GURL("http://127.0.0.3:81/foo"));
context()->AddProviderHost(std::move(host));
Register(kProviderId,
GURL("http://127.0.0.3:81/bar"),
GURL("http://127.0.0.3:81/baz"),
ServiceWorkerMsg_ServiceWorkerRegistered::ID);
}
TEST_F(ServiceWorkerDispatcherHostTest, Register_InvalidScopeShouldFail) {
const int64_t kProviderId = 99; // Dummy value
std::unique_ptr<ServiceWorkerProviderHost> host(
CreateServiceWorkerProviderHost(kProviderId));
host->SetDocumentUrl(GURL("https://www.example.com/foo"));
context()->AddProviderHost(std::move(host));
SendRegister(kProviderId, GURL(""),
GURL("https://www.example.com/bar/hoge.js"));
EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_);
}
TEST_F(ServiceWorkerDispatcherHostTest, Register_InvalidScriptShouldFail) {
const int64_t kProviderId = 99; // Dummy value
std::unique_ptr<ServiceWorkerProviderHost> host(
CreateServiceWorkerProviderHost(kProviderId));
host->SetDocumentUrl(GURL("https://www.example.com/foo"));
context()->AddProviderHost(std::move(host));
SendRegister(kProviderId, GURL("https://www.example.com/bar/"), GURL(""));
EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_);
}
TEST_F(ServiceWorkerDispatcherHostTest, Register_NonSecureOriginShouldFail) {
const int64_t kProviderId = 99; // Dummy value
std::unique_ptr<ServiceWorkerProviderHost> host(
CreateServiceWorkerProviderHost(kProviderId));
host->SetDocumentUrl(GURL("http://www.example.com/foo"));
context()->AddProviderHost(std::move(host));
SendRegister(kProviderId,
GURL("http://www.example.com/"),
GURL("http://www.example.com/bar"));
EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_);
}
TEST_F(ServiceWorkerDispatcherHostTest, Register_CrossOriginShouldFail) {
const int64_t kProviderId = 99; // Dummy value
std::unique_ptr<ServiceWorkerProviderHost> host(
CreateServiceWorkerProviderHost(kProviderId));
host->SetDocumentUrl(GURL("https://www.example.com/foo"));
context()->AddProviderHost(std::move(host));
// Script has a different host
SendRegister(kProviderId,
GURL("https://www.example.com/"),
GURL("https://foo.example.com/bar"));
EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_);
// Scope has a different host
SendRegister(kProviderId,
GURL("https://foo.example.com/"),
GURL("https://www.example.com/bar"));
EXPECT_EQ(2, dispatcher_host_->bad_messages_received_count_);
// Script has a different port
SendRegister(kProviderId,
GURL("https://www.example.com/"),
GURL("https://www.example.com:8080/bar"));
EXPECT_EQ(3, dispatcher_host_->bad_messages_received_count_);
// Scope has a different transport
SendRegister(kProviderId,
GURL("wss://www.example.com/"),
GURL("https://www.example.com/bar"));
EXPECT_EQ(4, dispatcher_host_->bad_messages_received_count_);
// Script and scope have a different host but match each other
SendRegister(kProviderId,
GURL("https://foo.example.com/"),
GURL("https://foo.example.com/bar"));
EXPECT_EQ(5, dispatcher_host_->bad_messages_received_count_);
// Script and scope URLs are invalid
SendRegister(kProviderId,
GURL(),
GURL("h@ttps://@"));
EXPECT_EQ(6, dispatcher_host_->bad_messages_received_count_);
}
TEST_F(ServiceWorkerDispatcherHostTest, Register_BadCharactersShouldFail) {
const int64_t kProviderId = 99; // Dummy value
std::unique_ptr<ServiceWorkerProviderHost> host(
CreateServiceWorkerProviderHost(kProviderId));
host->SetDocumentUrl(GURL("https://www.example.com/"));
context()->AddProviderHost(std::move(host));
SendRegister(kProviderId, GURL("https://www.example.com/%2f"),
GURL("https://www.example.com/"));
EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_);
SendRegister(kProviderId, GURL("https://www.example.com/%2F"),
GURL("https://www.example.com/"));
EXPECT_EQ(2, dispatcher_host_->bad_messages_received_count_);
SendRegister(kProviderId, GURL("https://www.example.com/"),
GURL("https://www.example.com/%2f"));
EXPECT_EQ(3, dispatcher_host_->bad_messages_received_count_);
SendRegister(kProviderId, GURL("https://www.example.com/%5c"),
GURL("https://www.example.com/"));
EXPECT_EQ(4, dispatcher_host_->bad_messages_received_count_);
SendRegister(kProviderId, GURL("https://www.example.com/"),
GURL("https://www.example.com/%5c"));
EXPECT_EQ(5, dispatcher_host_->bad_messages_received_count_);
SendRegister(kProviderId, GURL("https://www.example.com/"),
GURL("https://www.example.com/%5C"));
EXPECT_EQ(6, dispatcher_host_->bad_messages_received_count_);
}
TEST_F(ServiceWorkerDispatcherHostTest,
Register_FileSystemDocumentShouldFail) {
const int64_t kProviderId = 99; // Dummy value
std::unique_ptr<ServiceWorkerProviderHost> host(
CreateServiceWorkerProviderHost(kProviderId));
host->SetDocumentUrl(GURL("filesystem:https://www.example.com/temporary/a"));
context()->AddProviderHost(std::move(host));
SendRegister(kProviderId,
GURL("filesystem:https://www.example.com/temporary/"),
GURL("https://www.example.com/temporary/bar"));
EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_);
SendRegister(kProviderId,
GURL("https://www.example.com/temporary/"),
GURL("filesystem:https://www.example.com/temporary/bar"));
EXPECT_EQ(2, dispatcher_host_->bad_messages_received_count_);
SendRegister(kProviderId,
GURL("filesystem:https://www.example.com/temporary/"),
GURL("filesystem:https://www.example.com/temporary/bar"));
EXPECT_EQ(3, dispatcher_host_->bad_messages_received_count_);
}
TEST_F(ServiceWorkerDispatcherHostTest,
Register_FileSystemScriptOrScopeShouldFail) {
const int64_t kProviderId = 99; // Dummy value
std::unique_ptr<ServiceWorkerProviderHost> host(
CreateServiceWorkerProviderHost(kProviderId));
host->SetDocumentUrl(GURL("https://www.example.com/temporary/"));
context()->AddProviderHost(std::move(host));
SendRegister(kProviderId,
GURL("filesystem:https://www.example.com/temporary/"),
GURL("https://www.example.com/temporary/bar"));
EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_);
SendRegister(kProviderId,
GURL("https://www.example.com/temporary/"),
GURL("filesystem:https://www.example.com/temporary/bar"));
EXPECT_EQ(2, dispatcher_host_->bad_messages_received_count_);
SendRegister(kProviderId,
GURL("filesystem:https://www.example.com/temporary/"),
GURL("filesystem:https://www.example.com/temporary/bar"));
EXPECT_EQ(3, dispatcher_host_->bad_messages_received_count_);
}
TEST_F(ServiceWorkerDispatcherHostTest, EarlyContextDeletion) {
helper_->ShutdownContext();
// Let the shutdown reach the simulated IO thread.
base::RunLoop().RunUntilIdle();
Register(-1,
GURL(),
GURL(),
ServiceWorkerMsg_ServiceWorkerRegistrationError::ID);
}
TEST_F(ServiceWorkerDispatcherHostTest, ProviderCreatedAndDestroyed) {
const int kProviderId = 1001;
int process_id = helper_->mock_render_process_id();
dispatcher_host_->OnMessageReceived(ServiceWorkerHostMsg_ProviderCreated(
kProviderId, MSG_ROUTING_NONE, SERVICE_WORKER_PROVIDER_FOR_WINDOW,
true /* is_parent_frame_secure */));
EXPECT_TRUE(context()->GetProviderHost(process_id, kProviderId));
// Two with the same ID should be seen as a bad message.
dispatcher_host_->OnMessageReceived(ServiceWorkerHostMsg_ProviderCreated(
kProviderId, MSG_ROUTING_NONE, SERVICE_WORKER_PROVIDER_FOR_WINDOW,
true /* is_parent_frame_secure */));
EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_);
dispatcher_host_->OnMessageReceived(
ServiceWorkerHostMsg_ProviderDestroyed(kProviderId));
EXPECT_FALSE(context()->GetProviderHost(process_id, kProviderId));
// Destroying an ID that does not exist warrants a bad message.
dispatcher_host_->OnMessageReceived(
ServiceWorkerHostMsg_ProviderDestroyed(kProviderId));
EXPECT_EQ(2, dispatcher_host_->bad_messages_received_count_);
// Deletion of the dispatcher_host should cause providers for that
// process to get deleted as well.
dispatcher_host_->OnMessageReceived(ServiceWorkerHostMsg_ProviderCreated(
kProviderId, MSG_ROUTING_NONE, SERVICE_WORKER_PROVIDER_FOR_WINDOW,
true /* is_parent_frame_secure */));
EXPECT_TRUE(context()->GetProviderHost(process_id, kProviderId));
EXPECT_TRUE(dispatcher_host_->HasOneRef());
dispatcher_host_ = NULL;
EXPECT_FALSE(context()->GetProviderHost(process_id, kProviderId));
}
TEST_F(ServiceWorkerDispatcherHostTest, GetRegistration_SameOrigin) {
const int64_t kProviderId = 99; // Dummy value
std::unique_ptr<ServiceWorkerProviderHost> host(
CreateServiceWorkerProviderHost(kProviderId));
host->SetDocumentUrl(GURL("https://www.example.com/foo"));
context()->AddProviderHost(std::move(host));
GetRegistration(kProviderId,
GURL("https://www.example.com/"),
ServiceWorkerMsg_DidGetRegistration::ID);
}
TEST_F(ServiceWorkerDispatcherHostTest, GetRegistration_CrossOriginShouldFail) {
const int64_t kProviderId = 99; // Dummy value
std::unique_ptr<ServiceWorkerProviderHost> host(
CreateServiceWorkerProviderHost(kProviderId));
host->SetDocumentUrl(GURL("https://www.example.com/foo"));
context()->AddProviderHost(std::move(host));
SendGetRegistration(kProviderId, GURL("https://foo.example.com/"));
EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_);
}
TEST_F(ServiceWorkerDispatcherHostTest,
GetRegistration_InvalidScopeShouldFail) {
const int64_t kProviderId = 99; // Dummy value
std::unique_ptr<ServiceWorkerProviderHost> host(
CreateServiceWorkerProviderHost(kProviderId));
host->SetDocumentUrl(GURL("https://www.example.com/foo"));
context()->AddProviderHost(std::move(host));
SendGetRegistration(kProviderId, GURL(""));
EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_);
}
TEST_F(ServiceWorkerDispatcherHostTest,
GetRegistration_NonSecureOriginShouldFail) {
const int64_t kProviderId = 99; // Dummy value
std::unique_ptr<ServiceWorkerProviderHost> host(
CreateServiceWorkerProviderHost(kProviderId));
host->SetDocumentUrl(GURL("http://www.example.com/foo"));
context()->AddProviderHost(std::move(host));
SendGetRegistration(kProviderId, GURL("http://www.example.com/"));
EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_);
}
TEST_F(ServiceWorkerDispatcherHostTest, GetRegistration_EarlyContextDeletion) {
helper_->ShutdownContext();
// Let the shutdown reach the simulated IO thread.
base::RunLoop().RunUntilIdle();
GetRegistration(-1,
GURL(),
ServiceWorkerMsg_ServiceWorkerGetRegistrationError::ID);
}
TEST_F(ServiceWorkerDispatcherHostTest, GetRegistrations_SecureOrigin) {
const int64_t kProviderId = 99; // Dummy value
std::unique_ptr<ServiceWorkerProviderHost> host(
CreateServiceWorkerProviderHost(kProviderId));
host->SetDocumentUrl(GURL("https://www.example.com/foo"));
context()->AddProviderHost(std::move(host));
GetRegistrations(kProviderId, ServiceWorkerMsg_DidGetRegistrations::ID);
}
TEST_F(ServiceWorkerDispatcherHostTest,
GetRegistrations_NonSecureOriginShouldFail) {
const int64_t kProviderId = 99; // Dummy value
std::unique_ptr<ServiceWorkerProviderHost> host(
CreateServiceWorkerProviderHost(kProviderId));
host->SetDocumentUrl(GURL("http://www.example.com/foo"));
context()->AddProviderHost(std::move(host));
SendGetRegistrations(kProviderId);
EXPECT_EQ(1, dispatcher_host_->bad_messages_received_count_);
}
TEST_F(ServiceWorkerDispatcherHostTest, GetRegistrations_EarlyContextDeletion) {
helper_->ShutdownContext();
// Let the shutdown reach the simulated IO thread.
base::RunLoop().RunUntilIdle();
GetRegistrations(-1, ServiceWorkerMsg_ServiceWorkerGetRegistrationsError::ID);
}
TEST_F(ServiceWorkerDispatcherHostTest, CleanupOnRendererCrash) {
GURL pattern = GURL("http://www.example.com/");
GURL script_url = GURL("http://www.example.com/service_worker.js");
int process_id = helper_->mock_render_process_id();
SendProviderCreated(SERVICE_WORKER_PROVIDER_FOR_WINDOW, pattern);
SetUpRegistration(pattern, script_url);
int64_t provider_id = provider_host_->provider_id();
// Start up the worker.
bool called = false;
ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_ABORT;
version_->StartWorker(ServiceWorkerMetrics::EventType::UNKNOWN,
base::Bind(&SaveStatusCallback, &called, &status));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(called);
EXPECT_EQ(SERVICE_WORKER_OK, status);
EXPECT_TRUE(context()->GetProviderHost(process_id, provider_id));
EXPECT_EQ(ServiceWorkerVersion::RUNNING, version_->running_status());
// Simulate the render process crashing.
dispatcher_host_->OnFilterRemoved();
// The dispatcher host should clean up the state from the process.
EXPECT_FALSE(context()->GetProviderHost(process_id, provider_id));
EXPECT_EQ(ServiceWorkerVersion::STOPPED, version_->running_status());
// We should be able to hook up a new dispatcher host although the old object
// is not yet destroyed. This is what the browser does when reusing a crashed
// render process.
scoped_refptr<TestingServiceWorkerDispatcherHost> new_dispatcher_host(
new TestingServiceWorkerDispatcherHost(
process_id, context_wrapper(), &resource_context_, helper_.get()));
// To show the new dispatcher can operate, simulate provider creation. Since
// the old dispatcher cleaned up the old provider host, the new one won't
// complain.
new_dispatcher_host->OnMessageReceived(ServiceWorkerHostMsg_ProviderCreated(
provider_id, MSG_ROUTING_NONE, SERVICE_WORKER_PROVIDER_FOR_WINDOW,
true /* is_parent_frame_secure */));
EXPECT_EQ(0, new_dispatcher_host->bad_messages_received_count_);
}
TEST_F(ServiceWorkerDispatcherHostTest, DispatchExtendableMessageEvent) {
GURL pattern = GURL("http://www.example.com/");
GURL script_url = GURL("http://www.example.com/service_worker.js");
SendProviderCreated(SERVICE_WORKER_PROVIDER_FOR_CONTROLLER, pattern);
SetUpRegistration(pattern, script_url);
// Set the running hosted version so that we can retrieve a valid service
// worker object information for the source attribute of the message event.
provider_host_->running_hosted_version_ = version_;
// Set aside the initial refcount of the worker handle.
provider_host_->GetOrCreateServiceWorkerHandle(version_.get());
ServiceWorkerHandle* sender_worker_handle =
dispatcher_host_->FindServiceWorkerHandle(provider_host_->provider_id(),
version_->version_id());
const int ref_count = sender_worker_handle->ref_count();
// Dispatch ExtendableMessageEvent.
std::vector<int> ports;
SetUpDummyMessagePort(&ports);
bool called = false;
ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_MAX_VALUE;
DispatchExtendableMessageEvent(
version_, base::string16(), url::Origin(version_->scope().GetOrigin()),
ports, provider_host_, base::Bind(&SaveStatusCallback, &called, &status));
for (int port : ports)
EXPECT_TRUE(MessagePortService::GetInstance()->AreMessagesHeld(port));
EXPECT_EQ(ref_count + 1, sender_worker_handle->ref_count());
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(called);
EXPECT_EQ(SERVICE_WORKER_OK, status);
// Messages should be held until ports are created at the destination.
for (int port : ports)
EXPECT_TRUE(MessagePortService::GetInstance()->AreMessagesHeld(port));
EXPECT_EQ(ref_count + 1, sender_worker_handle->ref_count());
}
TEST_F(ServiceWorkerDispatcherHostTest, DispatchExtendableMessageEvent_Fail) {
GURL pattern = GURL("http://www.example.com/");
GURL script_url = GURL("http://www.example.com/service_worker.js");
Initialize(base::WrapUnique(new FailToStartWorkerTestHelper));
SendProviderCreated(SERVICE_WORKER_PROVIDER_FOR_CONTROLLER, pattern);
SetUpRegistration(pattern, script_url);
// Set the running hosted version so that we can retrieve a valid service
// worker object information for the source attribute of the message event.
provider_host_->running_hosted_version_ = version_;
// Set aside the initial refcount of the worker handle.
provider_host_->GetOrCreateServiceWorkerHandle(version_.get());
ServiceWorkerHandle* sender_worker_handle =
dispatcher_host_->FindServiceWorkerHandle(provider_host_->provider_id(),
version_->version_id());
const int ref_count = sender_worker_handle->ref_count();
// Try to dispatch ExtendableMessageEvent. This should fail to start the
// worker and to dispatch the event.
std::vector<int> ports;
SetUpDummyMessagePort(&ports);
bool called = false;
ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_MAX_VALUE;
DispatchExtendableMessageEvent(
version_, base::string16(), url::Origin(version_->scope().GetOrigin()),
ports, provider_host_, base::Bind(&SaveStatusCallback, &called, &status));
for (int port : ports)
EXPECT_TRUE(MessagePortService::GetInstance()->AreMessagesHeld(port));
EXPECT_EQ(ref_count + 1, sender_worker_handle->ref_count());
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(called);
EXPECT_EQ(SERVICE_WORKER_ERROR_START_WORKER_FAILED, status);
// The error callback should clean up the ports and handle.
for (int port : ports)
EXPECT_FALSE(MessagePortService::GetInstance()->AreMessagesHeld(port));
EXPECT_EQ(ref_count, sender_worker_handle->ref_count());
}
TEST_F(ServiceWorkerDispatcherHostTest, OnSetHostedVersionId) {
GURL pattern = GURL("http://www.example.com/");
GURL script_url = GURL("http://www.example.com/service_worker.js");
Initialize(base::WrapUnique(new FailToStartWorkerTestHelper));
SendProviderCreated(SERVICE_WORKER_PROVIDER_FOR_CONTROLLER, pattern);
SetUpRegistration(pattern, script_url);
const int64_t kProviderId = 99; // Dummy value
bool called;
ServiceWorkerStatusCode status;
// StartWorker puts the worker in STARTING state but it will have no
// process id yet.
version_->StartWorker(ServiceWorkerMetrics::EventType::UNKNOWN,
base::Bind(&SaveStatusCallback, &called, &status));
EXPECT_NE(version_->embedded_worker()->process_id(),
provider_host_->process_id());
// SendSetHostedVersionId should reject because the provider host process id
// is different.
SendSetHostedVersionId(kProviderId, version_->version_id());
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(dispatcher_host_->ipc_sink()->GetUniqueMessageMatching(
ServiceWorkerMsg_AssociateRegistration::ID));
}
} // namespace content