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"
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
;
26 const int64 kBackingStoreGracePeriodMs
= 2000;
28 IndexedDBFactoryImpl::IndexedDBFactoryImpl(IndexedDBContextImpl
* 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
);
53 void IndexedDBFactoryImpl::ReleaseDatabase(
54 const IndexedDBDatabase::Identifier
& identifier
,
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
,
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
))
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.
85 CloseBackingStore(origin_url
);
89 // Start a timer to close the backing store, unless something else opens it
91 DCHECK(!backing_store_map_
[origin_url
]->close_timer()->IsRunning());
92 backing_store_map_
[origin_url
]->close_timer()->Start(
94 base::TimeDelta::FromMilliseconds(kBackingStoreGracePeriodMs
),
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
;
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();
153 void IndexedDBFactoryImpl::ReportOutstandingBlobs(const GURL
& origin_url
,
154 bool blobs_outstanding
) {
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
);
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
;
185 // TODO(cmumford): Handle this error
186 scoped_refptr
<IndexedDBBackingStore
> backing_store
=
187 OpenBackingStore(origin_url
,
194 if (!backing_store
.get()) {
196 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError
,
197 "Internal error opening backing store for "
198 "indexedDB.webkitGetDatabaseNames."));
202 std::vector
<base::string16
> names
= backing_store
->GetDatabaseNames(&s
);
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
);
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
231 it
->second
->DeleteDatabase(callbacks
);
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;
240 scoped_refptr
<IndexedDBBackingStore
> backing_store
=
241 OpenBackingStore(origin_url
,
248 if (!backing_store
.get()) {
249 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
251 "Internal error opening backing store "
252 "for indexedDB.deleteDatabase."));
253 callbacks
->OnError(error
);
254 if (s
.IsCorruption()) {
255 HandleBackingStoreCorruption(origin_url
, error
);
260 std::vector
<base::string16
> names
= backing_store
->GetDatabaseNames(&s
);
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
);
272 if (!ContainsValue(names
, name
)) {
273 const int64 version
= 0;
274 callbacks
->OnSuccess(version
);
275 backing_store
= NULL
;
276 ReleaseBackingStore(origin_url
, false /* immediate */);
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
,
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
);
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
);
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.
310 context_
->DatabaseDeleted(identifier
.first
);
313 void IndexedDBFactoryImpl::HandleBackingStoreFailure(const GURL
& origin_url
) {
314 // NULL after ContextDestroyed() called, and in some unit tests.
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
);
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.
335 IndexedDBBackingStore::DestroyBackingStore(path_base
, saved_origin_url
);
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())
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
,
367 leveldb::Status
* status
) {
368 return IndexedDBBackingStore::Open(this,
375 context_
->TaskRunner(),
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
,
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();
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
);
402 first_time
= !backends_opened_since_boot_
.count(origin_url
);
404 backing_store
= OpenBackingStoreHelper(origin_url
,
414 if (backing_store
.get()) {
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.
420 session_only_backing_stores_
.insert(backing_store
);
422 // All backing stores associated with this factory should be of the same
424 DCHECK_NE(session_only_backing_stores_
.empty(), open_in_memory
);
426 return backing_store
;
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());
448 scoped_refptr
<IndexedDBBackingStore
> backing_store
=
449 OpenBackingStore(origin_url
,
456 if (!backing_store
.get()) {
458 connection
.callbacks
->OnError(
459 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError
,
461 "Encountered full disk while opening "
462 "backing store for indexedDB.open.")));
465 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
467 "Internal error opening backing store"
468 " for indexedDB.open."));
469 connection
.callbacks
->OnError(error
);
470 if (s
.IsCorruption()) {
471 HandleBackingStoreCorruption(origin_url
, error
);
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
,
482 "Internal error creating "
483 "database backend for "
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
);
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 {
516 OriginDBs range
= GetOpenDatabasesForOrigin(origin_url
);
517 for (OriginDBMapIterator it
= range
.first
; it
!= range
.second
; ++it
)
518 count
+= it
->second
->ConnectionCount();
523 } // namespace content