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