Revert 268405 "Make sure that ScratchBuffer::Allocate() always r..."
[chromium-blink-merge.git] / content / browser / indexed_db / indexed_db_factory.cc
blobbf6e0b9dfd5bb3272381499e4ea931fd7fbd228e
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;
20 namespace content {
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);
42 break;
47 void IndexedDBFactory::ReleaseDatabase(
48 const IndexedDBDatabase::Identifier& identifier,
49 bool forcedClose) {
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,
62 bool immediate) {
63 if (immediate) {
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))
74 return;
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.
79 if (immediate) {
80 CloseBackingStore(origin_url);
81 return;
84 // Start a timer to close the backing store, unless something else opens it
85 // in the mean time.
86 DCHECK(!backing_store_map_[origin_url]->close_timer()->IsRunning());
87 backing_store_map_[origin_url]->close_timer()->Start(
88 FROM_HERE,
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)
110 const {
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;
128 ++range.first;
129 db->ForceClose();
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();
143 ++it)
144 it->second->close_timer()->Stop();
145 backing_store_map_.clear();
146 backing_stores_with_active_blobs_.clear();
147 context_ = NULL;
150 void IndexedDBFactory::ReportOutstandingBlobs(const GURL& origin_url,
151 bool blobs_outstanding) {
152 if (!context_)
153 return;
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);
159 else
160 DCHECK(false);
161 } else {
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;
180 bool disk_full;
181 scoped_refptr<IndexedDBBackingStore> backing_store =
182 OpenBackingStore(origin_url,
183 data_directory,
184 request_context,
185 &data_loss,
186 &data_loss_message,
187 &disk_full);
188 if (!backing_store) {
189 callbacks->OnError(
190 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
191 "Internal error opening backing store for "
192 "indexedDB.webkitGetDatabaseNames."));
193 return;
196 leveldb::Status s;
197 std::vector<base::string16> names = backing_store->GetDatabaseNames(&s);
198 if (!s.ok()) {
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
218 // database.
219 it->second->DeleteDatabase(callbacks);
220 return;
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,
229 data_directory,
230 request_context,
231 &data_loss,
232 &data_loss_message,
233 &disk_full);
234 if (!backing_store) {
235 callbacks->OnError(
236 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
237 ASCIIToUTF16(
238 "Internal error opening backing store "
239 "for indexedDB.deleteDatabase.")));
240 return;
243 leveldb::Status s;
244 scoped_refptr<IndexedDBDatabase> database = IndexedDBDatabase::Create(
245 name, backing_store, this, unique_identifier, &s);
246 if (!database) {
247 IndexedDBDatabaseError error(
248 blink::WebIDBDatabaseExceptionUnknownError,
249 ASCIIToUTF16(
250 "Internal error creating database backend for "
251 "indexedDB.deleteDatabase."));
252 callbacks->OnError(error);
253 if (s.IsCorruption())
254 HandleBackingStoreCorruption(origin_url, error);
255 return;
258 database_map_[unique_identifier] = database;
259 origin_dbs_.insert(std::make_pair(origin_url, database));
260 database->DeleteDatabase(callbacks);
261 RemoveDatabaseFromMaps(unique_identifier);
262 database = NULL;
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.
270 if (!context_)
271 return;
272 context_->DatabaseDeleted(identifier.first);
275 void IndexedDBFactory::HandleBackingStoreFailure(const GURL& origin_url) {
276 // NULL after ContextDestroyed() called, and in some unit tests.
277 if (!context_)
278 return;
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);
289 DCHECK(context_);
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.
296 leveldb::Status s =
297 IndexedDBBackingStore::DestroyBackingStore(path_base, saved_origin_url);
298 if (!s.ok())
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)
313 const {
314 IndexedDBBackingStoreMap::const_iterator it =
315 backing_store_map_.find(origin_url);
316 if (it == backing_store_map_.end())
317 return false;
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,
327 bool* disk_full,
328 bool first_time) {
329 return IndexedDBBackingStore::Open(this,
330 origin_url,
331 data_directory,
332 request_context,
333 data_loss,
334 data_loss_message,
335 disk_full,
336 context_->TaskRunner(),
337 first_time);
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,
346 bool* disk_full) {
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();
352 return it2->second;
355 scoped_refptr<IndexedDBBackingStore> backing_store;
356 bool first_time = false;
357 if (open_in_memory) {
358 backing_store =
359 IndexedDBBackingStore::OpenInMemory(origin_url, context_->TaskRunner());
360 } else {
361 first_time = !backends_opened_since_boot_.count(origin_url);
363 backing_store = OpenBackingStoreHelper(origin_url,
364 data_directory,
365 request_context,
366 data_loss,
367 data_loss_message,
368 disk_full,
369 first_time);
372 if (backing_store.get()) {
373 if (first_time)
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.
377 if (open_in_memory)
378 session_only_backing_stores_.insert(backing_store);
380 // All backing stores associated with this factory should be of the same
381 // type.
382 DCHECK_NE(session_only_backing_stores_.empty(), open_in_memory);
384 return backing_store;
387 return 0;
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());
404 if (!was_open) {
405 scoped_refptr<IndexedDBBackingStore> backing_store =
406 OpenBackingStore(origin_url,
407 data_directory,
408 request_context,
409 &data_loss,
410 &data_loss_message,
411 &disk_full);
412 if (!backing_store) {
413 if (disk_full) {
414 connection.callbacks->OnError(
415 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError,
416 ASCIIToUTF16(
417 "Encountered full disk while opening "
418 "backing store for indexedDB.open.")));
419 return;
421 connection.callbacks->OnError(IndexedDBDatabaseError(
422 blink::WebIDBDatabaseExceptionUnknownError,
423 ASCIIToUTF16(
424 "Internal error opening backing store for indexedDB.open.")));
425 return;
428 leveldb::Status s;
429 database = IndexedDBDatabase::Create(
430 name, backing_store, this, unique_identifier, &s);
431 if (!database) {
432 DLOG(ERROR) << "Unable to create the database";
433 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
434 ASCIIToUTF16(
435 "Internal error creating "
436 "database backend for "
437 "indexedDB.open."));
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);
443 return;
445 } else {
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 {
467 size_t count(0);
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();
474 return count;
477 } // namespace content