Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(394)

Side by Side Diff: content/browser/indexed_db/indexed_db_factory.cc

Issue 313883003: Split IndexedDBFactory into virtual base + impl. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2013 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 "content/browser/indexed_db/indexed_db_factory.h"
6
7 #include "base/logging.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "base/time/time.h"
10 #include "content/browser/indexed_db/indexed_db_backing_store.h"
11 #include "content/browser/indexed_db/indexed_db_context_impl.h"
12 #include "content/browser/indexed_db/indexed_db_database_error.h"
13 #include "content/browser/indexed_db/indexed_db_tracing.h"
14 #include "content/browser/indexed_db/indexed_db_transaction_coordinator.h"
15 #include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
16 #include "third_party/leveldatabase/env_chromium.h"
17 #include "webkit/common/database/database_identifier.h"
18
19 using base::ASCIIToUTF16;
20
21 namespace content {
22
23 const int64 kBackingStoreGracePeriodMs = 2000;
24
25 IndexedDBFactory::IndexedDBFactory(IndexedDBContextImpl* context)
26 : context_(context) {}
27
28 IndexedDBFactory::~IndexedDBFactory() {}
29
30 void IndexedDBFactory::RemoveDatabaseFromMaps(
31 const IndexedDBDatabase::Identifier& identifier) {
32 IndexedDBDatabaseMap::iterator it = database_map_.find(identifier);
33 DCHECK(it != database_map_.end());
34 IndexedDBDatabase* database = it->second;
35 database_map_.erase(it);
36
37 std::pair<OriginDBMap::iterator, OriginDBMap::iterator> range =
38 origin_dbs_.equal_range(database->identifier().first);
39 DCHECK(range.first != range.second);
40 for (OriginDBMap::iterator it2 = range.first; it2 != range.second; ++it2) {
41 if (it2->second == database) {
42 origin_dbs_.erase(it2);
43 break;
44 }
45 }
46 }
47
48 void IndexedDBFactory::ReleaseDatabase(
49 const IndexedDBDatabase::Identifier& identifier,
50 bool forcedClose) {
51
52 DCHECK(!database_map_.find(identifier)->second->backing_store());
53
54 RemoveDatabaseFromMaps(identifier);
55
56 // No grace period on a forced-close, as the initiator is
57 // assuming the backing store will be released once all
58 // connections are closed.
59 ReleaseBackingStore(identifier.first, forcedClose);
60 }
61
62 void IndexedDBFactory::ReleaseBackingStore(const GURL& origin_url,
63 bool immediate) {
64 if (immediate) {
65 IndexedDBBackingStoreMap::iterator it =
66 backing_stores_with_active_blobs_.find(origin_url);
67 if (it != backing_stores_with_active_blobs_.end()) {
68 it->second->active_blob_registry()->ForceShutdown();
69 backing_stores_with_active_blobs_.erase(it);
70 }
71 }
72
73 // Only close if this is the last reference.
74 if (!HasLastBackingStoreReference(origin_url))
75 return;
76
77 // If this factory does hold the last reference to the backing store, it can
78 // be closed - but unless requested to close it immediately, keep it around
79 // for a short period so that a re-open is fast.
80 if (immediate) {
81 CloseBackingStore(origin_url);
82 return;
83 }
84
85 // Start a timer to close the backing store, unless something else opens it
86 // in the mean time.
87 DCHECK(!backing_store_map_[origin_url]->close_timer()->IsRunning());
88 backing_store_map_[origin_url]->close_timer()->Start(
89 FROM_HERE,
90 base::TimeDelta::FromMilliseconds(kBackingStoreGracePeriodMs),
91 base::Bind(&IndexedDBFactory::MaybeCloseBackingStore, this, origin_url));
92 }
93
94 void IndexedDBFactory::MaybeCloseBackingStore(const GURL& origin_url) {
95 // Another reference may have opened since the maybe-close was posted, so it
96 // is necessary to check again.
97 if (HasLastBackingStoreReference(origin_url))
98 CloseBackingStore(origin_url);
99 }
100
101 void IndexedDBFactory::CloseBackingStore(const GURL& origin_url) {
102 IndexedDBBackingStoreMap::iterator it = backing_store_map_.find(origin_url);
103 DCHECK(it != backing_store_map_.end());
104 // Stop the timer (if it's running) - this may happen if the timer was started
105 // and then a forced close occurs.
106 it->second->close_timer()->Stop();
107 backing_store_map_.erase(it);
108 }
109
110 bool IndexedDBFactory::HasLastBackingStoreReference(const GURL& origin_url)
111 const {
112 IndexedDBBackingStore* ptr;
113 {
114 // Scope so that the implicit scoped_refptr<> is freed.
115 IndexedDBBackingStoreMap::const_iterator it =
116 backing_store_map_.find(origin_url);
117 DCHECK(it != backing_store_map_.end());
118 ptr = it->second.get();
119 }
120 return ptr->HasOneRef();
121 }
122
123 void IndexedDBFactory::ForceClose(const GURL& origin_url) {
124 std::pair<OriginDBMapIterator, OriginDBMapIterator> range =
125 GetOpenDatabasesForOrigin(origin_url);
126
127 while (range.first != range.second) {
128 IndexedDBDatabase* db = range.first->second;
129 ++range.first;
130 db->ForceClose();
131 }
132
133 if (backing_store_map_.find(origin_url) != backing_store_map_.end())
134 ReleaseBackingStore(origin_url, true /* immediate */);
135 }
136
137 void IndexedDBFactory::ContextDestroyed() {
138 // Timers on backing stores hold a reference to this factory. When the
139 // context (which nominally owns this factory) is destroyed during thread
140 // termination the timers must be stopped so that this factory and the
141 // stores can be disposed of.
142 for (IndexedDBBackingStoreMap::iterator it = backing_store_map_.begin();
143 it != backing_store_map_.end();
144 ++it)
145 it->second->close_timer()->Stop();
146 backing_store_map_.clear();
147 backing_stores_with_active_blobs_.clear();
148 context_ = NULL;
149 }
150
151 void IndexedDBFactory::ReportOutstandingBlobs(const GURL& origin_url,
152 bool blobs_outstanding) {
153 if (!context_)
154 return;
155 if (blobs_outstanding) {
156 DCHECK(!backing_stores_with_active_blobs_.count(origin_url));
157 IndexedDBBackingStoreMap::iterator it = backing_store_map_.find(origin_url);
158 if (it != backing_store_map_.end())
159 backing_stores_with_active_blobs_.insert(*it);
160 else
161 DCHECK(false);
162 } else {
163 IndexedDBBackingStoreMap::iterator it =
164 backing_stores_with_active_blobs_.find(origin_url);
165 if (it != backing_stores_with_active_blobs_.end()) {
166 backing_stores_with_active_blobs_.erase(it);
167 ReleaseBackingStore(origin_url, false /* immediate */);
168 }
169 }
170 }
171
172 void IndexedDBFactory::GetDatabaseNames(
173 scoped_refptr<IndexedDBCallbacks> callbacks,
174 const GURL& origin_url,
175 const base::FilePath& data_directory,
176 net::URLRequestContext* request_context) {
177 IDB_TRACE("IndexedDBFactory::GetDatabaseNames");
178 // TODO(dgrogan): Plumb data_loss back to script eventually?
179 blink::WebIDBDataLoss data_loss;
180 std::string data_loss_message;
181 bool disk_full;
182 scoped_refptr<IndexedDBBackingStore> backing_store =
183 OpenBackingStore(origin_url,
184 data_directory,
185 request_context,
186 &data_loss,
187 &data_loss_message,
188 &disk_full);
189 if (!backing_store) {
190 callbacks->OnError(
191 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
192 "Internal error opening backing store for "
193 "indexedDB.webkitGetDatabaseNames."));
194 return;
195 }
196
197 leveldb::Status s;
198 std::vector<base::string16> names = backing_store->GetDatabaseNames(&s);
199 if (!s.ok()) {
200 // TODO(cmumford): Handle this error
201 DLOG(ERROR) << "Internal error getting database names";
202 }
203 callbacks->OnSuccess(names);
204 backing_store = NULL;
205 ReleaseBackingStore(origin_url, false /* immediate */);
206 }
207
208 void IndexedDBFactory::DeleteDatabase(
209 const base::string16& name,
210 net::URLRequestContext* request_context,
211 scoped_refptr<IndexedDBCallbacks> callbacks,
212 const GURL& origin_url,
213 const base::FilePath& data_directory) {
214 IDB_TRACE("IndexedDBFactory::DeleteDatabase");
215 IndexedDBDatabase::Identifier unique_identifier(origin_url, name);
216 IndexedDBDatabaseMap::iterator it = database_map_.find(unique_identifier);
217 if (it != database_map_.end()) {
218 // If there are any connections to the database, directly delete the
219 // database.
220 it->second->DeleteDatabase(callbacks);
221 return;
222 }
223
224 // TODO(dgrogan): Plumb data_loss back to script eventually?
225 blink::WebIDBDataLoss data_loss;
226 std::string data_loss_message;
227 bool disk_full = false;
228 scoped_refptr<IndexedDBBackingStore> backing_store =
229 OpenBackingStore(origin_url,
230 data_directory,
231 request_context,
232 &data_loss,
233 &data_loss_message,
234 &disk_full);
235 if (!backing_store) {
236 callbacks->OnError(
237 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
238 ASCIIToUTF16(
239 "Internal error opening backing store "
240 "for indexedDB.deleteDatabase.")));
241 return;
242 }
243
244 leveldb::Status s;
245 scoped_refptr<IndexedDBDatabase> database = IndexedDBDatabase::Create(
246 name, backing_store, this, unique_identifier, &s);
247 if (!database) {
248 IndexedDBDatabaseError error(
249 blink::WebIDBDatabaseExceptionUnknownError,
250 ASCIIToUTF16(
251 "Internal error creating database backend for "
252 "indexedDB.deleteDatabase."));
253 callbacks->OnError(error);
254 if (leveldb_env::IsCorruption(s))
255 HandleBackingStoreCorruption(origin_url, error);
256 return;
257 }
258
259 database_map_[unique_identifier] = database;
260 origin_dbs_.insert(std::make_pair(origin_url, database));
261 database->DeleteDatabase(callbacks);
262 RemoveDatabaseFromMaps(unique_identifier);
263 database = NULL;
264 backing_store = NULL;
265 ReleaseBackingStore(origin_url, false /* immediate */);
266 }
267
268 void IndexedDBFactory::DatabaseDeleted(
269 const IndexedDBDatabase::Identifier& identifier) {
270 // NULL after ContextDestroyed() called, and in some unit tests.
271 if (!context_)
272 return;
273 context_->DatabaseDeleted(identifier.first);
274 }
275
276 void IndexedDBFactory::HandleBackingStoreFailure(const GURL& origin_url) {
277 // NULL after ContextDestroyed() called, and in some unit tests.
278 if (!context_)
279 return;
280 context_->ForceClose(origin_url,
281 IndexedDBContextImpl::FORCE_CLOSE_BACKING_STORE_FAILURE);
282 }
283
284 void IndexedDBFactory::HandleBackingStoreCorruption(
285 const GURL& origin_url,
286 const IndexedDBDatabaseError& error) {
287 // Make a copy of origin_url as this is likely a reference to a member of a
288 // backing store which this function will be deleting.
289 GURL saved_origin_url(origin_url);
290 DCHECK(context_);
291 base::FilePath path_base = context_->data_path();
292 IndexedDBBackingStore::RecordCorruptionInfo(
293 path_base, saved_origin_url, base::UTF16ToUTF8(error.message()));
294 HandleBackingStoreFailure(saved_origin_url);
295 // Note: DestroyBackingStore only deletes LevelDB files, leaving all others,
296 // so our corruption info file will remain.
297 leveldb::Status s =
298 IndexedDBBackingStore::DestroyBackingStore(path_base, saved_origin_url);
299 if (!s.ok())
300 DLOG(ERROR) << "Unable to delete backing store: " << s.ToString();
301 }
302
303 bool IndexedDBFactory::IsDatabaseOpen(const GURL& origin_url,
304 const base::string16& name) const {
305
306 return !!database_map_.count(IndexedDBDatabase::Identifier(origin_url, name));
307 }
308
309 bool IndexedDBFactory::IsBackingStoreOpen(const GURL& origin_url) const {
310 return backing_store_map_.find(origin_url) != backing_store_map_.end();
311 }
312
313 bool IndexedDBFactory::IsBackingStorePendingClose(const GURL& origin_url)
314 const {
315 IndexedDBBackingStoreMap::const_iterator it =
316 backing_store_map_.find(origin_url);
317 if (it == backing_store_map_.end())
318 return false;
319 return it->second->close_timer()->IsRunning();
320 }
321
322 scoped_refptr<IndexedDBBackingStore> IndexedDBFactory::OpenBackingStoreHelper(
323 const GURL& origin_url,
324 const base::FilePath& data_directory,
325 net::URLRequestContext* request_context,
326 blink::WebIDBDataLoss* data_loss,
327 std::string* data_loss_message,
328 bool* disk_full,
329 bool first_time) {
330 return IndexedDBBackingStore::Open(this,
331 origin_url,
332 data_directory,
333 request_context,
334 data_loss,
335 data_loss_message,
336 disk_full,
337 context_->TaskRunner(),
338 first_time);
339 }
340
341 scoped_refptr<IndexedDBBackingStore> IndexedDBFactory::OpenBackingStore(
342 const GURL& origin_url,
343 const base::FilePath& data_directory,
344 net::URLRequestContext* request_context,
345 blink::WebIDBDataLoss* data_loss,
346 std::string* data_loss_message,
347 bool* disk_full) {
348 const bool open_in_memory = data_directory.empty();
349
350 IndexedDBBackingStoreMap::iterator it2 = backing_store_map_.find(origin_url);
351 if (it2 != backing_store_map_.end()) {
352 it2->second->close_timer()->Stop();
353 return it2->second;
354 }
355
356 scoped_refptr<IndexedDBBackingStore> backing_store;
357 bool first_time = false;
358 if (open_in_memory) {
359 backing_store =
360 IndexedDBBackingStore::OpenInMemory(origin_url, context_->TaskRunner());
361 } else {
362 first_time = !backends_opened_since_boot_.count(origin_url);
363
364 backing_store = OpenBackingStoreHelper(origin_url,
365 data_directory,
366 request_context,
367 data_loss,
368 data_loss_message,
369 disk_full,
370 first_time);
371 }
372
373 if (backing_store.get()) {
374 if (first_time)
375 backends_opened_since_boot_.insert(origin_url);
376 backing_store_map_[origin_url] = backing_store;
377 // If an in-memory database, bind lifetime to this factory instance.
378 if (open_in_memory)
379 session_only_backing_stores_.insert(backing_store);
380
381 // All backing stores associated with this factory should be of the same
382 // type.
383 DCHECK_NE(session_only_backing_stores_.empty(), open_in_memory);
384
385 return backing_store;
386 }
387
388 return 0;
389 }
390
391 void IndexedDBFactory::Open(const base::string16& name,
392 const IndexedDBPendingConnection& connection,
393 net::URLRequestContext* request_context,
394 const GURL& origin_url,
395 const base::FilePath& data_directory) {
396 IDB_TRACE("IndexedDBFactory::Open");
397 scoped_refptr<IndexedDBDatabase> database;
398 IndexedDBDatabase::Identifier unique_identifier(origin_url, name);
399 IndexedDBDatabaseMap::iterator it = database_map_.find(unique_identifier);
400 blink::WebIDBDataLoss data_loss =
401 blink::WebIDBDataLossNone;
402 std::string data_loss_message;
403 bool disk_full = false;
404 bool was_open = (it != database_map_.end());
405 if (!was_open) {
406 scoped_refptr<IndexedDBBackingStore> backing_store =
407 OpenBackingStore(origin_url,
408 data_directory,
409 request_context,
410 &data_loss,
411 &data_loss_message,
412 &disk_full);
413 if (!backing_store) {
414 if (disk_full) {
415 connection.callbacks->OnError(
416 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError,
417 ASCIIToUTF16(
418 "Encountered full disk while opening "
419 "backing store for indexedDB.open.")));
420 return;
421 }
422 connection.callbacks->OnError(IndexedDBDatabaseError(
423 blink::WebIDBDatabaseExceptionUnknownError,
424 ASCIIToUTF16(
425 "Internal error opening backing store for indexedDB.open.")));
426 return;
427 }
428
429 leveldb::Status s;
430 database = IndexedDBDatabase::Create(
431 name, backing_store, this, unique_identifier, &s);
432 if (!database) {
433 DLOG(ERROR) << "Unable to create the database";
434 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
435 ASCIIToUTF16(
436 "Internal error creating "
437 "database backend for "
438 "indexedDB.open."));
439 connection.callbacks->OnError(error);
440 if (leveldb_env::IsCorruption(s)) {
441 backing_store = NULL; // Closes the LevelDB so that it can be deleted
442 HandleBackingStoreCorruption(origin_url, error);
443 }
444 return;
445 }
446 } else {
447 database = it->second;
448 }
449
450 if (data_loss != blink::WebIDBDataLossNone)
451 connection.callbacks->OnDataLoss(data_loss, data_loss_message);
452
453 database->OpenConnection(connection);
454
455 if (!was_open && database->ConnectionCount() > 0) {
456 database_map_[unique_identifier] = database;
457 origin_dbs_.insert(std::make_pair(origin_url, database));
458 }
459 }
460
461 std::pair<IndexedDBFactory::OriginDBMapIterator,
462 IndexedDBFactory::OriginDBMapIterator>
463 IndexedDBFactory::GetOpenDatabasesForOrigin(const GURL& origin_url) const {
464 return origin_dbs_.equal_range(origin_url);
465 }
466
467 size_t IndexedDBFactory::GetConnectionCount(const GURL& origin_url) const {
468 size_t count(0);
469
470 std::pair<OriginDBMapIterator, OriginDBMapIterator> range =
471 GetOpenDatabasesForOrigin(origin_url);
472 for (OriginDBMapIterator it = range.first; it != range.second; ++it)
473 count += it->second->ConnectionCount();
474
475 return count;
476 }
477
478 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698