blob: 7d1d2293df9ac4ede10223192d6de246ef67d565 [file] [log] [blame]
// Copyright 2015 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/cache_storage/cache_storage_dispatcher_host.h"
#include <stddef.h>
#include <utility>
#include "base/bind.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "content/browser/bad_message.h"
#include "content/browser/cache_storage/cache_storage_cache.h"
#include "content/browser/cache_storage/cache_storage_cache_handle.h"
#include "content/browser/cache_storage/cache_storage_context_impl.h"
#include "content/browser/cache_storage/cache_storage_manager.h"
#include "content/common/cache_storage/cache_storage_messages.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/origin_util.h"
#include "storage/browser/blob/blob_data_handle.h"
#include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerCacheError.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace content {
namespace {
const uint32_t kFilteredMessageClasses[] = {CacheStorageMsgStart};
const int32_t kCachePreservationSeconds = 5;
blink::WebServiceWorkerCacheError ToWebServiceWorkerCacheError(
CacheStorageError err) {
switch (err) {
case CACHE_STORAGE_OK:
NOTREACHED();
return blink::WebServiceWorkerCacheErrorNotImplemented;
case CACHE_STORAGE_ERROR_EXISTS:
return blink::WebServiceWorkerCacheErrorExists;
case CACHE_STORAGE_ERROR_STORAGE:
// TODO(nhiroki): Add WebServiceWorkerCacheError equivalent to
// CACHE_STORAGE_ERROR_STORAGE.
return blink::WebServiceWorkerCacheErrorNotFound;
case CACHE_STORAGE_ERROR_NOT_FOUND:
return blink::WebServiceWorkerCacheErrorNotFound;
case CACHE_STORAGE_ERROR_QUOTA_EXCEEDED:
return blink::WebServiceWorkerCacheErrorQuotaExceeded;
case CACHE_STORAGE_ERROR_CACHE_NAME_NOT_FOUND:
return blink::WebServiceWorkerCacheErrorCacheNameNotFound;
}
NOTREACHED();
return blink::WebServiceWorkerCacheErrorNotImplemented;
}
bool OriginCanAccessCacheStorage(const url::Origin& origin) {
return !origin.unique() && IsOriginSecure(GURL(origin.Serialize()));
}
void StopPreservingCache(
std::unique_ptr<CacheStorageCacheHandle> cache_handle) {}
} // namespace
CacheStorageDispatcherHost::CacheStorageDispatcherHost()
: BrowserMessageFilter(kFilteredMessageClasses,
arraysize(kFilteredMessageClasses)) {}
CacheStorageDispatcherHost::~CacheStorageDispatcherHost() {
}
void CacheStorageDispatcherHost::Init(CacheStorageContextImpl* context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&CacheStorageDispatcherHost::CreateCacheListener, this,
base::RetainedRef(context)));
}
void CacheStorageDispatcherHost::OnDestruct() const {
BrowserThread::DeleteOnIOThread::Destruct(this);
}
bool CacheStorageDispatcherHost::OnMessageReceived(
const IPC::Message& message) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(CacheStorageDispatcherHost, message)
IPC_MESSAGE_HANDLER(CacheStorageHostMsg_CacheStorageHas, OnCacheStorageHas)
IPC_MESSAGE_HANDLER(CacheStorageHostMsg_CacheStorageOpen, OnCacheStorageOpen)
IPC_MESSAGE_HANDLER(CacheStorageHostMsg_CacheStorageDelete,
OnCacheStorageDelete)
IPC_MESSAGE_HANDLER(CacheStorageHostMsg_CacheStorageKeys, OnCacheStorageKeys)
IPC_MESSAGE_HANDLER(CacheStorageHostMsg_CacheStorageMatch,
OnCacheStorageMatch)
IPC_MESSAGE_HANDLER(CacheStorageHostMsg_CacheMatch, OnCacheMatch)
IPC_MESSAGE_HANDLER(CacheStorageHostMsg_CacheMatchAll, OnCacheMatchAll)
IPC_MESSAGE_HANDLER(CacheStorageHostMsg_CacheKeys, OnCacheKeys)
IPC_MESSAGE_HANDLER(CacheStorageHostMsg_CacheBatch, OnCacheBatch)
IPC_MESSAGE_HANDLER(CacheStorageHostMsg_CacheClosed, OnCacheClosed)
IPC_MESSAGE_HANDLER(CacheStorageHostMsg_BlobDataHandled, OnBlobDataHandled)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
if (!handled)
bad_message::ReceivedBadMessage(this, bad_message::CSDH_NOT_RECOGNIZED);
return handled;
}
void CacheStorageDispatcherHost::CreateCacheListener(
CacheStorageContextImpl* context) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
context_ = context;
}
void CacheStorageDispatcherHost::OnCacheStorageHas(
int thread_id,
int request_id,
const url::Origin& origin,
const base::string16& cache_name) {
TRACE_EVENT0("CacheStorage", "CacheStorageDispatcherHost::OnCacheStorageHas");
if (!OriginCanAccessCacheStorage(origin)) {
bad_message::ReceivedBadMessage(this, bad_message::CSDH_INVALID_ORIGIN);
return;
}
context_->cache_manager()->HasCache(
GURL(origin.Serialize()), base::UTF16ToUTF8(cache_name),
base::Bind(&CacheStorageDispatcherHost::OnCacheStorageHasCallback, this,
thread_id, request_id));
}
void CacheStorageDispatcherHost::OnCacheStorageOpen(
int thread_id,
int request_id,
const url::Origin& origin,
const base::string16& cache_name) {
TRACE_EVENT0("CacheStorage",
"CacheStorageDispatcherHost::OnCacheStorageOpen");
if (!OriginCanAccessCacheStorage(origin)) {
bad_message::ReceivedBadMessage(this, bad_message::CSDH_INVALID_ORIGIN);
return;
}
context_->cache_manager()->OpenCache(
GURL(origin.Serialize()), base::UTF16ToUTF8(cache_name),
base::Bind(&CacheStorageDispatcherHost::OnCacheStorageOpenCallback, this,
thread_id, request_id));
}
void CacheStorageDispatcherHost::OnCacheStorageDelete(
int thread_id,
int request_id,
const url::Origin& origin,
const base::string16& cache_name) {
TRACE_EVENT0("CacheStorage",
"CacheStorageDispatcherHost::OnCacheStorageDelete");
if (!OriginCanAccessCacheStorage(origin)) {
bad_message::ReceivedBadMessage(this, bad_message::CSDH_INVALID_ORIGIN);
return;
}
context_->cache_manager()->DeleteCache(
GURL(origin.Serialize()), base::UTF16ToUTF8(cache_name),
base::Bind(&CacheStorageDispatcherHost::OnCacheStorageDeleteCallback,
this, thread_id, request_id));
}
void CacheStorageDispatcherHost::OnCacheStorageKeys(int thread_id,
int request_id,
const url::Origin& origin) {
TRACE_EVENT0("CacheStorage",
"CacheStorageDispatcherHost::OnCacheStorageKeys");
if (!OriginCanAccessCacheStorage(origin)) {
bad_message::ReceivedBadMessage(this, bad_message::CSDH_INVALID_ORIGIN);
return;
}
context_->cache_manager()->EnumerateCaches(
GURL(origin.Serialize()),
base::Bind(&CacheStorageDispatcherHost::OnCacheStorageKeysCallback, this,
thread_id, request_id));
}
void CacheStorageDispatcherHost::OnCacheStorageMatch(
int thread_id,
int request_id,
const url::Origin& origin,
const ServiceWorkerFetchRequest& request,
const CacheStorageCacheQueryParams& match_params) {
TRACE_EVENT0("CacheStorage",
"CacheStorageDispatcherHost::OnCacheStorageMatch");
if (!OriginCanAccessCacheStorage(origin)) {
bad_message::ReceivedBadMessage(this, bad_message::CSDH_INVALID_ORIGIN);
return;
}
std::unique_ptr<ServiceWorkerFetchRequest> scoped_request(
new ServiceWorkerFetchRequest(request.url, request.method,
request.headers, request.referrer,
request.is_reload));
if (match_params.cache_name.empty()) {
context_->cache_manager()->MatchAllCaches(
GURL(origin.Serialize()), std::move(scoped_request),
base::Bind(&CacheStorageDispatcherHost::OnCacheStorageMatchCallback,
this, thread_id, request_id));
return;
}
context_->cache_manager()->MatchCache(
GURL(origin.Serialize()), base::UTF16ToUTF8(match_params.cache_name),
std::move(scoped_request),
base::Bind(&CacheStorageDispatcherHost::OnCacheStorageMatchCallback, this,
thread_id, request_id));
}
void CacheStorageDispatcherHost::OnCacheMatch(
int thread_id,
int request_id,
int cache_id,
const ServiceWorkerFetchRequest& request,
const CacheStorageCacheQueryParams& match_params) {
IDToCacheMap::iterator it = id_to_cache_map_.find(cache_id);
if (it == id_to_cache_map_.end() || !it->second->value()) {
Send(new CacheStorageMsg_CacheMatchError(
thread_id, request_id, blink::WebServiceWorkerCacheErrorNotFound));
return;
}
CacheStorageCache* cache = it->second->value();
std::unique_ptr<ServiceWorkerFetchRequest> scoped_request(
new ServiceWorkerFetchRequest(request.url, request.method,
request.headers, request.referrer,
request.is_reload));
cache->Match(
std::move(scoped_request),
base::Bind(&CacheStorageDispatcherHost::OnCacheMatchCallback, this,
thread_id, request_id, base::Passed(it->second->Clone())));
}
void CacheStorageDispatcherHost::OnCacheMatchAll(
int thread_id,
int request_id,
int cache_id,
const ServiceWorkerFetchRequest& request,
const CacheStorageCacheQueryParams& match_params) {
IDToCacheMap::iterator it = id_to_cache_map_.find(cache_id);
if (it == id_to_cache_map_.end() || !it->second->value()) {
Send(new CacheStorageMsg_CacheMatchError(
thread_id, request_id, blink::WebServiceWorkerCacheErrorNotFound));
return;
}
CacheStorageCache* cache = it->second->value();
if (request.url.is_empty()) {
cache->MatchAll(
std::unique_ptr<ServiceWorkerFetchRequest>(), match_params,
base::Bind(&CacheStorageDispatcherHost::OnCacheMatchAllCallback, this,
thread_id, request_id, base::Passed(it->second->Clone())));
return;
}
std::unique_ptr<ServiceWorkerFetchRequest> scoped_request(
new ServiceWorkerFetchRequest(request.url, request.method,
request.headers, request.referrer,
request.is_reload));
if (match_params.ignore_search) {
cache->MatchAll(
std::move(scoped_request), match_params,
base::Bind(&CacheStorageDispatcherHost::OnCacheMatchAllCallback, this,
thread_id, request_id, base::Passed(it->second->Clone())));
return;
}
cache->Match(
std::move(scoped_request),
base::Bind(&CacheStorageDispatcherHost::OnCacheMatchAllCallbackAdapter,
this, thread_id, request_id,
base::Passed(it->second->Clone())));
}
void CacheStorageDispatcherHost::OnCacheKeys(
int thread_id,
int request_id,
int cache_id,
const ServiceWorkerFetchRequest& request,
const CacheStorageCacheQueryParams& match_params) {
IDToCacheMap::iterator it = id_to_cache_map_.find(cache_id);
if (it == id_to_cache_map_.end() || !it->second->value()) {
Send(new CacheStorageMsg_CacheKeysError(
thread_id, request_id, blink::WebServiceWorkerCacheErrorNotFound));
return;
}
CacheStorageCache* cache = it->second->value();
std::unique_ptr<ServiceWorkerFetchRequest> request_ptr(
new ServiceWorkerFetchRequest(request.url, request.method,
request.headers, request.referrer,
request.is_reload));
cache->Keys(
std::move(request_ptr), match_params,
base::Bind(&CacheStorageDispatcherHost::OnCacheKeysCallback, this,
thread_id, request_id, base::Passed(it->second->Clone())));
}
void CacheStorageDispatcherHost::OnCacheBatch(
int thread_id,
int request_id,
int cache_id,
const std::vector<CacheStorageBatchOperation>& operations) {
IDToCacheMap::iterator it = id_to_cache_map_.find(cache_id);
if (it == id_to_cache_map_.end() || !it->second->value()) {
Send(new CacheStorageMsg_CacheBatchError(
thread_id, request_id, blink::WebServiceWorkerCacheErrorNotFound));
return;
}
CacheStorageCache* cache = it->second->value();
cache->BatchOperation(
operations,
base::Bind(&CacheStorageDispatcherHost::OnCacheBatchCallback, this,
thread_id, request_id, base::Passed(it->second->Clone())));
}
void CacheStorageDispatcherHost::OnCacheClosed(int cache_id) {
DropCacheReference(cache_id);
}
void CacheStorageDispatcherHost::OnBlobDataHandled(const std::string& uuid) {
DropBlobDataHandle(uuid);
}
void CacheStorageDispatcherHost::OnCacheStorageHasCallback(
int thread_id,
int request_id,
bool has_cache,
CacheStorageError error) {
if (error != CACHE_STORAGE_OK) {
Send(new CacheStorageMsg_CacheStorageHasError(
thread_id, request_id, ToWebServiceWorkerCacheError(error)));
return;
}
if (!has_cache) {
Send(new CacheStorageMsg_CacheStorageHasError(
thread_id, request_id, blink::WebServiceWorkerCacheErrorNotFound));
return;
}
Send(new CacheStorageMsg_CacheStorageHasSuccess(thread_id, request_id));
}
void CacheStorageDispatcherHost::OnCacheStorageOpenCallback(
int thread_id,
int request_id,
std::unique_ptr<CacheStorageCacheHandle> cache_handle,
CacheStorageError error) {
if (error != CACHE_STORAGE_OK) {
Send(new CacheStorageMsg_CacheStorageOpenError(
thread_id, request_id, ToWebServiceWorkerCacheError(error)));
return;
}
// Hang on to the cache for a few seconds. This way if the user quickly closes
// and reopens it the cache backend won't have to be reinitialized.
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::Bind(&StopPreservingCache, base::Passed(cache_handle->Clone())),
base::TimeDelta::FromSeconds(kCachePreservationSeconds));
CacheID cache_id = StoreCacheReference(std::move(cache_handle));
Send(new CacheStorageMsg_CacheStorageOpenSuccess(thread_id, request_id,
cache_id));
}
void CacheStorageDispatcherHost::OnCacheStorageDeleteCallback(
int thread_id,
int request_id,
bool deleted,
CacheStorageError error) {
if (!deleted || error != CACHE_STORAGE_OK) {
Send(new CacheStorageMsg_CacheStorageDeleteError(
thread_id, request_id, ToWebServiceWorkerCacheError(error)));
return;
}
Send(new CacheStorageMsg_CacheStorageDeleteSuccess(thread_id, request_id));
}
void CacheStorageDispatcherHost::OnCacheStorageKeysCallback(
int thread_id,
int request_id,
const std::vector<std::string>& strings,
CacheStorageError error) {
if (error != CACHE_STORAGE_OK) {
Send(new CacheStorageMsg_CacheStorageKeysError(
thread_id, request_id, ToWebServiceWorkerCacheError(error)));
return;
}
std::vector<base::string16> string16s;
for (size_t i = 0, max = strings.size(); i < max; ++i) {
string16s.push_back(base::UTF8ToUTF16(strings[i]));
}
Send(new CacheStorageMsg_CacheStorageKeysSuccess(thread_id, request_id,
string16s));
}
void CacheStorageDispatcherHost::OnCacheStorageMatchCallback(
int thread_id,
int request_id,
CacheStorageError error,
std::unique_ptr<ServiceWorkerResponse> response,
std::unique_ptr<storage::BlobDataHandle> blob_data_handle) {
if (error != CACHE_STORAGE_OK) {
Send(new CacheStorageMsg_CacheStorageMatchError(
thread_id, request_id, ToWebServiceWorkerCacheError(error)));
return;
}
if (blob_data_handle)
StoreBlobDataHandle(*blob_data_handle);
Send(new CacheStorageMsg_CacheStorageMatchSuccess(thread_id, request_id,
*response));
}
void CacheStorageDispatcherHost::OnCacheMatchCallback(
int thread_id,
int request_id,
std::unique_ptr<CacheStorageCacheHandle> cache_handle,
CacheStorageError error,
std::unique_ptr<ServiceWorkerResponse> response,
std::unique_ptr<storage::BlobDataHandle> blob_data_handle) {
if (error != CACHE_STORAGE_OK) {
Send(new CacheStorageMsg_CacheMatchError(
thread_id, request_id, ToWebServiceWorkerCacheError(error)));
return;
}
if (blob_data_handle)
StoreBlobDataHandle(*blob_data_handle);
Send(new CacheStorageMsg_CacheMatchSuccess(thread_id, request_id, *response));
}
void CacheStorageDispatcherHost::OnCacheMatchAllCallbackAdapter(
int thread_id,
int request_id,
std::unique_ptr<CacheStorageCacheHandle> cache_handle,
CacheStorageError error,
std::unique_ptr<ServiceWorkerResponse> response,
std::unique_ptr<storage::BlobDataHandle> blob_data_handle) {
std::unique_ptr<CacheStorageCache::Responses> responses(
new CacheStorageCache::Responses);
std::unique_ptr<CacheStorageCache::BlobDataHandles> blob_data_handles(
new CacheStorageCache::BlobDataHandles);
if (error == CACHE_STORAGE_OK) {
DCHECK(response);
responses->push_back(*response);
if (blob_data_handle)
blob_data_handles->push_back(*blob_data_handle);
}
OnCacheMatchAllCallback(thread_id, request_id, std::move(cache_handle), error,
std::move(responses), std::move(blob_data_handles));
}
void CacheStorageDispatcherHost::OnCacheMatchAllCallback(
int thread_id,
int request_id,
std::unique_ptr<CacheStorageCacheHandle> cache_handle,
CacheStorageError error,
std::unique_ptr<CacheStorageCache::Responses> responses,
std::unique_ptr<CacheStorageCache::BlobDataHandles> blob_data_handles) {
if (error != CACHE_STORAGE_OK && error != CACHE_STORAGE_ERROR_NOT_FOUND) {
Send(new CacheStorageMsg_CacheMatchAllError(
thread_id, request_id, ToWebServiceWorkerCacheError(error)));
return;
}
for (const storage::BlobDataHandle& handle : *blob_data_handles)
StoreBlobDataHandle(handle);
Send(new CacheStorageMsg_CacheMatchAllSuccess(thread_id, request_id,
*responses));
}
void CacheStorageDispatcherHost::OnCacheKeysCallback(
int thread_id,
int request_id,
std::unique_ptr<CacheStorageCacheHandle> cache_handle,
CacheStorageError error,
std::unique_ptr<CacheStorageCache::Requests> requests) {
if (error != CACHE_STORAGE_OK) {
Send(new CacheStorageMsg_CacheKeysError(
thread_id, request_id, ToWebServiceWorkerCacheError(error)));
return;
}
CacheStorageCache::Requests out;
for (CacheStorageCache::Requests::const_iterator it = requests->begin();
it != requests->end(); ++it) {
ServiceWorkerFetchRequest request(it->url, it->method, it->headers,
it->referrer, it->is_reload);
out.push_back(request);
}
Send(new CacheStorageMsg_CacheKeysSuccess(thread_id, request_id, out));
}
void CacheStorageDispatcherHost::OnCacheBatchCallback(
int thread_id,
int request_id,
std::unique_ptr<CacheStorageCacheHandle> cache_handle,
CacheStorageError error) {
if (error != CACHE_STORAGE_OK) {
Send(new CacheStorageMsg_CacheBatchError(
thread_id, request_id, ToWebServiceWorkerCacheError(error)));
return;
}
Send(new CacheStorageMsg_CacheBatchSuccess(thread_id, request_id));
}
CacheStorageDispatcherHost::CacheID
CacheStorageDispatcherHost::StoreCacheReference(
std::unique_ptr<CacheStorageCacheHandle> cache_handle) {
int cache_id = next_cache_id_++;
id_to_cache_map_[cache_id] = std::move(cache_handle);
return cache_id;
}
void CacheStorageDispatcherHost::DropCacheReference(CacheID cache_id) {
id_to_cache_map_.erase(cache_id);
}
void CacheStorageDispatcherHost::StoreBlobDataHandle(
const storage::BlobDataHandle& blob_data_handle) {
std::pair<UUIDToBlobDataHandleList::iterator, bool> rv =
blob_handle_store_.insert(std::make_pair(
blob_data_handle.uuid(), std::list<storage::BlobDataHandle>()));
rv.first->second.push_front(storage::BlobDataHandle(blob_data_handle));
}
void CacheStorageDispatcherHost::DropBlobDataHandle(const std::string& uuid) {
UUIDToBlobDataHandleList::iterator it = blob_handle_store_.find(uuid);
if (it == blob_handle_store_.end())
return;
DCHECK(!it->second.empty());
it->second.pop_front();
if (it->second.empty())
blob_handle_store_.erase(it);
}
} // namespace content