[IndexedDB] Adding traces, perf tests
[chromium-blink-merge.git] / content / browser / indexed_db / indexed_db_factory_impl.cc
blobe66ff62b746b5d21cb88de043cd55a88e87b701c
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.
5 #include "content/browser/indexed_db/indexed_db_factory_impl.h"
7 #include <utility>
8 #include <vector>
10 #include "base/logging.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "base/time/time.h"
13 #include "content/browser/indexed_db/indexed_db_backing_store.h"
14 #include "content/browser/indexed_db/indexed_db_context_impl.h"
15 #include "content/browser/indexed_db/indexed_db_database_error.h"
16 #include "content/browser/indexed_db/indexed_db_tracing.h"
17 #include "content/browser/indexed_db/indexed_db_transaction_coordinator.h"
18 #include "storage/common/database/database_identifier.h"
19 #include "third_party/WebKit/public/platform/modules/indexeddb/WebIDBDatabaseException.h"
20 #include "third_party/leveldatabase/env_chromium.h"
22 using base::ASCIIToUTF16;
24 namespace content {
26 const int64 kBackingStoreGracePeriodMs = 2000;
28 IndexedDBFactoryImpl::IndexedDBFactoryImpl(IndexedDBContextImpl* context)
29 : context_(context) {
32 IndexedDBFactoryImpl::~IndexedDBFactoryImpl() {
35 void IndexedDBFactoryImpl::RemoveDatabaseFromMaps(
36 const IndexedDBDatabase::Identifier& identifier) {
37 IndexedDBDatabaseMap::iterator it = database_map_.find(identifier);
38 DCHECK(it != database_map_.end());
39 IndexedDBDatabase* database = it->second;
40 database_map_.erase(it);
42 std::pair<OriginDBMap::iterator, OriginDBMap::iterator> range =
43 origin_dbs_.equal_range(database->identifier().first);
44 DCHECK(range.first != range.second);
45 for (OriginDBMap::iterator it2 = range.first; it2 != range.second; ++it2) {
46 if (it2->second == database) {
47 origin_dbs_.erase(it2);
48 break;
53 void IndexedDBFactoryImpl::ReleaseDatabase(
54 const IndexedDBDatabase::Identifier& identifier,
55 bool forced_close) {
56 DCHECK(!database_map_.find(identifier)->second->backing_store());
58 RemoveDatabaseFromMaps(identifier);
60 // No grace period on a forced-close, as the initiator is
61 // assuming the backing store will be released once all
62 // connections are closed.
63 ReleaseBackingStore(identifier.first, forced_close);
66 void IndexedDBFactoryImpl::ReleaseBackingStore(const GURL& origin_url,
67 bool immediate) {
68 if (immediate) {
69 IndexedDBBackingStoreMap::iterator it =
70 backing_stores_with_active_blobs_.find(origin_url);
71 if (it != backing_stores_with_active_blobs_.end()) {
72 it->second->active_blob_registry()->ForceShutdown();
73 backing_stores_with_active_blobs_.erase(it);
77 // Only close if this is the last reference.
78 if (!HasLastBackingStoreReference(origin_url))
79 return;
81 // If this factory does hold the last reference to the backing store, it can
82 // be closed - but unless requested to close it immediately, keep it around
83 // for a short period so that a re-open is fast.
84 if (immediate) {
85 CloseBackingStore(origin_url);
86 return;
89 // Start a timer to close the backing store, unless something else opens it
90 // in the mean time.
91 DCHECK(!backing_store_map_[origin_url]->close_timer()->IsRunning());
92 backing_store_map_[origin_url]->close_timer()->Start(
93 FROM_HERE,
94 base::TimeDelta::FromMilliseconds(kBackingStoreGracePeriodMs),
95 base::Bind(
96 &IndexedDBFactoryImpl::MaybeCloseBackingStore, this, origin_url));
99 void IndexedDBFactoryImpl::MaybeCloseBackingStore(const GURL& origin_url) {
100 // Another reference may have opened since the maybe-close was posted, so it
101 // is necessary to check again.
102 if (HasLastBackingStoreReference(origin_url))
103 CloseBackingStore(origin_url);
106 void IndexedDBFactoryImpl::CloseBackingStore(const GURL& origin_url) {
107 IndexedDBBackingStoreMap::iterator it = backing_store_map_.find(origin_url);
108 DCHECK(it != backing_store_map_.end());
109 // Stop the timer (if it's running) - this may happen if the timer was started
110 // and then a forced close occurs.
111 it->second->close_timer()->Stop();
112 backing_store_map_.erase(it);
115 bool IndexedDBFactoryImpl::HasLastBackingStoreReference(
116 const GURL& origin_url) const {
117 IndexedDBBackingStore* ptr;
119 // Scope so that the implicit scoped_refptr<> is freed.
120 IndexedDBBackingStoreMap::const_iterator it =
121 backing_store_map_.find(origin_url);
122 DCHECK(it != backing_store_map_.end());
123 ptr = it->second.get();
125 return ptr->HasOneRef();
128 void IndexedDBFactoryImpl::ForceClose(const GURL& origin_url) {
129 OriginDBs range = GetOpenDatabasesForOrigin(origin_url);
131 while (range.first != range.second) {
132 IndexedDBDatabase* db = range.first->second;
133 ++range.first;
134 db->ForceClose();
137 if (backing_store_map_.find(origin_url) != backing_store_map_.end())
138 ReleaseBackingStore(origin_url, true /* immediate */);
141 void IndexedDBFactoryImpl::ContextDestroyed() {
142 // Timers on backing stores hold a reference to this factory. When the
143 // context (which nominally owns this factory) is destroyed during thread
144 // termination the timers must be stopped so that this factory and the
145 // stores can be disposed of.
146 for (const auto& it : backing_store_map_)
147 it.second->close_timer()->Stop();
148 backing_store_map_.clear();
149 backing_stores_with_active_blobs_.clear();
150 context_ = NULL;
153 void IndexedDBFactoryImpl::ReportOutstandingBlobs(const GURL& origin_url,
154 bool blobs_outstanding) {
155 if (!context_)
156 return;
157 if (blobs_outstanding) {
158 DCHECK(!backing_stores_with_active_blobs_.count(origin_url));
159 IndexedDBBackingStoreMap::iterator it = backing_store_map_.find(origin_url);
160 if (it != backing_store_map_.end())
161 backing_stores_with_active_blobs_.insert(*it);
162 else
163 DCHECK(false);
164 } else {
165 IndexedDBBackingStoreMap::iterator it =
166 backing_stores_with_active_blobs_.find(origin_url);
167 if (it != backing_stores_with_active_blobs_.end()) {
168 backing_stores_with_active_blobs_.erase(it);
169 ReleaseBackingStore(origin_url, false /* immediate */);
174 void IndexedDBFactoryImpl::GetDatabaseNames(
175 scoped_refptr<IndexedDBCallbacks> callbacks,
176 const GURL& origin_url,
177 const base::FilePath& data_directory,
178 net::URLRequestContext* request_context) {
179 IDB_TRACE("IndexedDBFactoryImpl::GetDatabaseNames");
180 // TODO(dgrogan): Plumb data_loss back to script eventually?
181 blink::WebIDBDataLoss data_loss;
182 std::string data_loss_message;
183 bool disk_full;
184 leveldb::Status s;
185 // TODO(cmumford): Handle this error
186 scoped_refptr<IndexedDBBackingStore> backing_store =
187 OpenBackingStore(origin_url,
188 data_directory,
189 request_context,
190 &data_loss,
191 &data_loss_message,
192 &disk_full,
193 &s);
194 if (!backing_store.get()) {
195 callbacks->OnError(
196 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
197 "Internal error opening backing store for "
198 "indexedDB.webkitGetDatabaseNames."));
199 return;
202 std::vector<base::string16> names = backing_store->GetDatabaseNames(&s);
203 if (!s.ok()) {
204 DLOG(ERROR) << "Internal error getting database names";
205 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
206 "Internal error opening backing store for "
207 "indexedDB.webkitGetDatabaseNames.");
208 callbacks->OnError(error);
209 backing_store = NULL;
210 if (s.IsCorruption())
211 HandleBackingStoreCorruption(origin_url, error);
212 return;
214 callbacks->OnSuccess(names);
215 backing_store = NULL;
216 ReleaseBackingStore(origin_url, false /* immediate */);
219 void IndexedDBFactoryImpl::DeleteDatabase(
220 const base::string16& name,
221 net::URLRequestContext* request_context,
222 scoped_refptr<IndexedDBCallbacks> callbacks,
223 const GURL& origin_url,
224 const base::FilePath& data_directory) {
225 IDB_TRACE("IndexedDBFactoryImpl::DeleteDatabase");
226 IndexedDBDatabase::Identifier unique_identifier(origin_url, name);
227 IndexedDBDatabaseMap::iterator it = database_map_.find(unique_identifier);
228 if (it != database_map_.end()) {
229 // If there are any connections to the database, directly delete the
230 // database.
231 it->second->DeleteDatabase(callbacks);
232 return;
235 // TODO(dgrogan): Plumb data_loss back to script eventually?
236 blink::WebIDBDataLoss data_loss;
237 std::string data_loss_message;
238 bool disk_full = false;
239 leveldb::Status s;
240 scoped_refptr<IndexedDBBackingStore> backing_store =
241 OpenBackingStore(origin_url,
242 data_directory,
243 request_context,
244 &data_loss,
245 &data_loss_message,
246 &disk_full,
247 &s);
248 if (!backing_store.get()) {
249 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
250 ASCIIToUTF16(
251 "Internal error opening backing store "
252 "for indexedDB.deleteDatabase."));
253 callbacks->OnError(error);
254 if (s.IsCorruption()) {
255 HandleBackingStoreCorruption(origin_url, error);
257 return;
260 std::vector<base::string16> names = backing_store->GetDatabaseNames(&s);
261 if (!s.ok()) {
262 DLOG(ERROR) << "Internal error getting database names";
263 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
264 "Internal error opening backing store for "
265 "indexedDB.deleteDatabase.");
266 callbacks->OnError(error);
267 backing_store = NULL;
268 if (s.IsCorruption())
269 HandleBackingStoreCorruption(origin_url, error);
270 return;
272 if (!ContainsValue(names, name)) {
273 const int64 version = 0;
274 callbacks->OnSuccess(version);
275 backing_store = NULL;
276 ReleaseBackingStore(origin_url, false /* immediate */);
277 return;
280 scoped_refptr<IndexedDBDatabase> database = IndexedDBDatabase::Create(
281 name, backing_store.get(), this, unique_identifier, &s);
282 if (!database.get()) {
283 IndexedDBDatabaseError error(
284 blink::WebIDBDatabaseExceptionUnknownError,
285 ASCIIToUTF16(
286 "Internal error creating database backend for "
287 "indexedDB.deleteDatabase."));
288 callbacks->OnError(error);
289 if (s.IsCorruption()) {
290 backing_store = NULL;
291 HandleBackingStoreCorruption(origin_url, error);
293 return;
296 database_map_[unique_identifier] = database.get();
297 origin_dbs_.insert(std::make_pair(origin_url, database.get()));
298 database->DeleteDatabase(callbacks);
299 RemoveDatabaseFromMaps(unique_identifier);
300 database = NULL;
301 backing_store = NULL;
302 ReleaseBackingStore(origin_url, false /* immediate */);
305 void IndexedDBFactoryImpl::DatabaseDeleted(
306 const IndexedDBDatabase::Identifier& identifier) {
307 // NULL after ContextDestroyed() called, and in some unit tests.
308 if (!context_)
309 return;
310 context_->DatabaseDeleted(identifier.first);
313 void IndexedDBFactoryImpl::HandleBackingStoreFailure(const GURL& origin_url) {
314 // NULL after ContextDestroyed() called, and in some unit tests.
315 if (!context_)
316 return;
317 context_->ForceClose(origin_url,
318 IndexedDBContextImpl::FORCE_CLOSE_BACKING_STORE_FAILURE);
321 void IndexedDBFactoryImpl::HandleBackingStoreCorruption(
322 const GURL& origin_url,
323 const IndexedDBDatabaseError& error) {
324 // Make a copy of origin_url as this is likely a reference to a member of a
325 // backing store which this function will be deleting.
326 GURL saved_origin_url(origin_url);
327 DCHECK(context_);
328 base::FilePath path_base = context_->data_path();
329 IndexedDBBackingStore::RecordCorruptionInfo(
330 path_base, saved_origin_url, base::UTF16ToUTF8(error.message()));
331 HandleBackingStoreFailure(saved_origin_url);
332 // Note: DestroyBackingStore only deletes LevelDB files, leaving all others,
333 // so our corruption info file will remain.
334 leveldb::Status s =
335 IndexedDBBackingStore::DestroyBackingStore(path_base, saved_origin_url);
336 if (!s.ok())
337 DLOG(ERROR) << "Unable to delete backing store: " << s.ToString();
340 bool IndexedDBFactoryImpl::IsDatabaseOpen(const GURL& origin_url,
341 const base::string16& name) const {
342 return !!database_map_.count(IndexedDBDatabase::Identifier(origin_url, name));
345 bool IndexedDBFactoryImpl::IsBackingStoreOpen(const GURL& origin_url) const {
346 return backing_store_map_.find(origin_url) != backing_store_map_.end();
349 bool IndexedDBFactoryImpl::IsBackingStorePendingClose(
350 const GURL& origin_url) const {
351 IndexedDBBackingStoreMap::const_iterator it =
352 backing_store_map_.find(origin_url);
353 if (it == backing_store_map_.end())
354 return false;
355 return it->second->close_timer()->IsRunning();
358 scoped_refptr<IndexedDBBackingStore>
359 IndexedDBFactoryImpl::OpenBackingStoreHelper(
360 const GURL& origin_url,
361 const base::FilePath& data_directory,
362 net::URLRequestContext* request_context,
363 blink::WebIDBDataLoss* data_loss,
364 std::string* data_loss_message,
365 bool* disk_full,
366 bool first_time,
367 leveldb::Status* status) {
368 return IndexedDBBackingStore::Open(this,
369 origin_url,
370 data_directory,
371 request_context,
372 data_loss,
373 data_loss_message,
374 disk_full,
375 context_->TaskRunner(),
376 first_time,
377 status);
380 scoped_refptr<IndexedDBBackingStore> IndexedDBFactoryImpl::OpenBackingStore(
381 const GURL& origin_url,
382 const base::FilePath& data_directory,
383 net::URLRequestContext* request_context,
384 blink::WebIDBDataLoss* data_loss,
385 std::string* data_loss_message,
386 bool* disk_full,
387 leveldb::Status* status) {
388 const bool open_in_memory = data_directory.empty();
390 IndexedDBBackingStoreMap::iterator it2 = backing_store_map_.find(origin_url);
391 if (it2 != backing_store_map_.end()) {
392 it2->second->close_timer()->Stop();
393 return it2->second;
396 scoped_refptr<IndexedDBBackingStore> backing_store;
397 bool first_time = false;
398 if (open_in_memory) {
399 backing_store = IndexedDBBackingStore::OpenInMemory(
400 origin_url, context_->TaskRunner(), status);
401 } else {
402 first_time = !backends_opened_since_boot_.count(origin_url);
404 backing_store = OpenBackingStoreHelper(origin_url,
405 data_directory,
406 request_context,
407 data_loss,
408 data_loss_message,
409 disk_full,
410 first_time,
411 status);
414 if (backing_store.get()) {
415 if (first_time)
416 backends_opened_since_boot_.insert(origin_url);
417 backing_store_map_[origin_url] = backing_store;
418 // If an in-memory database, bind lifetime to this factory instance.
419 if (open_in_memory)
420 session_only_backing_stores_.insert(backing_store);
422 // All backing stores associated with this factory should be of the same
423 // type.
424 DCHECK_NE(session_only_backing_stores_.empty(), open_in_memory);
426 return backing_store;
429 return 0;
432 void IndexedDBFactoryImpl::Open(const base::string16& name,
433 const IndexedDBPendingConnection& connection,
434 net::URLRequestContext* request_context,
435 const GURL& origin_url,
436 const base::FilePath& data_directory) {
437 IDB_TRACE("IndexedDBFactoryImpl::Open");
438 scoped_refptr<IndexedDBDatabase> database;
439 IndexedDBDatabase::Identifier unique_identifier(origin_url, name);
440 IndexedDBDatabaseMap::iterator it = database_map_.find(unique_identifier);
441 blink::WebIDBDataLoss data_loss =
442 blink::WebIDBDataLossNone;
443 std::string data_loss_message;
444 bool disk_full = false;
445 bool was_open = (it != database_map_.end());
446 if (!was_open) {
447 leveldb::Status s;
448 scoped_refptr<IndexedDBBackingStore> backing_store =
449 OpenBackingStore(origin_url,
450 data_directory,
451 request_context,
452 &data_loss,
453 &data_loss_message,
454 &disk_full,
455 &s);
456 if (!backing_store.get()) {
457 if (disk_full) {
458 connection.callbacks->OnError(
459 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError,
460 ASCIIToUTF16(
461 "Encountered full disk while opening "
462 "backing store for indexedDB.open.")));
463 return;
465 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
466 ASCIIToUTF16(
467 "Internal error opening backing store"
468 " for indexedDB.open."));
469 connection.callbacks->OnError(error);
470 if (s.IsCorruption()) {
471 HandleBackingStoreCorruption(origin_url, error);
473 return;
476 database = IndexedDBDatabase::Create(
477 name, backing_store.get(), this, unique_identifier, &s);
478 if (!database.get()) {
479 DLOG(ERROR) << "Unable to create the database";
480 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
481 ASCIIToUTF16(
482 "Internal error creating "
483 "database backend for "
484 "indexedDB.open."));
485 connection.callbacks->OnError(error);
486 if (s.IsCorruption()) {
487 backing_store = NULL; // Closes the LevelDB so that it can be deleted
488 HandleBackingStoreCorruption(origin_url, error);
490 return;
492 } else {
493 database = it->second;
496 if (data_loss != blink::WebIDBDataLossNone)
497 connection.callbacks->OnDataLoss(data_loss, data_loss_message);
499 database->OpenConnection(connection);
501 if (!was_open && database->ConnectionCount() > 0) {
502 database_map_[unique_identifier] = database.get();
503 origin_dbs_.insert(std::make_pair(origin_url, database.get()));
507 std::pair<IndexedDBFactoryImpl::OriginDBMapIterator,
508 IndexedDBFactoryImpl::OriginDBMapIterator>
509 IndexedDBFactoryImpl::GetOpenDatabasesForOrigin(const GURL& origin_url) const {
510 return origin_dbs_.equal_range(origin_url);
513 size_t IndexedDBFactoryImpl::GetConnectionCount(const GURL& origin_url) const {
514 size_t count(0);
516 OriginDBs range = GetOpenDatabasesForOrigin(origin_url);
517 for (OriginDBMapIterator it = range.first; it != range.second; ++it)
518 count += it->second->ConnectionCount();
520 return count;
523 } // namespace content