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.h"
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 "webkit/common/database/database_identifier.h"
18 using base::ASCIIToUTF16
;
22 const int64 kBackingStoreGracePeriodMs
= 2000;
24 IndexedDBFactory::IndexedDBFactory(IndexedDBContextImpl
* context
)
25 : context_(context
) {}
27 IndexedDBFactory::~IndexedDBFactory() {}
29 void IndexedDBFactory::RemoveDatabaseFromMaps(
30 const IndexedDBDatabase::Identifier
& identifier
) {
31 IndexedDBDatabaseMap::iterator it
= database_map_
.find(identifier
);
32 DCHECK(it
!= database_map_
.end());
33 IndexedDBDatabase
* database
= it
->second
;
34 database_map_
.erase(it
);
36 std::pair
<OriginDBMap::iterator
, OriginDBMap::iterator
> range
=
37 origin_dbs_
.equal_range(database
->identifier().first
);
38 DCHECK(range
.first
!= range
.second
);
39 for (OriginDBMap::iterator it2
= range
.first
; it2
!= range
.second
; ++it2
) {
40 if (it2
->second
== database
) {
41 origin_dbs_
.erase(it2
);
47 void IndexedDBFactory::ReleaseDatabase(
48 const IndexedDBDatabase::Identifier
& identifier
,
51 DCHECK(!database_map_
.find(identifier
)->second
->backing_store());
53 RemoveDatabaseFromMaps(identifier
);
55 // No grace period on a forced-close, as the initiator is
56 // assuming the backing store will be released once all
57 // connections are closed.
58 ReleaseBackingStore(identifier
.first
, forcedClose
);
61 void IndexedDBFactory::ReleaseBackingStore(const GURL
& origin_url
,
64 IndexedDBBackingStoreMap::iterator it
=
65 backing_stores_with_active_blobs_
.find(origin_url
);
66 if (it
!= backing_stores_with_active_blobs_
.end()) {
67 it
->second
->active_blob_registry()->ForceShutdown();
68 backing_stores_with_active_blobs_
.erase(it
);
72 // Only close if this is the last reference.
73 if (!HasLastBackingStoreReference(origin_url
))
76 // If this factory does hold the last reference to the backing store, it can
77 // be closed - but unless requested to close it immediately, keep it around
78 // for a short period so that a re-open is fast.
80 CloseBackingStore(origin_url
);
84 // Start a timer to close the backing store, unless something else opens it
86 DCHECK(!backing_store_map_
[origin_url
]->close_timer()->IsRunning());
87 backing_store_map_
[origin_url
]->close_timer()->Start(
89 base::TimeDelta::FromMilliseconds(kBackingStoreGracePeriodMs
),
90 base::Bind(&IndexedDBFactory::MaybeCloseBackingStore
, this, origin_url
));
93 void IndexedDBFactory::MaybeCloseBackingStore(const GURL
& origin_url
) {
94 // Another reference may have opened since the maybe-close was posted, so it
95 // is necessary to check again.
96 if (HasLastBackingStoreReference(origin_url
))
97 CloseBackingStore(origin_url
);
100 void IndexedDBFactory::CloseBackingStore(const GURL
& origin_url
) {
101 IndexedDBBackingStoreMap::iterator it
= backing_store_map_
.find(origin_url
);
102 DCHECK(it
!= backing_store_map_
.end());
103 // Stop the timer (if it's running) - this may happen if the timer was started
104 // and then a forced close occurs.
105 it
->second
->close_timer()->Stop();
106 backing_store_map_
.erase(it
);
109 bool IndexedDBFactory::HasLastBackingStoreReference(const GURL
& origin_url
)
111 IndexedDBBackingStore
* ptr
;
113 // Scope so that the implicit scoped_refptr<> is freed.
114 IndexedDBBackingStoreMap::const_iterator it
=
115 backing_store_map_
.find(origin_url
);
116 DCHECK(it
!= backing_store_map_
.end());
117 ptr
= it
->second
.get();
119 return ptr
->HasOneRef();
122 void IndexedDBFactory::ForceClose(const GURL
& origin_url
) {
123 std::pair
<OriginDBMapIterator
, OriginDBMapIterator
> range
=
124 GetOpenDatabasesForOrigin(origin_url
);
126 while (range
.first
!= range
.second
) {
127 IndexedDBDatabase
* db
= range
.first
->second
;
132 if (backing_store_map_
.find(origin_url
) != backing_store_map_
.end())
133 ReleaseBackingStore(origin_url
, true /* immediate */);
136 void IndexedDBFactory::ContextDestroyed() {
137 // Timers on backing stores hold a reference to this factory. When the
138 // context (which nominally owns this factory) is destroyed during thread
139 // termination the timers must be stopped so that this factory and the
140 // stores can be disposed of.
141 for (IndexedDBBackingStoreMap::iterator it
= backing_store_map_
.begin();
142 it
!= backing_store_map_
.end();
144 it
->second
->close_timer()->Stop();
145 backing_store_map_
.clear();
146 backing_stores_with_active_blobs_
.clear();
150 void IndexedDBFactory::ReportOutstandingBlobs(const GURL
& origin_url
,
151 bool blobs_outstanding
) {
154 if (blobs_outstanding
) {
155 DCHECK(!backing_stores_with_active_blobs_
.count(origin_url
));
156 IndexedDBBackingStoreMap::iterator it
= backing_store_map_
.find(origin_url
);
157 if (it
!= backing_store_map_
.end())
158 backing_stores_with_active_blobs_
.insert(*it
);
162 IndexedDBBackingStoreMap::iterator it
=
163 backing_stores_with_active_blobs_
.find(origin_url
);
164 if (it
!= backing_stores_with_active_blobs_
.end()) {
165 backing_stores_with_active_blobs_
.erase(it
);
166 ReleaseBackingStore(origin_url
, false /* immediate */);
171 void IndexedDBFactory::GetDatabaseNames(
172 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
173 const GURL
& origin_url
,
174 const base::FilePath
& data_directory
,
175 net::URLRequestContext
* request_context
) {
176 IDB_TRACE("IndexedDBFactory::GetDatabaseNames");
177 // TODO(dgrogan): Plumb data_loss back to script eventually?
178 blink::WebIDBDataLoss data_loss
;
179 std::string data_loss_message
;
181 scoped_refptr
<IndexedDBBackingStore
> backing_store
=
182 OpenBackingStore(origin_url
,
188 if (!backing_store
) {
190 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError
,
191 "Internal error opening backing store for "
192 "indexedDB.webkitGetDatabaseNames."));
197 std::vector
<base::string16
> names
= backing_store
->GetDatabaseNames(&s
);
199 // TODO(cmumford): Handle this error
200 DLOG(ERROR
) << "Internal error getting database names";
202 callbacks
->OnSuccess(names
);
203 backing_store
= NULL
;
204 ReleaseBackingStore(origin_url
, false /* immediate */);
207 void IndexedDBFactory::DeleteDatabase(
208 const base::string16
& name
,
209 net::URLRequestContext
* request_context
,
210 scoped_refptr
<IndexedDBCallbacks
> callbacks
,
211 const GURL
& origin_url
,
212 const base::FilePath
& data_directory
) {
213 IDB_TRACE("IndexedDBFactory::DeleteDatabase");
214 IndexedDBDatabase::Identifier
unique_identifier(origin_url
, name
);
215 IndexedDBDatabaseMap::iterator it
= database_map_
.find(unique_identifier
);
216 if (it
!= database_map_
.end()) {
217 // If there are any connections to the database, directly delete the
219 it
->second
->DeleteDatabase(callbacks
);
223 // TODO(dgrogan): Plumb data_loss back to script eventually?
224 blink::WebIDBDataLoss data_loss
;
225 std::string data_loss_message
;
226 bool disk_full
= false;
227 scoped_refptr
<IndexedDBBackingStore
> backing_store
=
228 OpenBackingStore(origin_url
,
234 if (!backing_store
) {
236 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError
,
238 "Internal error opening backing store "
239 "for indexedDB.deleteDatabase.")));
244 scoped_refptr
<IndexedDBDatabase
> database
= IndexedDBDatabase::Create(
245 name
, backing_store
, this, unique_identifier
, &s
);
247 IndexedDBDatabaseError
error(
248 blink::WebIDBDatabaseExceptionUnknownError
,
250 "Internal error creating database backend for "
251 "indexedDB.deleteDatabase."));
252 callbacks
->OnError(error
);
253 if (s
.IsCorruption())
254 HandleBackingStoreCorruption(origin_url
, error
);
258 database_map_
[unique_identifier
] = database
;
259 origin_dbs_
.insert(std::make_pair(origin_url
, database
));
260 database
->DeleteDatabase(callbacks
);
261 RemoveDatabaseFromMaps(unique_identifier
);
263 backing_store
= NULL
;
264 ReleaseBackingStore(origin_url
, false /* immediate */);
267 void IndexedDBFactory::DatabaseDeleted(
268 const IndexedDBDatabase::Identifier
& identifier
) {
269 // NULL after ContextDestroyed() called, and in some unit tests.
272 context_
->DatabaseDeleted(identifier
.first
);
275 void IndexedDBFactory::HandleBackingStoreFailure(const GURL
& origin_url
) {
276 // NULL after ContextDestroyed() called, and in some unit tests.
279 context_
->ForceClose(origin_url
,
280 IndexedDBContextImpl::FORCE_CLOSE_BACKING_STORE_FAILURE
);
283 void IndexedDBFactory::HandleBackingStoreCorruption(
284 const GURL
& origin_url
,
285 const IndexedDBDatabaseError
& error
) {
286 // Make a copy of origin_url as this is likely a reference to a member of a
287 // backing store which this function will be deleting.
288 GURL
saved_origin_url(origin_url
);
290 base::FilePath path_base
= context_
->data_path();
291 IndexedDBBackingStore::RecordCorruptionInfo(
292 path_base
, saved_origin_url
, base::UTF16ToUTF8(error
.message()));
293 HandleBackingStoreFailure(saved_origin_url
);
294 // Note: DestroyBackingStore only deletes LevelDB files, leaving all others,
295 // so our corruption info file will remain.
297 IndexedDBBackingStore::DestroyBackingStore(path_base
, saved_origin_url
);
299 DLOG(ERROR
) << "Unable to delete backing store: " << s
.ToString();
302 bool IndexedDBFactory::IsDatabaseOpen(const GURL
& origin_url
,
303 const base::string16
& name
) const {
305 return !!database_map_
.count(IndexedDBDatabase::Identifier(origin_url
, name
));
308 bool IndexedDBFactory::IsBackingStoreOpen(const GURL
& origin_url
) const {
309 return backing_store_map_
.find(origin_url
) != backing_store_map_
.end();
312 bool IndexedDBFactory::IsBackingStorePendingClose(const GURL
& origin_url
)
314 IndexedDBBackingStoreMap::const_iterator it
=
315 backing_store_map_
.find(origin_url
);
316 if (it
== backing_store_map_
.end())
318 return it
->second
->close_timer()->IsRunning();
321 scoped_refptr
<IndexedDBBackingStore
> IndexedDBFactory::OpenBackingStoreHelper(
322 const GURL
& origin_url
,
323 const base::FilePath
& data_directory
,
324 net::URLRequestContext
* request_context
,
325 blink::WebIDBDataLoss
* data_loss
,
326 std::string
* data_loss_message
,
329 return IndexedDBBackingStore::Open(this,
336 context_
->TaskRunner(),
340 scoped_refptr
<IndexedDBBackingStore
> IndexedDBFactory::OpenBackingStore(
341 const GURL
& origin_url
,
342 const base::FilePath
& data_directory
,
343 net::URLRequestContext
* request_context
,
344 blink::WebIDBDataLoss
* data_loss
,
345 std::string
* data_loss_message
,
347 const bool open_in_memory
= data_directory
.empty();
349 IndexedDBBackingStoreMap::iterator it2
= backing_store_map_
.find(origin_url
);
350 if (it2
!= backing_store_map_
.end()) {
351 it2
->second
->close_timer()->Stop();
355 scoped_refptr
<IndexedDBBackingStore
> backing_store
;
356 bool first_time
= false;
357 if (open_in_memory
) {
359 IndexedDBBackingStore::OpenInMemory(origin_url
, context_
->TaskRunner());
361 first_time
= !backends_opened_since_boot_
.count(origin_url
);
363 backing_store
= OpenBackingStoreHelper(origin_url
,
372 if (backing_store
.get()) {
374 backends_opened_since_boot_
.insert(origin_url
);
375 backing_store_map_
[origin_url
] = backing_store
;
376 // If an in-memory database, bind lifetime to this factory instance.
378 session_only_backing_stores_
.insert(backing_store
);
380 // All backing stores associated with this factory should be of the same
382 DCHECK_NE(session_only_backing_stores_
.empty(), open_in_memory
);
384 return backing_store
;
390 void IndexedDBFactory::Open(const base::string16
& name
,
391 const IndexedDBPendingConnection
& connection
,
392 net::URLRequestContext
* request_context
,
393 const GURL
& origin_url
,
394 const base::FilePath
& data_directory
) {
395 IDB_TRACE("IndexedDBFactory::Open");
396 scoped_refptr
<IndexedDBDatabase
> database
;
397 IndexedDBDatabase::Identifier
unique_identifier(origin_url
, name
);
398 IndexedDBDatabaseMap::iterator it
= database_map_
.find(unique_identifier
);
399 blink::WebIDBDataLoss data_loss
=
400 blink::WebIDBDataLossNone
;
401 std::string data_loss_message
;
402 bool disk_full
= false;
403 bool was_open
= (it
!= database_map_
.end());
405 scoped_refptr
<IndexedDBBackingStore
> backing_store
=
406 OpenBackingStore(origin_url
,
412 if (!backing_store
) {
414 connection
.callbacks
->OnError(
415 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError
,
417 "Encountered full disk while opening "
418 "backing store for indexedDB.open.")));
421 connection
.callbacks
->OnError(IndexedDBDatabaseError(
422 blink::WebIDBDatabaseExceptionUnknownError
,
424 "Internal error opening backing store for indexedDB.open.")));
429 database
= IndexedDBDatabase::Create(
430 name
, backing_store
, this, unique_identifier
, &s
);
432 DLOG(ERROR
) << "Unable to create the database";
433 IndexedDBDatabaseError
error(blink::WebIDBDatabaseExceptionUnknownError
,
435 "Internal error creating "
436 "database backend for "
438 connection
.callbacks
->OnError(error
);
439 if (s
.IsCorruption()) {
440 backing_store
= NULL
; // Closes the LevelDB so that it can be deleted
441 HandleBackingStoreCorruption(origin_url
, error
);
446 database
= it
->second
;
449 if (data_loss
!= blink::WebIDBDataLossNone
)
450 connection
.callbacks
->OnDataLoss(data_loss
, data_loss_message
);
452 database
->OpenConnection(connection
);
454 if (!was_open
&& database
->ConnectionCount() > 0) {
455 database_map_
[unique_identifier
] = database
;
456 origin_dbs_
.insert(std::make_pair(origin_url
, database
));
460 std::pair
<IndexedDBFactory::OriginDBMapIterator
,
461 IndexedDBFactory::OriginDBMapIterator
>
462 IndexedDBFactory::GetOpenDatabasesForOrigin(const GURL
& origin_url
) const {
463 return origin_dbs_
.equal_range(origin_url
);
466 size_t IndexedDBFactory::GetConnectionCount(const GURL
& origin_url
) const {
469 std::pair
<OriginDBMapIterator
, OriginDBMapIterator
> range
=
470 GetOpenDatabasesForOrigin(origin_url
);
471 for (OriginDBMapIterator it
= range
.first
; it
!= range
.second
; ++it
)
472 count
+= it
->second
->ConnectionCount();
477 } // namespace content