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// A simple, incomplete implementation of the Cache API, intended to facilitate
6// end to end serviceworker testing.
7
8// See https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#cache-objects
9
10// FIXME: Support AbstractResponse/OpaqueResponse correctly.
11// FIXME: Serialize the cache.
12// FIXME: Bind all function references.
13(function(global) {
14    var _castToRequest = function(item) {
15        if (typeof item === 'string') {
16            item = new Request({
17                url: item,
18            });
19        }
20        return item;
21    };
22
23    var Cache = function() {
24        // An object containing a property for each HTTP fetch method. Those
25        // referenced objects contain a property for each URL, which is the
26        // Response.
27        this.entriesByMethod = {};
28    };
29
30    // FIXME: Should this be in the spec?
31    Cache.prototype.keys = function() {
32        var that = this;
33
34        var flatten = Array.prototype.concat.apply.bind(Array.prototype.concat, []);
35
36        return Promise.resolve(flatten(
37            Object.keys(this.entriesByMethod).map(function(method) {
38                return Object.keys(that.entriesByMethod[method]).map(function(url) {
39                    return new Request({method: method, url: url});
40                });
41            })));
42    };
43
44    // FIXME: Implement this.
45    // FIXME: Should spec rename each --> forEach?
46    Cache.prototype.forEach = Promise.reject.bind(Promise, 'Cache.prototype.forEach() not implemented.');
47    Cache.prototype.each = Promise.reject.bind(Promise, 'Cache.prototype.each() not implemented.');
48
49    Cache.prototype.put = function(request, response) {
50        var that = this;
51
52        return new Promise(function(resolve, reject) {
53            request = _castToRequest(request);
54
55            if (!that.entriesByMethod.hasOwnProperty(request.method)) {
56                that.entriesByMethod[request.method] = {};
57            }
58
59            var entriesByUrl = that.entriesByMethod[request.method];
60            entriesByUrl[request.url] = response;
61
62            resolve();
63        });
64    };
65
66    Cache.prototype.add = function(request) {
67        var that = this;
68        request = _castToRequest(request);
69        return new Promise(function (resolve, reject) {
70            fetch(request).then(
71                function(response) {
72                    that.put(request, response).then(resolve);
73                },
74                reject);
75        });
76    };
77
78    // FIXME: Add QueryParams argument.
79    Cache.prototype.delete = function(request) {
80        request = _castToRequest(request);
81
82        var that = this;
83        return new Promise(function(resolve, reject) {
84            if (that.entriesByMethod.hasOwnProperty(request.method)) {
85                var entriesByUrl = that.entriesByMethod[request.method];
86                delete entriesByUrl[request.url];
87            }
88            resolve();
89        });
90    };
91
92    // FIXME: Add QueryParams argument.
93    Cache.prototype.match = function(request) {
94        var that = this;
95
96        return new Promise(function(resolve, reject) {
97            request = _castToRequest(request);
98
99            if (!that.entriesByMethod.hasOwnProperty(request.method)) {
100                reject('not found');
101                return;
102            }
103
104            var entriesByUrl = that.entriesByMethod[request.method];
105
106            if (!entriesByUrl.hasOwnProperty(request.url)) {
107                reject('not found');
108                return;
109            }
110
111            var entry = entriesByUrl[request.url];
112            resolve(entry);
113        });
114    };
115
116    // FIXME: Implement this.
117    Cache.prototype.matchAll = Promise.reject.bind(Promise, 'Cache.prototype.matchAll not implemented.');
118
119    global.Cache = global.Cache || Cache;
120}(self));  // window or worker global scope.
121