1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "config.h"
6#include "modules/serviceworkers/Cache.h"
7
8#include "bindings/core/v8/ScriptPromiseResolver.h"
9#include "bindings/core/v8/ScriptState.h"
10#include "bindings/core/v8/V8ThrowException.h"
11#include "core/dom/DOMException.h"
12#include "modules/serviceworkers/Request.h"
13#include "modules/serviceworkers/Response.h"
14#include "public/platform/WebServiceWorkerCache.h"
15
16namespace blink {
17
18namespace {
19
20WebServiceWorkerCache::QueryParams toWebQueryParams(const QueryParams& queryParams)
21{
22    WebServiceWorkerCache::QueryParams webQueryParams;
23    // FIXME: The queryParams.hasXXXX() calls can be removed if defaults are
24    // added to the IDL. https://github.com/slightlyoff/ServiceWorker/issues/466
25    webQueryParams.ignoreSearch = queryParams.hasIgnoreSearch() && queryParams.ignoreSearch();
26    webQueryParams.ignoreMethod = queryParams.hasIgnoreMethod() && queryParams.ignoreMethod();
27    webQueryParams.ignoreVary = queryParams.hasIgnoreVary() && queryParams.ignoreVary();
28    webQueryParams.prefixMatch = queryParams.hasPrefixMatch() && queryParams.prefixMatch();
29    webQueryParams.cacheName = queryParams.cacheName();
30    return webQueryParams;
31}
32
33// FIXME: Consider using CallbackPromiseAdapter.
34class CacheMatchCallbacks : public WebServiceWorkerCache::CacheMatchCallbacks {
35    WTF_MAKE_NONCOPYABLE(CacheMatchCallbacks);
36public:
37    CacheMatchCallbacks(PassRefPtr<ScriptPromiseResolver> resolver)
38        : m_resolver(resolver) { }
39
40    virtual void onSuccess(WebServiceWorkerResponse* webResponse) OVERRIDE
41    {
42        m_resolver->resolve(Response::create(m_resolver->scriptState()->executionContext(), *webResponse));
43        m_resolver.clear();
44    }
45
46    virtual void onError(WebServiceWorkerCacheError* reason) OVERRIDE
47    {
48        m_resolver->reject(Cache::domExceptionForCacheError(*reason));
49        m_resolver.clear();
50    }
51
52private:
53    RefPtr<ScriptPromiseResolver> m_resolver;
54};
55
56// FIXME: Consider using CallbackPromiseAdapter.
57class CacheWithResponsesCallbacks : public WebServiceWorkerCache::CacheWithResponsesCallbacks {
58    WTF_MAKE_NONCOPYABLE(CacheWithResponsesCallbacks);
59public:
60    CacheWithResponsesCallbacks(PassRefPtr<ScriptPromiseResolver> resolver)
61        : m_resolver(resolver) { }
62
63    virtual void onSuccess(WebVector<WebServiceWorkerResponse>* webResponses) OVERRIDE
64    {
65        HeapVector<Member<Response> > responses;
66        for (size_t i = 0; i < webResponses->size(); ++i)
67            responses.append(Response::create(m_resolver->scriptState()->executionContext(), (*webResponses)[i]));
68        m_resolver->resolve(responses);
69        m_resolver.clear();
70    }
71
72    virtual void onError(WebServiceWorkerCacheError* reason) OVERRIDE
73    {
74        m_resolver->reject(Cache::domExceptionForCacheError(*reason));
75        m_resolver.clear();
76    }
77
78private:
79    RefPtr<ScriptPromiseResolver> m_resolver;
80};
81
82// FIXME: Consider using CallbackPromiseAdapter.
83class CacheWithRequestsCallbacks : public WebServiceWorkerCache::CacheWithRequestsCallbacks {
84    WTF_MAKE_NONCOPYABLE(CacheWithRequestsCallbacks);
85public:
86    CacheWithRequestsCallbacks(PassRefPtr<ScriptPromiseResolver> resolver)
87        : m_resolver(resolver) { }
88
89    virtual void onSuccess(WebVector<WebServiceWorkerRequest>* webRequests) OVERRIDE
90    {
91        HeapVector<Member<Request> > requests;
92        for (size_t i = 0; i < webRequests->size(); ++i)
93            requests.append(Request::create(m_resolver->scriptState()->executionContext(), (*webRequests)[i]));
94        m_resolver->resolve(requests);
95        m_resolver.clear();
96    }
97
98    virtual void onError(WebServiceWorkerCacheError* reason) OVERRIDE
99    {
100        m_resolver->reject(Cache::domExceptionForCacheError(*reason));
101        m_resolver.clear();
102    }
103
104private:
105    RefPtr<ScriptPromiseResolver> m_resolver;
106};
107
108ScriptPromise rejectForCacheError(ScriptState* scriptState, WebServiceWorkerCacheError error)
109{
110    return ScriptPromise::rejectWithDOMException(scriptState, Cache::domExceptionForCacheError(error));
111}
112
113ScriptPromise rejectAsNotImplemented(ScriptState* scriptState)
114{
115    return ScriptPromise::rejectWithDOMException(scriptState, DOMException::create(NotSupportedError, "Cache is not implemented"));
116}
117
118} // namespace
119
120Cache* Cache::create(WebServiceWorkerCache* webCache)
121{
122    return new Cache(webCache);
123}
124
125ScriptPromise Cache::match(ScriptState* scriptState, Request* originalRequest, const QueryParams& queryParams)
126{
127    TrackExceptionState exceptionState;
128    Request* request = Request::create(scriptState->executionContext(), originalRequest, exceptionState);
129    if (exceptionState.hadException()) {
130        // FIXME: We should throw the caught error.
131        return rejectForCacheError(scriptState, WebServiceWorkerCacheErrorNotFound);
132    }
133    return matchImpl(scriptState, request, queryParams);
134}
135
136ScriptPromise Cache::match(ScriptState* scriptState, const String& requestString, const QueryParams& queryParams)
137{
138    TrackExceptionState exceptionState;
139    Request* request = Request::create(scriptState->executionContext(), requestString, exceptionState);
140    if (exceptionState.hadException()) {
141        // FIXME: We should throw the caught error.
142        return rejectForCacheError(scriptState, WebServiceWorkerCacheErrorNotFound);
143    }
144    return matchImpl(scriptState, request, queryParams);
145}
146
147ScriptPromise Cache::matchAll(ScriptState* scriptState, Request* originalRequest, const QueryParams& queryParams)
148{
149    TrackExceptionState exceptionState;
150    Request* request = Request::create(scriptState->executionContext(), originalRequest, exceptionState);
151    if (exceptionState.hadException()) {
152        // FIXME: We should throw the caught error.
153        return rejectForCacheError(scriptState, WebServiceWorkerCacheErrorNotFound);
154    }
155    return matchAllImpl(scriptState, request, queryParams);
156}
157
158ScriptPromise Cache::matchAll(ScriptState* scriptState, const String& requestString, const QueryParams& queryParams)
159{
160    TrackExceptionState exceptionState;
161    Request* request = Request::create(scriptState->executionContext(), requestString, exceptionState);
162    if (exceptionState.hadException()) {
163        // FIXME: We should throw the caught error.
164        return rejectForCacheError(scriptState, WebServiceWorkerCacheErrorNotFound);
165    }
166    return matchAllImpl(scriptState, request, queryParams);
167}
168
169ScriptPromise Cache::add(ScriptState* scriptState, Request* originalRequest)
170{
171    TrackExceptionState exceptionState;
172    Request* request = Request::create(scriptState->executionContext(), originalRequest, exceptionState);
173    if (exceptionState.hadException()) {
174        // FIXME: We should throw the caught error.
175        return rejectForCacheError(scriptState, WebServiceWorkerCacheErrorNotFound);
176    }
177    return addImpl(scriptState, request);
178}
179
180ScriptPromise Cache::add(ScriptState* scriptState, const String& requestString)
181{
182    TrackExceptionState exceptionState;
183    Request* request = Request::create(scriptState->executionContext(), requestString, exceptionState);
184    if (exceptionState.hadException()) {
185        // FIXME: We should throw the caught error.
186        return rejectForCacheError(scriptState, WebServiceWorkerCacheErrorNotFound);
187    }
188    return addImpl(scriptState, request);
189}
190
191ScriptPromise Cache::addAll(ScriptState* scriptState, const Vector<ScriptValue>& rawRequests)
192{
193    // FIXME: Implement this.
194    return rejectAsNotImplemented(scriptState);
195}
196
197ScriptPromise Cache::deleteFunction(ScriptState* scriptState, Request* originalRequest, const QueryParams& queryParams)
198{
199    TrackExceptionState exceptionState;
200    Request* request = Request::create(scriptState->executionContext(), originalRequest, exceptionState);
201    if (exceptionState.hadException()) {
202        // FIXME: We should throw the caught error.
203        return rejectForCacheError(scriptState, WebServiceWorkerCacheErrorNotFound);
204    }
205    return deleteImpl(scriptState, request, queryParams);
206}
207
208ScriptPromise Cache::deleteFunction(ScriptState* scriptState, const String& requestString, const QueryParams& queryParams)
209{
210    TrackExceptionState exceptionState;
211    Request* request = Request::create(scriptState->executionContext(), requestString, exceptionState);
212    if (exceptionState.hadException()) {
213        // FIXME: We should throw the caught error.
214        return rejectForCacheError(scriptState, WebServiceWorkerCacheErrorNotFound);
215    }
216    return deleteImpl(scriptState, request, queryParams);
217}
218
219ScriptPromise Cache::put(ScriptState* scriptState, Request* originalRequest, Response* response)
220{
221    TrackExceptionState exceptionState;
222    Request* request = Request::create(scriptState->executionContext(), originalRequest, exceptionState);
223    if (exceptionState.hadException()) {
224        // FIXME: We should throw the caught error.
225        return rejectForCacheError(scriptState, WebServiceWorkerCacheErrorNotFound);
226    }
227    return putImpl(scriptState, request, response);
228}
229
230ScriptPromise Cache::put(ScriptState* scriptState, const String& requestString, Response* response)
231{
232    TrackExceptionState exceptionState;
233    Request* request = Request::create(scriptState->executionContext(), requestString, exceptionState);
234    if (exceptionState.hadException()) {
235        // FIXME: We should throw the caught error.
236        return rejectForCacheError(scriptState, WebServiceWorkerCacheErrorNotFound);
237    }
238    return putImpl(scriptState, request, response);
239}
240
241ScriptPromise Cache::keys(ScriptState* scriptState)
242{
243    return keysImpl(scriptState);
244}
245
246ScriptPromise Cache::keys(ScriptState* scriptState, Request* originalRequest, const QueryParams& queryParams)
247{
248    TrackExceptionState exceptionState;
249    Request* request = Request::create(scriptState->executionContext(), originalRequest, exceptionState);
250    if (exceptionState.hadException()) {
251        // FIXME: We should throw the caught error.
252        return rejectForCacheError(scriptState, WebServiceWorkerCacheErrorNotFound);
253    }
254    return keysImpl(scriptState, request, queryParams);
255}
256
257ScriptPromise Cache::keys(ScriptState* scriptState, const String& requestString, const QueryParams& queryParams)
258{
259    TrackExceptionState exceptionState;
260    Request* request = Request::create(scriptState->executionContext(), requestString, exceptionState);
261    if (exceptionState.hadException()) {
262        // FIXME: We should throw the caught error.
263        return rejectForCacheError(scriptState, WebServiceWorkerCacheErrorNotFound);
264    }
265    return keysImpl(scriptState, request, queryParams);
266}
267
268Cache::Cache(WebServiceWorkerCache* webCache)
269    : m_webCache(adoptPtr(webCache)) { }
270
271ScriptPromise Cache::matchImpl(ScriptState* scriptState, Request* request, const QueryParams& queryParams)
272{
273    WebServiceWorkerRequest webRequest;
274    request->populateWebServiceWorkerRequest(webRequest);
275
276    RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scriptState);
277    const ScriptPromise promise = resolver->promise();
278    m_webCache->dispatchMatch(new CacheMatchCallbacks(resolver), webRequest, toWebQueryParams(queryParams));
279    return promise;
280}
281
282ScriptPromise Cache::matchAllImpl(ScriptState* scriptState, Request* request, const QueryParams& queryParams)
283{
284    WebServiceWorkerRequest webRequest;
285    request->populateWebServiceWorkerRequest(webRequest);
286
287    RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scriptState);
288    const ScriptPromise promise = resolver->promise();
289    m_webCache->dispatchMatchAll(new CacheWithResponsesCallbacks(resolver), webRequest, toWebQueryParams(queryParams));
290    return promise;
291}
292
293ScriptPromise Cache::addImpl(ScriptState* scriptState, Request*)
294{
295    // FIXME: Implement this.
296    return rejectAsNotImplemented(scriptState);
297}
298
299ScriptPromise Cache::addAllImpl(ScriptState* scriptState, Vector<Request*>)
300{
301    // FIXME: Implement this.
302    return rejectAsNotImplemented(scriptState);
303}
304
305PassRefPtrWillBeRawPtr<DOMException> Cache::domExceptionForCacheError(WebServiceWorkerCacheError reason)
306{
307    switch (reason) {
308    case WebServiceWorkerCacheErrorNotImplemented:
309        return DOMException::create(NotSupportedError, "Method is not implemented.");
310    case WebServiceWorkerCacheErrorNotFound:
311        return DOMException::create(NotFoundError, "Entry was not found.");
312    case WebServiceWorkerCacheErrorExists:
313        return DOMException::create(InvalidAccessError, "Entry already exists.");
314    default:
315        ASSERT_NOT_REACHED();
316        return DOMException::create(NotSupportedError, "Unknown error.");
317    }
318}
319
320ScriptPromise Cache::deleteImpl(ScriptState* scriptState, Request* request, const QueryParams& queryParams)
321{
322    WebVector<WebServiceWorkerCache::BatchOperation> batchOperations(size_t(1));
323    batchOperations[0].operationType = WebServiceWorkerCache::OperationTypeDelete;
324    request->populateWebServiceWorkerRequest(batchOperations[0].request);
325    batchOperations[0].matchParams = toWebQueryParams(queryParams);
326
327    RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scriptState);
328    const ScriptPromise promise = resolver->promise();
329    m_webCache->dispatchBatch(new CacheWithResponsesCallbacks(resolver), batchOperations);
330    return promise;
331}
332
333ScriptPromise Cache::putImpl(ScriptState* scriptState, Request* request, Response* response)
334{
335    WebVector<WebServiceWorkerCache::BatchOperation> batchOperations(size_t(1));
336    batchOperations[0].operationType = WebServiceWorkerCache::OperationTypePut;
337    request->populateWebServiceWorkerRequest(batchOperations[0].request);
338    response->populateWebServiceWorkerResponse(batchOperations[0].response);
339
340    RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scriptState);
341    const ScriptPromise promise = resolver->promise();
342    m_webCache->dispatchBatch(new CacheWithResponsesCallbacks(resolver), batchOperations);
343    return promise;
344}
345
346ScriptPromise Cache::keysImpl(ScriptState* scriptState)
347{
348    RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scriptState);
349    const ScriptPromise promise = resolver->promise();
350    m_webCache->dispatchKeys(new CacheWithRequestsCallbacks(resolver), 0, WebServiceWorkerCache::QueryParams());
351    return promise;
352}
353
354ScriptPromise Cache::keysImpl(ScriptState* scriptState, Request* request, const QueryParams& queryParams)
355{
356    WebServiceWorkerRequest webRequest;
357    request->populateWebServiceWorkerRequest(webRequest);
358
359    RefPtr<ScriptPromiseResolver> resolver = ScriptPromiseResolver::create(scriptState);
360    const ScriptPromise promise = resolver->promise();
361    m_webCache->dispatchKeys(new CacheWithRequestsCallbacks(resolver), 0, toWebQueryParams(queryParams));
362    return promise;
363}
364
365} // namespace blink
366