blob: 54a2cc966fc17eaa0f03ec66515bbcee180d39e6 [file] [log] [blame]
// 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 "base/basictypes.h"
#include "base/callback.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "content/browser/fileapi/chrome_blob_storage_context.h"
#include "content/browser/fileapi/mock_url_request_delegate.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_provider_host.h"
#include "content/browser/service_worker/service_worker_registration.h"
#include "content/browser/service_worker/service_worker_test_utils.h"
#include "content/browser/service_worker/service_worker_url_request_job.h"
#include "content/browser/service_worker/service_worker_version.h"
#include "content/common/service_worker/service_worker_messages.h"
#include "content/public/browser/blob_handle.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "net/base/io_buffer.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_response_headers.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_context.h"
#include "net/url_request/url_request_job_factory_impl.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webkit/browser/blob/blob_storage_context.h"
#include "webkit/browser/blob/blob_url_request_job.h"
#include "webkit/browser/blob/blob_url_request_job_factory.h"
#include "webkit/common/blob/blob_data.h"
namespace content {
class ServiceWorkerURLRequestJobTest;
namespace {
const int kProcessID = 1;
const int kProviderID = 100;
const char kTestData[] = "Here is sample text for the blob.";
class MockHttpProtocolHandler
: public net::URLRequestJobFactory::ProtocolHandler {
public:
MockHttpProtocolHandler(
base::WeakPtr<ServiceWorkerProviderHost> provider_host,
base::WeakPtr<webkit_blob::BlobStorageContext> blob_storage_context)
: provider_host_(provider_host),
blob_storage_context_(blob_storage_context) {}
virtual ~MockHttpProtocolHandler() {}
virtual net::URLRequestJob* MaybeCreateJob(
net::URLRequest* request,
net::NetworkDelegate* network_delegate) const OVERRIDE {
ServiceWorkerURLRequestJob* job = new ServiceWorkerURLRequestJob(
request, network_delegate, provider_host_, blob_storage_context_);
job->ForwardToServiceWorker();
return job;
}
private:
base::WeakPtr<ServiceWorkerProviderHost> provider_host_;
base::WeakPtr<webkit_blob::BlobStorageContext> blob_storage_context_;
};
// Returns a BlobProtocolHandler that uses |blob_storage_context|. Caller owns
// the memory.
webkit_blob::BlobProtocolHandler* CreateMockBlobProtocolHandler(
webkit_blob::BlobStorageContext* blob_storage_context) {
// The FileSystemContext and MessageLoopProxy are not actually used but a
// MessageLoopProxy is needed to avoid a DCHECK in BlobURLRequestJob ctor.
return new webkit_blob::BlobProtocolHandler(
blob_storage_context, NULL, base::MessageLoopProxy::current().get());
}
} // namespace
class ServiceWorkerURLRequestJobTest : public testing::Test {
protected:
ServiceWorkerURLRequestJobTest()
: thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
blob_data_(new webkit_blob::BlobData("blob-id:myblob")) {}
virtual ~ServiceWorkerURLRequestJobTest() {}
virtual void SetUp() OVERRIDE {
browser_context_.reset(new TestBrowserContext);
SetUpWithHelper(new EmbeddedWorkerTestHelper(kProcessID));
}
void SetUpWithHelper(EmbeddedWorkerTestHelper* helper) {
helper_.reset(helper);
registration_ = new ServiceWorkerRegistration(
GURL("http://example.com/*"),
GURL("http://example.com/service_worker.js"),
1L,
helper_->context()->AsWeakPtr());
version_ = new ServiceWorkerVersion(
registration_, 1L, helper_->context()->AsWeakPtr());
scoped_ptr<ServiceWorkerProviderHost> provider_host(
new ServiceWorkerProviderHost(
kProcessID, kProviderID, helper_->context()->AsWeakPtr(), NULL));
provider_host->SetActiveVersion(version_.get());
ChromeBlobStorageContext* chrome_blob_storage_context =
ChromeBlobStorageContext::GetFor(browser_context_.get());
// Wait for chrome_blob_storage_context to finish initializing.
base::RunLoop().RunUntilIdle();
webkit_blob::BlobStorageContext* blob_storage_context =
chrome_blob_storage_context->context();
url_request_job_factory_.reset(new net::URLRequestJobFactoryImpl);
url_request_job_factory_->SetProtocolHandler(
"http",
new MockHttpProtocolHandler(provider_host->AsWeakPtr(),
blob_storage_context->AsWeakPtr()));
url_request_job_factory_->SetProtocolHandler(
"blob", CreateMockBlobProtocolHandler(blob_storage_context));
url_request_context_.set_job_factory(url_request_job_factory_.get());
helper_->context()->AddProviderHost(provider_host.Pass());
}
virtual void TearDown() OVERRIDE {
version_ = NULL;
registration_ = NULL;
helper_.reset();
}
void TestRequest(int expected_status_code,
const std::string& expected_status_text,
const std::string& expected_response) {
request_ = url_request_context_.CreateRequest(
GURL("http://example.com/foo.html"),
net::DEFAULT_PRIORITY,
&url_request_delegate_,
NULL);
request_->set_method("GET");
request_->Start();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(request_->status().is_success());
EXPECT_EQ(expected_status_code,
request_->response_headers()->response_code());
EXPECT_EQ(expected_status_text,
request_->response_headers()->GetStatusText());
EXPECT_EQ(expected_response, url_request_delegate_.response_data());
}
TestBrowserThreadBundle thread_bundle_;
scoped_ptr<TestBrowserContext> browser_context_;
scoped_ptr<EmbeddedWorkerTestHelper> helper_;
scoped_refptr<ServiceWorkerRegistration> registration_;
scoped_refptr<ServiceWorkerVersion> version_;
scoped_ptr<net::URLRequestJobFactoryImpl> url_request_job_factory_;
net::URLRequestContext url_request_context_;
MockURLRequestDelegate url_request_delegate_;
scoped_ptr<net::URLRequest> request_;
scoped_refptr<webkit_blob::BlobData> blob_data_;
DISALLOW_COPY_AND_ASSIGN(ServiceWorkerURLRequestJobTest);
};
TEST_F(ServiceWorkerURLRequestJobTest, Simple) {
version_->SetStatus(ServiceWorkerVersion::ACTIVE);
TestRequest(200, "OK", std::string());
}
TEST_F(ServiceWorkerURLRequestJobTest, WaitForActivation) {
ServiceWorkerStatusCode status = SERVICE_WORKER_ERROR_FAILED;
version_->SetStatus(ServiceWorkerVersion::INSTALLED);
version_->DispatchActivateEvent(CreateReceiverOnCurrentThread(&status));
TestRequest(200, "OK", std::string());
EXPECT_EQ(SERVICE_WORKER_OK, status);
}
// Responds to fetch events with a blob.
class BlobResponder : public EmbeddedWorkerTestHelper {
public:
BlobResponder(int mock_render_process_id, const std::string& blob_uuid)
: EmbeddedWorkerTestHelper(mock_render_process_id),
blob_uuid_(blob_uuid) {}
virtual ~BlobResponder() {}
protected:
virtual void OnFetchEvent(int embedded_worker_id,
int request_id,
const ServiceWorkerFetchRequest& request) OVERRIDE {
SimulateSend(new ServiceWorkerHostMsg_FetchEventFinished(
embedded_worker_id,
request_id,
SERVICE_WORKER_FETCH_EVENT_RESULT_RESPONSE,
ServiceWorkerResponse(200,
"OK",
std::map<std::string, std::string>(),
blob_uuid_)));
}
std::string blob_uuid_;
DISALLOW_COPY_AND_ASSIGN(BlobResponder);
};
TEST_F(ServiceWorkerURLRequestJobTest, BlobResponse) {
ChromeBlobStorageContext* blob_storage_context =
ChromeBlobStorageContext::GetFor(browser_context_.get());
std::string expected_response;
for (int i = 0; i < 1024; ++i) {
blob_data_->AppendData(kTestData);
expected_response += kTestData;
}
scoped_ptr<webkit_blob::BlobDataHandle> blob_handle =
blob_storage_context->context()->AddFinishedBlob(blob_data_);
SetUpWithHelper(new BlobResponder(kProcessID, blob_handle->uuid()));
version_->SetStatus(ServiceWorkerVersion::ACTIVE);
TestRequest(200, "OK", expected_response);
}
TEST_F(ServiceWorkerURLRequestJobTest, NonExistentBlobUUIDResponse) {
SetUpWithHelper(new BlobResponder(kProcessID, "blob-id:nothing-is-here"));
version_->SetStatus(ServiceWorkerVersion::ACTIVE);
TestRequest(500, "Service Worker Response Error", std::string());
}
// TODO(kinuko): Add more tests with different response data and also for
// FallbackToNetwork case.
} // namespace content