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#ifndef CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_DATABASE_H_
6#define CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_DATABASE_H_
7
8#include <map>
9#include <set>
10#include <vector>
11
12#include "base/files/file_path.h"
13#include "base/gtest_prod_util.h"
14#include "base/macros.h"
15#include "base/memory/scoped_ptr.h"
16#include "base/sequence_checker.h"
17#include "base/time/time.h"
18#include "content/common/content_export.h"
19#include "content/common/service_worker/service_worker_status_code.h"
20#include "url/gurl.h"
21
22namespace leveldb {
23class DB;
24class Env;
25class Status;
26class WriteBatch;
27}
28
29namespace content {
30
31// Class to persist serviceworker registration data in a database.
32// Should NOT be used on the IO thread since this does blocking
33// file io. The ServiceWorkerStorage class owns this class and
34// is responsible for only calling it serially on background
35// non-IO threads (ala SequencedWorkerPool).
36class CONTENT_EXPORT ServiceWorkerDatabase {
37 public:
38  // We do leveldb stuff in |path| or in memory if |path| is empty.
39  explicit ServiceWorkerDatabase(const base::FilePath& path);
40  ~ServiceWorkerDatabase();
41
42  // Used in UMA. A new value must be appended only.
43  enum Status {
44    STATUS_OK,
45    STATUS_ERROR_NOT_FOUND,
46    STATUS_ERROR_IO_ERROR,
47    STATUS_ERROR_CORRUPTED,
48    STATUS_ERROR_FAILED,
49    STATUS_ERROR_MAX,
50  };
51
52  struct CONTENT_EXPORT RegistrationData {
53    // These values are immutable for the life of a registration.
54    int64 registration_id;
55    GURL scope;
56
57    // Versions are first stored once they successfully install and become
58    // the waiting version. Then transition to the active version. The stored
59    // version may be in the ACTIVATED state or in the INSTALLED state.
60    GURL script;
61    int64 version_id;
62    bool is_active;
63    bool has_fetch_handler;
64    base::Time last_update_check;
65
66    RegistrationData();
67    ~RegistrationData();
68  };
69
70  struct ResourceRecord {
71    int64 resource_id;
72    GURL url;
73
74    ResourceRecord() {}
75    ResourceRecord(int64 id, GURL url) : resource_id(id), url(url) {}
76  };
77
78  // Reads next available ids from the database. Returns OK if they are
79  // successfully read. Fills the arguments with an initial value and returns
80  // OK if they are not found in the database. Otherwise, returns an error.
81  Status GetNextAvailableIds(
82      int64* next_avail_registration_id,
83      int64* next_avail_version_id,
84      int64* next_avail_resource_id);
85
86  // Reads origins that have one or more than one registration from the
87  // database. Returns OK if they are successfully read or not found.
88  // Otherwise, returns an error.
89  Status GetOriginsWithRegistrations(std::set<GURL>* origins);
90
91  // Reads registrations for |origin| from the database. Returns OK if they are
92  // successfully read or not found. Otherwise, returns an error.
93  Status GetRegistrationsForOrigin(
94      const GURL& origin,
95      std::vector<RegistrationData>* registrations);
96
97  // Reads all registrations from the database. Returns OK if successfully read
98  // or not found. Otherwise, returns an error.
99  Status GetAllRegistrations(std::vector<RegistrationData>* registrations);
100
101  // Saving, retrieving, and updating registration data.
102  // (will bump next_avail_xxxx_ids as needed)
103  // (resource ids will be added/removed from the uncommitted/purgeable
104  // lists as needed)
105
106  // Reads a registration for |registration_id| and resource records associated
107  // with it from the database. Returns OK if they are successfully read.
108  // Otherwise, returns an error.
109  Status ReadRegistration(
110      int64 registration_id,
111      const GURL& origin,
112      RegistrationData* registration,
113      std::vector<ResourceRecord>* resources);
114
115  // Writes |registration| and |resources| into the database and does following
116  // things:
117  //   - If an old version of the registration exists, deletes it and sets
118  //   |deleted_version_id| to the old version id and
119  //   |newly_purgeable_resources| to its resources. Otherwise, sets
120  //   |deleted_version_id| to -1.
121  //   - Bumps the next registration id and the next version id if needed.
122  //   - Removes |resources| from the uncommitted list if exist.
123  // Returns OK they are successfully written. Otherwise, returns an error.
124  Status WriteRegistration(const RegistrationData& registration,
125                           const std::vector<ResourceRecord>& resources,
126                           int64* deleted_version_id,
127                           std::vector<int64>* newly_purgeable_resources);
128
129  // Updates a registration for |registration_id| to an active state. Returns OK
130  // if it's successfully updated. Otherwise, returns an error.
131  Status UpdateVersionToActive(
132      int64 registration_id,
133      const GURL& origin);
134
135  // Updates last check time of a registration for |registration_id| by |time|.
136  // Returns OK if it's successfully updated. Otherwise, returns an error.
137  Status UpdateLastCheckTime(
138      int64 registration_id,
139      const GURL& origin,
140      const base::Time& time);
141
142  // Deletes a registration for |registration_id| and moves resource records
143  // associated with it into the purgeable list. If deletion occurred, sets
144  // |version_id| to the id of the version that was deleted and
145  // |newly_purgeable_resources| to its resources; otherwise, sets |version_id|
146  // to -1. Returns OK if it's successfully deleted or not found in the
147  // database. Otherwise, returns an error.
148  Status DeleteRegistration(int64 registration_id,
149                            const GURL& origin,
150                            int64* version_id,
151                            std::vector<int64>* newly_purgeable_resources);
152
153  // As new resources are put into the diskcache, they go into an uncommitted
154  // list. When a registration is saved that refers to those ids, they're
155  // removed from that list. When a resource no longer has any registrations or
156  // caches referring to it, it's added to the purgeable list. Periodically,
157  // the purgeable list can be purged from the diskcache. At system startup, all
158  // uncommitted ids are moved to the purgeable list.
159
160  // Reads uncommitted resource ids from the database. Returns OK on success.
161  // Otherwise clears |ids| and returns an error.
162  Status GetUncommittedResourceIds(std::set<int64>* ids);
163
164  // Writes |ids| into the database as uncommitted resources. Returns OK on
165  // success. Otherwise writes nothing and returns an error.
166  Status WriteUncommittedResourceIds(const std::set<int64>& ids);
167
168  // Deletes uncommitted resource ids specified by |ids| from the database.
169  // Returns OK on success. Otherwise deletes nothing and returns an error.
170  Status ClearUncommittedResourceIds(const std::set<int64>& ids);
171
172  // Reads purgeable resource ids from the database. Returns OK on success.
173  // Otherwise clears |ids| and returns an error.
174  Status GetPurgeableResourceIds(std::set<int64>* ids);
175
176  // Writes |ids| into the database as purgeable resources. Returns OK on
177  // success. Otherwise writes nothing and returns an error.
178  Status WritePurgeableResourceIds(const std::set<int64>& ids);
179
180  // Deletes purgeable resource ids specified by |ids| from the database.
181  // Returns OK on success. Otherwise deletes nothing and returns an error.
182  Status ClearPurgeableResourceIds(const std::set<int64>& ids);
183
184  // Moves |ids| from the uncommitted list to the purgeable list.
185  // Returns OK on success. Otherwise deletes nothing and returns an error.
186  Status PurgeUncommittedResourceIds(const std::set<int64>& ids);
187
188  // Deletes all data for |origin|, namely, unique origin, registrations and
189  // resource records. Resources are moved to the purgeable list. Returns OK if
190  // they are successfully deleted or not found in the database. Otherwise,
191  // returns an error.
192  Status DeleteAllDataForOrigin(
193      const GURL& origin,
194      std::vector<int64>* newly_purgeable_resources);
195
196  // Completely deletes the contents of the database.
197  // Be careful using this function.
198  Status DestroyDatabase();
199
200 private:
201  // Opens the database at the |path_|. This is lazily called when the first
202  // database API is called. Returns OK if the database is successfully opened.
203  // Returns NOT_FOUND if the database does not exist and |create_if_missing| is
204  // false. Otherwise, returns an error.
205  Status LazyOpen(bool create_if_missing);
206
207  // Helper for LazyOpen(). |status| must be the return value from LazyOpen()
208  // and this must be called just after LazyOpen() is called. Returns true if
209  // the database is new or nonexistent, that is, it has never been used.
210  bool IsNewOrNonexistentDatabase(Status status);
211
212  // Reads the next available id for |id_key|. Returns OK if it's successfully
213  // read. Fills |next_avail_id| with an initial value and returns OK if it's
214  // not found in the database. Otherwise, returns an error.
215  Status ReadNextAvailableId(
216      const char* id_key,
217      int64* next_avail_id);
218
219  // Reads registration data for |registration_id| from the database. Returns OK
220  // if successfully reads. Otherwise, returns an error.
221  Status ReadRegistrationData(
222      int64 registration_id,
223      const GURL& origin,
224      RegistrationData* registration);
225
226  // Reads resource records for |version_id| from the database. Returns OK if
227  // it's successfully read or not found in the database. Otherwise, returns an
228  // error.
229  Status ReadResourceRecords(
230      int64 version_id,
231      std::vector<ResourceRecord>* resources);
232
233  // Deletes resource records for |version_id| from the database. Returns OK if
234  // they are successfully deleted or not found in the database. Otherwise,
235  // returns an error.
236  Status DeleteResourceRecords(
237      int64 version_id,
238      std::vector<int64>* newly_purgeable_resources,
239      leveldb::WriteBatch* batch);
240
241  // Reads resource ids for |id_key_prefix| from the database. Returns OK if
242  // it's successfully read or not found in the database. Otherwise, returns an
243  // error.
244  Status ReadResourceIds(
245      const char* id_key_prefix,
246      std::set<int64>* ids);
247
248  // Write resource ids for |id_key_prefix| into the database. Returns OK on
249  // success. Otherwise, returns writes nothing and returns an error.
250  Status WriteResourceIds(
251      const char* id_key_prefix,
252      const std::set<int64>& ids);
253  Status WriteResourceIdsInBatch(
254      const char* id_key_prefix,
255      const std::set<int64>& ids,
256      leveldb::WriteBatch* batch);
257
258  // Deletes resource ids for |id_key_prefix| from the database. Returns OK if
259  // it's successfully deleted or not found in the database. Otherwise, returns
260  // an error.
261  Status DeleteResourceIds(
262      const char* id_key_prefix,
263      const std::set<int64>& ids);
264  Status DeleteResourceIdsInBatch(
265      const char* id_key_prefix,
266      const std::set<int64>& ids,
267      leveldb::WriteBatch* batch);
268
269  // Reads the current schema version from the database. If the database hasn't
270  // been written anything yet, sets |db_version| to 0 and returns OK.
271  Status ReadDatabaseVersion(int64* db_version);
272
273  // Writes a batch into the database.
274  // NOTE: You must call this when you want to put something into the database
275  // because this initializes the database if needed.
276  Status WriteBatch(leveldb::WriteBatch* batch);
277
278  // Bumps the next available id if |used_id| is greater than or equal to the
279  // cached one.
280  void BumpNextRegistrationIdIfNeeded(
281      int64 used_id,
282      leveldb::WriteBatch* batch);
283  void BumpNextResourceIdIfNeeded(
284      int64 used_id,
285      leveldb::WriteBatch* batch);
286  void BumpNextVersionIdIfNeeded(
287      int64 used_id,
288      leveldb::WriteBatch* batch);
289
290  bool IsOpen();
291
292  void Disable(
293      const tracked_objects::Location& from_here,
294      Status status);
295  void HandleOpenResult(
296      const tracked_objects::Location& from_here,
297      Status status);
298  void HandleReadResult(
299      const tracked_objects::Location& from_here,
300      Status status);
301  void HandleWriteResult(
302      const tracked_objects::Location& from_here,
303      Status status);
304
305  base::FilePath path_;
306  scoped_ptr<leveldb::Env> env_;
307  scoped_ptr<leveldb::DB> db_;
308
309  int64 next_avail_registration_id_;
310  int64 next_avail_resource_id_;
311  int64 next_avail_version_id_;
312
313  enum State {
314    UNINITIALIZED,
315    INITIALIZED,
316    DISABLED,
317  };
318  State state_;
319
320  base::SequenceChecker sequence_checker_;
321
322  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerDatabaseTest, OpenDatabase);
323  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerDatabaseTest, OpenDatabase_InMemory);
324  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerDatabaseTest, DatabaseVersion);
325  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerDatabaseTest, GetNextAvailableIds);
326  FRIEND_TEST_ALL_PREFIXES(ServiceWorkerDatabaseTest, DestroyDatabase);
327
328  DISALLOW_COPY_AND_ASSIGN(ServiceWorkerDatabase);
329};
330
331}  // namespace content
332
333#endif  // CONTENT_BROWSER_SERVICE_WORKER_SERVICE_WORKER_DATABASE_H_
334