| // 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/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; |
| case WebServiceWorkerCacheErrorQuotaExceeded: |
| return CString("quota exceeded."); |
| case WebServiceWorkerCacheErrorCacheNameNotFound: |
| return CString("cache not found."); |
| case WebServiceWorkerCacheErrorTooLarge: |
| return CString("operation too large."); |
| } |
| NOTREACHED(); |
| return ""; |
| } |
| |
| 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 |