blob: de7b57db649d9161014acee61150229b54e13277 [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 "modules/cachestorage/InspectorCacheStorageAgent.h"
#include "platform/heap/Handle.h"
#include "platform/inspector_protocol/DispatcherBase.h"
#include "platform/inspector_protocol/Values.h"
#include "platform/weborigin/KURL.h"
#include "platform/weborigin/SecurityOrigin.h"
#include "public/platform/Platform.h"
#include "public/platform/WebSecurityOrigin.h"
#include "public/platform/WebString.h"
#include "public/platform/WebURL.h"
#include "public/platform/WebVector.h"
#include "public/platform/modules/serviceworker/WebServiceWorkerCache.h"
#include "public/platform/modules/serviceworker/WebServiceWorkerCacheError.h"
#include "public/platform/modules/serviceworker/WebServiceWorkerCacheStorage.h"
#include "public/platform/modules/serviceworker/WebServiceWorkerRequest.h"
#include "public/platform/modules/serviceworker/WebServiceWorkerResponse.h"
#include "wtf/Noncopyable.h"
#include "wtf/PassRefPtr.h"
#include "wtf/PtrUtil.h"
#include "wtf/RefCounted.h"
#include "wtf/RefPtr.h"
#include "wtf/Vector.h"
#include "wtf/text/StringBuilder.h"
#include <algorithm>
#include <memory>
using blink::protocol::Array;
using blink::protocol::CacheStorage::Cache;
using blink::protocol::CacheStorage::DataEntry;
typedef blink::protocol::CacheStorage::Backend::DeleteCacheCallback DeleteCacheCallback;
typedef blink::protocol::CacheStorage::Backend::DeleteEntryCallback DeleteEntryCallback;
typedef blink::protocol::CacheStorage::Backend::RequestCacheNamesCallback RequestCacheNamesCallback;
typedef blink::protocol::CacheStorage::Backend::RequestEntriesCallback RequestEntriesCallback;
typedef blink::WebServiceWorkerCache::BatchOperation BatchOperation;
namespace blink {
namespace {
String buildCacheId(const String& securityOrigin, const String& cacheName)
{
String id(securityOrigin);
id.append('|');
id.append(cacheName);
return id;
}
bool parseCacheId(ErrorString* errorString, const String& id, String* securityOrigin, String* cacheName)
{
size_t pipe = id.find('|');
if (pipe == WTF::kNotFound) {
*errorString = "Invalid cache id.";
return false;
}
*securityOrigin = id.substring(0, pipe);
*cacheName = id.substring(pipe + 1);
return true;
}
std::unique_ptr<WebServiceWorkerCacheStorage> assertCacheStorage(ErrorString* errorString, const String& securityOrigin)
{
RefPtr<SecurityOrigin> secOrigin = SecurityOrigin::createFromString(securityOrigin);
// Cache Storage API is restricted to trustworthy origins.
if (!secOrigin->isPotentiallyTrustworthy()) {
*errorString = secOrigin->isPotentiallyTrustworthyErrorMessage();
return nullptr;
}
std::unique_ptr<WebServiceWorkerCacheStorage> cache = wrapUnique(Platform::current()->cacheStorage(WebSecurityOrigin(secOrigin)));
if (!cache)
*errorString = "Could not find cache storage.";
return cache;
}
std::unique_ptr<WebServiceWorkerCacheStorage> assertCacheStorageAndNameForId(ErrorString* errorString, const String& cacheId, String* cacheName)
{
String securityOrigin;
if (!parseCacheId(errorString, cacheId, &securityOrigin, cacheName)) {
return nullptr;
}
return assertCacheStorage(errorString, securityOrigin);
}
CString serviceWorkerCacheErrorString(WebServiceWorkerCacheError error)
{
switch (error) {
case WebServiceWorkerCacheErrorNotImplemented:
return CString("not implemented.");
break;
case WebServiceWorkerCacheErrorNotFound:
return CString("not found.");
break;
case WebServiceWorkerCacheErrorExists:
return CString("cache already exists.");
break;
default:
return CString("unknown error.");
break;
}
}
class RequestCacheNames
: public WebServiceWorkerCacheStorage::CacheStorageKeysCallbacks {
WTF_MAKE_NONCOPYABLE(RequestCacheNames);
public:
RequestCacheNames(const String& securityOrigin, std::unique_ptr<RequestCacheNamesCallback> callback)
: m_securityOrigin(securityOrigin)
, m_callback(std::move(callback))
{
}
~RequestCacheNames() override { }
void onSuccess(const WebVector<WebString>& caches) override
{
std::unique_ptr<Array<Cache>> array = Array<Cache>::create();
for (size_t i = 0; i < caches.size(); i++) {
String name = String(caches[i]);
std::unique_ptr<Cache> entry = Cache::create()
.setSecurityOrigin(m_securityOrigin)
.setCacheName(name)
.setCacheId(buildCacheId(m_securityOrigin, name)).build();
array->addItem(std::move(entry));
}
m_callback->sendSuccess(std::move(array));
}
void onError(WebServiceWorkerCacheError error) override
{
m_callback->sendFailure(String::format("Error requesting cache names: %s", serviceWorkerCacheErrorString(error).data()));
}
private:
String m_securityOrigin;
std::unique_ptr<RequestCacheNamesCallback> m_callback;
};
struct DataRequestParams {
String cacheName;
int skipCount;
int pageSize;
};
struct RequestResponse {
RequestResponse() { }
RequestResponse(const String& request, const String& response)
: request(request)
, response(response)
{
}
String request;
String response;
};
class ResponsesAccumulator : public RefCounted<ResponsesAccumulator> {
WTF_MAKE_NONCOPYABLE(ResponsesAccumulator);
public:
ResponsesAccumulator(int numResponses, const DataRequestParams& params, std::unique_ptr<RequestEntriesCallback> callback)
: m_params(params)
, m_numResponsesLeft(numResponses)
, m_responses(static_cast<size_t>(numResponses))
, m_callback(std::move(callback))
{
}
void addRequestResponsePair(const WebServiceWorkerRequest& request, const WebServiceWorkerResponse& response)
{
ASSERT(m_numResponsesLeft > 0);
RequestResponse& requestResponse = m_responses.at(m_responses.size() - m_numResponsesLeft);
requestResponse.request = request.url().string();
requestResponse.response = response.statusText();
if (--m_numResponsesLeft != 0)
return;
std::sort(m_responses.begin(), m_responses.end(),
[](const RequestResponse& a, const RequestResponse& b)
{
return WTF::codePointCompareLessThan(a.request, b.request);
});
if (m_params.skipCount > 0)
m_responses.remove(0, m_params.skipCount);
bool hasMore = false;
if (static_cast<size_t>(m_params.pageSize) < m_responses.size()) {
m_responses.remove(m_params.pageSize, m_responses.size() - m_params.pageSize);
hasMore = true;
}
std::unique_ptr<Array<DataEntry>> array = Array<DataEntry>::create();
for (const auto& requestResponse : m_responses) {
std::unique_ptr<DataEntry> entry = DataEntry::create()
.setRequest(requestResponse.request)
.setResponse(requestResponse.response).build();
array->addItem(std::move(entry));
}
m_callback->sendSuccess(std::move(array), hasMore);
}
void sendFailure(const String& error)
{
m_callback->sendFailure(error);
}
private:
DataRequestParams m_params;
int m_numResponsesLeft;
Vector<RequestResponse> m_responses;
std::unique_ptr<RequestEntriesCallback> m_callback;
};
class GetCacheResponsesForRequestData : public WebServiceWorkerCache::CacheMatchCallbacks {
WTF_MAKE_NONCOPYABLE(GetCacheResponsesForRequestData);
public:
GetCacheResponsesForRequestData(const DataRequestParams& params, const WebServiceWorkerRequest& request, PassRefPtr<ResponsesAccumulator> accum)
: m_params(params)
, m_request(request)
, m_accumulator(accum)
{
}
~GetCacheResponsesForRequestData() override { }
void onSuccess(const WebServiceWorkerResponse& response) override
{
m_accumulator->addRequestResponsePair(m_request, response);
}
void onError(WebServiceWorkerCacheError error) override
{
m_accumulator->sendFailure(String::format("Error requesting responses for cache %s: %s", m_params.cacheName.utf8().data(), serviceWorkerCacheErrorString(error).data()));
}
private:
DataRequestParams m_params;
WebServiceWorkerRequest m_request;
RefPtr<ResponsesAccumulator> m_accumulator;
};
class GetCacheKeysForRequestData : public WebServiceWorkerCache::CacheWithRequestsCallbacks {
WTF_MAKE_NONCOPYABLE(GetCacheKeysForRequestData);
public:
GetCacheKeysForRequestData(const DataRequestParams& params, std::unique_ptr<WebServiceWorkerCache> cache, std::unique_ptr<RequestEntriesCallback> callback)
: m_params(params)
, m_cache(std::move(cache))
, m_callback(std::move(callback))
{
}
~GetCacheKeysForRequestData() override { }
WebServiceWorkerCache* cache() { return m_cache.get(); }
void onSuccess(const WebVector<WebServiceWorkerRequest>& requests) override
{
if (requests.isEmpty()) {
std::unique_ptr<Array<DataEntry>> array = Array<DataEntry>::create();
m_callback->sendSuccess(std::move(array), false);
return;
}
RefPtr<ResponsesAccumulator> accumulator = adoptRef(new ResponsesAccumulator(requests.size(), m_params, std::move(m_callback)));
for (size_t i = 0; i < requests.size(); i++) {
const auto& request = requests[i];
auto* cacheRequest = new GetCacheResponsesForRequestData(m_params, request, accumulator);
m_cache->dispatchMatch(cacheRequest, request, WebServiceWorkerCache::QueryParams());
}
}
void onError(WebServiceWorkerCacheError error) override
{
m_callback->sendFailure(String::format("Error requesting requests for cache %s: %s", m_params.cacheName.utf8().data(), serviceWorkerCacheErrorString(error).data()));
}
private:
DataRequestParams m_params;
std::unique_ptr<WebServiceWorkerCache> m_cache;
std::unique_ptr<RequestEntriesCallback> m_callback;
};
class GetCacheForRequestData
: public WebServiceWorkerCacheStorage::CacheStorageWithCacheCallbacks {
WTF_MAKE_NONCOPYABLE(GetCacheForRequestData);
public:
GetCacheForRequestData(const DataRequestParams& params, std::unique_ptr<RequestEntriesCallback> callback)
: m_params(params)
, m_callback(std::move(callback))
{
}
~GetCacheForRequestData() override { }
void onSuccess(std::unique_ptr<WebServiceWorkerCache> cache) override
{
auto* cacheRequest = new GetCacheKeysForRequestData(m_params, wrapUnique(cache.release()), std::move(m_callback));
cacheRequest->cache()->dispatchKeys(cacheRequest, WebServiceWorkerRequest(), WebServiceWorkerCache::QueryParams());
}
void onError(WebServiceWorkerCacheError error) override
{
m_callback->sendFailure(String::format("Error requesting cache %s: %s", m_params.cacheName.utf8().data(), serviceWorkerCacheErrorString(error).data()));
}
private:
DataRequestParams m_params;
std::unique_ptr<RequestEntriesCallback> m_callback;
};
class DeleteCache : public WebServiceWorkerCacheStorage::CacheStorageCallbacks {
WTF_MAKE_NONCOPYABLE(DeleteCache);
public:
DeleteCache(std::unique_ptr<DeleteCacheCallback> callback)
: m_callback(std::move(callback))
{
}
~DeleteCache() override { }
void onSuccess() override
{
m_callback->sendSuccess();
}
void onError(WebServiceWorkerCacheError error) override
{
m_callback->sendFailure(String::format("Error requesting cache names: %s", serviceWorkerCacheErrorString(error).data()));
}
private:
std::unique_ptr<DeleteCacheCallback> m_callback;
};
class DeleteCacheEntry : public WebServiceWorkerCache::CacheBatchCallbacks {
WTF_MAKE_NONCOPYABLE(DeleteCacheEntry);
public:
DeleteCacheEntry(std::unique_ptr<DeleteEntryCallback> callback)
: m_callback(std::move(callback))
{
}
~DeleteCacheEntry() override { }
void onSuccess() override
{
m_callback->sendSuccess();
}
void onError(WebServiceWorkerCacheError error) override
{
m_callback->sendFailure(String::format("Error requesting cache names: %s", serviceWorkerCacheErrorString(error).data()));
}
private:
std::unique_ptr<DeleteEntryCallback> m_callback;
};
class GetCacheForDeleteEntry
: public WebServiceWorkerCacheStorage::CacheStorageWithCacheCallbacks {
WTF_MAKE_NONCOPYABLE(GetCacheForDeleteEntry);
public:
GetCacheForDeleteEntry(const String& requestSpec, const String& cacheName, std::unique_ptr<DeleteEntryCallback> callback)
: m_requestSpec(requestSpec)
, m_cacheName(cacheName)
, m_callback(std::move(callback))
{
}
~GetCacheForDeleteEntry() override { }
void onSuccess(std::unique_ptr<WebServiceWorkerCache> cache) override
{
auto* deleteRequest = new DeleteCacheEntry(std::move(m_callback));
BatchOperation deleteOperation;
deleteOperation.operationType = WebServiceWorkerCache::OperationTypeDelete;
deleteOperation.request.setURL(KURL(ParsedURLString, m_requestSpec));
Vector<BatchOperation> operations;
operations.append(deleteOperation);
cache.release()->dispatchBatch(deleteRequest, WebVector<BatchOperation>(operations));
}
void onError(WebServiceWorkerCacheError error) override
{
m_callback->sendFailure(String::format("Error requesting cache %s: %s", m_cacheName.utf8().data(), serviceWorkerCacheErrorString(error).data()));
}
private:
String m_requestSpec;
String m_cacheName;
std::unique_ptr<DeleteEntryCallback> m_callback;
};
} // namespace
InspectorCacheStorageAgent::InspectorCacheStorageAgent() = default;
InspectorCacheStorageAgent::~InspectorCacheStorageAgent() = default;
DEFINE_TRACE(InspectorCacheStorageAgent)
{
InspectorBaseAgent::trace(visitor);
}
void InspectorCacheStorageAgent::requestCacheNames(const String& securityOrigin, std::unique_ptr<RequestCacheNamesCallback> callback)
{
RefPtr<SecurityOrigin> secOrigin = SecurityOrigin::createFromString(securityOrigin);
// Cache Storage API is restricted to trustworthy origins.
if (!secOrigin->isPotentiallyTrustworthy()) {
// Don't treat this as an error, just don't attempt to open and enumerate the caches.
callback->sendSuccess(Array<protocol::CacheStorage::Cache>::create());
return;
}
ErrorString errorString;
std::unique_ptr<WebServiceWorkerCacheStorage> cache = assertCacheStorage(&errorString, securityOrigin);
if (!cache) {
callback->sendFailure(errorString);
return;
}
cache->dispatchKeys(new RequestCacheNames(securityOrigin, std::move(callback)));
}
void InspectorCacheStorageAgent::requestEntries(const String& cacheId, int skipCount, int pageSize, std::unique_ptr<RequestEntriesCallback> callback)
{
ErrorString errorString;
String cacheName;
std::unique_ptr<WebServiceWorkerCacheStorage> cache = assertCacheStorageAndNameForId(&errorString, cacheId, &cacheName);
if (!cache) {
callback->sendFailure(errorString);
return;
}
DataRequestParams params;
params.cacheName = cacheName;
params.pageSize = pageSize;
params.skipCount = skipCount;
cache->dispatchOpen(new GetCacheForRequestData(params, std::move(callback)), WebString(cacheName));
}
void InspectorCacheStorageAgent::deleteCache(const String& cacheId, std::unique_ptr<DeleteCacheCallback> callback)
{
String cacheName;
ErrorString errorString;
std::unique_ptr<WebServiceWorkerCacheStorage> cache = assertCacheStorageAndNameForId(&errorString, cacheId, &cacheName);
if (!cache) {
callback->sendFailure(errorString);
return;
}
cache->dispatchDelete(new DeleteCache(std::move(callback)), WebString(cacheName));
}
void InspectorCacheStorageAgent::deleteEntry(const String& cacheId, const String& request, std::unique_ptr<DeleteEntryCallback> callback)
{
String cacheName;
ErrorString errorString;
std::unique_ptr<WebServiceWorkerCacheStorage> cache = assertCacheStorageAndNameForId(&errorString, cacheId, &cacheName);
if (!cache) {
callback->sendFailure(errorString);
return;
}
cache->dispatchOpen(new GetCacheForDeleteEntry(request, cacheName, std::move(callback)), WebString(cacheName));
}
} // namespace blink