Home | History | Annotate | Download | only in indexed_db
      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_impl.h"
      6 
      7 #include <utility>
      8 #include <vector>
      9 
     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/WebIDBDatabaseException.h"
     20 #include "third_party/leveldatabase/env_chromium.h"
     21 
     22 using base::ASCIIToUTF16;
     23 
     24 namespace content {
     25 
     26 const int64 kBackingStoreGracePeriodMs = 2000;
     27 
     28 IndexedDBFactoryImpl::IndexedDBFactoryImpl(IndexedDBContextImpl* context)
     29     : context_(context) {
     30 }
     31 
     32 IndexedDBFactoryImpl::~IndexedDBFactoryImpl() {
     33 }
     34 
     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);
     41 
     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;
     49     }
     50   }
     51 }
     52 
     53 void IndexedDBFactoryImpl::ReleaseDatabase(
     54     const IndexedDBDatabase::Identifier& identifier,
     55     bool forcedClose) {
     56   DCHECK(!database_map_.find(identifier)->second->backing_store());
     57 
     58   RemoveDatabaseFromMaps(identifier);
     59 
     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, forcedClose);
     64 }
     65 
     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);
     74     }
     75   }
     76 
     77   // Only close if this is the last reference.
     78   if (!HasLastBackingStoreReference(origin_url))
     79     return;
     80 
     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;
     87   }
     88 
     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));
     97 }
     98 
     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);
    104 }
    105 
    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);
    113 }
    114 
    115 bool IndexedDBFactoryImpl::HasLastBackingStoreReference(
    116     const GURL& origin_url) const {
    117   IndexedDBBackingStore* ptr;
    118   {
    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();
    124   }
    125   return ptr->HasOneRef();
    126 }
    127 
    128 void IndexedDBFactoryImpl::ForceClose(const GURL& origin_url) {
    129   OriginDBs range = GetOpenDatabasesForOrigin(origin_url);
    130 
    131   while (range.first != range.second) {
    132     IndexedDBDatabase* db = range.first->second;
    133     ++range.first;
    134     db->ForceClose();
    135   }
    136 
    137   if (backing_store_map_.find(origin_url) != backing_store_map_.end())
    138     ReleaseBackingStore(origin_url, true /* immediate */);
    139 }
    140 
    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 (IndexedDBBackingStoreMap::iterator it = backing_store_map_.begin();
    147        it != backing_store_map_.end();
    148        ++it)
    149     it->second->close_timer()->Stop();
    150   backing_store_map_.clear();
    151   backing_stores_with_active_blobs_.clear();
    152   context_ = NULL;
    153 }
    154 
    155 void IndexedDBFactoryImpl::ReportOutstandingBlobs(const GURL& origin_url,
    156                                                   bool blobs_outstanding) {
    157   if (!context_)
    158     return;
    159   if (blobs_outstanding) {
    160     DCHECK(!backing_stores_with_active_blobs_.count(origin_url));
    161     IndexedDBBackingStoreMap::iterator it = backing_store_map_.find(origin_url);
    162     if (it != backing_store_map_.end())
    163       backing_stores_with_active_blobs_.insert(*it);
    164     else
    165       DCHECK(false);
    166   } else {
    167     IndexedDBBackingStoreMap::iterator it =
    168         backing_stores_with_active_blobs_.find(origin_url);
    169     if (it != backing_stores_with_active_blobs_.end()) {
    170       backing_stores_with_active_blobs_.erase(it);
    171       ReleaseBackingStore(origin_url, false /* immediate */);
    172     }
    173   }
    174 }
    175 
    176 void IndexedDBFactoryImpl::GetDatabaseNames(
    177     scoped_refptr<IndexedDBCallbacks> callbacks,
    178     const GURL& origin_url,
    179     const base::FilePath& data_directory,
    180     net::URLRequestContext* request_context) {
    181   IDB_TRACE("IndexedDBFactoryImpl::GetDatabaseNames");
    182   // TODO(dgrogan): Plumb data_loss back to script eventually?
    183   blink::WebIDBDataLoss data_loss;
    184   std::string data_loss_message;
    185   bool disk_full;
    186   leveldb::Status s;
    187   // TODO(cmumford): Handle this error
    188   scoped_refptr<IndexedDBBackingStore> backing_store =
    189       OpenBackingStore(origin_url,
    190                        data_directory,
    191                        request_context,
    192                        &data_loss,
    193                        &data_loss_message,
    194                        &disk_full,
    195                        &s);
    196   if (!backing_store.get()) {
    197     callbacks->OnError(
    198         IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
    199                                "Internal error opening backing store for "
    200                                "indexedDB.webkitGetDatabaseNames."));
    201     return;
    202   }
    203 
    204   std::vector<base::string16> names = backing_store->GetDatabaseNames(&s);
    205   if (!s.ok()) {
    206     DLOG(ERROR) << "Internal error getting database names";
    207     IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
    208                                  "Internal error opening backing store for "
    209                                  "indexedDB.webkitGetDatabaseNames.");
    210     callbacks->OnError(error);
    211     backing_store = NULL;
    212     if (s.IsCorruption())
    213       HandleBackingStoreCorruption(origin_url, error);
    214     return;
    215   }
    216   callbacks->OnSuccess(names);
    217   backing_store = NULL;
    218   ReleaseBackingStore(origin_url, false /* immediate */);
    219 }
    220 
    221 void IndexedDBFactoryImpl::DeleteDatabase(
    222     const base::string16& name,
    223     net::URLRequestContext* request_context,
    224     scoped_refptr<IndexedDBCallbacks> callbacks,
    225     const GURL& origin_url,
    226     const base::FilePath& data_directory) {
    227   IDB_TRACE("IndexedDBFactoryImpl::DeleteDatabase");
    228   IndexedDBDatabase::Identifier unique_identifier(origin_url, name);
    229   IndexedDBDatabaseMap::iterator it = database_map_.find(unique_identifier);
    230   if (it != database_map_.end()) {
    231     // If there are any connections to the database, directly delete the
    232     // database.
    233     it->second->DeleteDatabase(callbacks);
    234     return;
    235   }
    236 
    237   // TODO(dgrogan): Plumb data_loss back to script eventually?
    238   blink::WebIDBDataLoss data_loss;
    239   std::string data_loss_message;
    240   bool disk_full = false;
    241   leveldb::Status s;
    242   scoped_refptr<IndexedDBBackingStore> backing_store =
    243       OpenBackingStore(origin_url,
    244                        data_directory,
    245                        request_context,
    246                        &data_loss,
    247                        &data_loss_message,
    248                        &disk_full,
    249                        &s);
    250   if (!backing_store.get()) {
    251     IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
    252                                  ASCIIToUTF16(
    253                                      "Internal error opening backing store "
    254                                      "for indexedDB.deleteDatabase."));
    255     callbacks->OnError(error);
    256     if (s.IsCorruption()) {
    257       HandleBackingStoreCorruption(origin_url, error);
    258     }
    259     return;
    260   }
    261 
    262   scoped_refptr<IndexedDBDatabase> database = IndexedDBDatabase::Create(
    263       name, backing_store.get(), this, unique_identifier, &s);
    264   if (!database.get()) {
    265     IndexedDBDatabaseError error(
    266         blink::WebIDBDatabaseExceptionUnknownError,
    267         ASCIIToUTF16(
    268             "Internal error creating database backend for "
    269             "indexedDB.deleteDatabase."));
    270     callbacks->OnError(error);
    271     if (leveldb_env::IsCorruption(s)) {
    272       backing_store = NULL;
    273       HandleBackingStoreCorruption(origin_url, error);
    274     }
    275     return;
    276   }
    277 
    278   database_map_[unique_identifier] = database.get();
    279   origin_dbs_.insert(std::make_pair(origin_url, database.get()));
    280   database->DeleteDatabase(callbacks);
    281   RemoveDatabaseFromMaps(unique_identifier);
    282   database = NULL;
    283   backing_store = NULL;
    284   ReleaseBackingStore(origin_url, false /* immediate */);
    285 }
    286 
    287 void IndexedDBFactoryImpl::DatabaseDeleted(
    288     const IndexedDBDatabase::Identifier& identifier) {
    289   // NULL after ContextDestroyed() called, and in some unit tests.
    290   if (!context_)
    291     return;
    292   context_->DatabaseDeleted(identifier.first);
    293 }
    294 
    295 void IndexedDBFactoryImpl::HandleBackingStoreFailure(const GURL& origin_url) {
    296   // NULL after ContextDestroyed() called, and in some unit tests.
    297   if (!context_)
    298     return;
    299   context_->ForceClose(origin_url,
    300                        IndexedDBContextImpl::FORCE_CLOSE_BACKING_STORE_FAILURE);
    301 }
    302 
    303 void IndexedDBFactoryImpl::HandleBackingStoreCorruption(
    304     const GURL& origin_url,
    305     const IndexedDBDatabaseError& error) {
    306   // Make a copy of origin_url as this is likely a reference to a member of a
    307   // backing store which this function will be deleting.
    308   GURL saved_origin_url(origin_url);
    309   DCHECK(context_);
    310   base::FilePath path_base = context_->data_path();
    311   IndexedDBBackingStore::RecordCorruptionInfo(
    312       path_base, saved_origin_url, base::UTF16ToUTF8(error.message()));
    313   HandleBackingStoreFailure(saved_origin_url);
    314   // Note: DestroyBackingStore only deletes LevelDB files, leaving all others,
    315   //       so our corruption info file will remain.
    316   leveldb::Status s =
    317       IndexedDBBackingStore::DestroyBackingStore(path_base, saved_origin_url);
    318   if (!s.ok())
    319     DLOG(ERROR) << "Unable to delete backing store: " << s.ToString();
    320 }
    321 
    322 bool IndexedDBFactoryImpl::IsDatabaseOpen(const GURL& origin_url,
    323                                           const base::string16& name) const {
    324   return !!database_map_.count(IndexedDBDatabase::Identifier(origin_url, name));
    325 }
    326 
    327 bool IndexedDBFactoryImpl::IsBackingStoreOpen(const GURL& origin_url) const {
    328   return backing_store_map_.find(origin_url) != backing_store_map_.end();
    329 }
    330 
    331 bool IndexedDBFactoryImpl::IsBackingStorePendingClose(
    332     const GURL& origin_url) const {
    333   IndexedDBBackingStoreMap::const_iterator it =
    334       backing_store_map_.find(origin_url);
    335   if (it == backing_store_map_.end())
    336     return false;
    337   return it->second->close_timer()->IsRunning();
    338 }
    339 
    340 scoped_refptr<IndexedDBBackingStore>
    341 IndexedDBFactoryImpl::OpenBackingStoreHelper(
    342     const GURL& origin_url,
    343     const base::FilePath& data_directory,
    344     net::URLRequestContext* request_context,
    345     blink::WebIDBDataLoss* data_loss,
    346     std::string* data_loss_message,
    347     bool* disk_full,
    348     bool first_time,
    349     leveldb::Status* status) {
    350   return IndexedDBBackingStore::Open(this,
    351                                      origin_url,
    352                                      data_directory,
    353                                      request_context,
    354                                      data_loss,
    355                                      data_loss_message,
    356                                      disk_full,
    357                                      context_->TaskRunner(),
    358                                      first_time,
    359                                      status);
    360 }
    361 
    362 scoped_refptr<IndexedDBBackingStore> IndexedDBFactoryImpl::OpenBackingStore(
    363     const GURL& origin_url,
    364     const base::FilePath& data_directory,
    365     net::URLRequestContext* request_context,
    366     blink::WebIDBDataLoss* data_loss,
    367     std::string* data_loss_message,
    368     bool* disk_full,
    369     leveldb::Status* status) {
    370   const bool open_in_memory = data_directory.empty();
    371 
    372   IndexedDBBackingStoreMap::iterator it2 = backing_store_map_.find(origin_url);
    373   if (it2 != backing_store_map_.end()) {
    374     it2->second->close_timer()->Stop();
    375     return it2->second;
    376   }
    377 
    378   scoped_refptr<IndexedDBBackingStore> backing_store;
    379   bool first_time = false;
    380   if (open_in_memory) {
    381     backing_store = IndexedDBBackingStore::OpenInMemory(
    382         origin_url, context_->TaskRunner(), status);
    383   } else {
    384     first_time = !backends_opened_since_boot_.count(origin_url);
    385 
    386     backing_store = OpenBackingStoreHelper(origin_url,
    387                                            data_directory,
    388                                            request_context,
    389                                            data_loss,
    390                                            data_loss_message,
    391                                            disk_full,
    392                                            first_time,
    393                                            status);
    394   }
    395 
    396   if (backing_store.get()) {
    397     if (first_time)
    398       backends_opened_since_boot_.insert(origin_url);
    399     backing_store_map_[origin_url] = backing_store;
    400     // If an in-memory database, bind lifetime to this factory instance.
    401     if (open_in_memory)
    402       session_only_backing_stores_.insert(backing_store);
    403 
    404     // All backing stores associated with this factory should be of the same
    405     // type.
    406     DCHECK_NE(session_only_backing_stores_.empty(), open_in_memory);
    407 
    408     return backing_store;
    409   }
    410 
    411   return 0;
    412 }
    413 
    414 void IndexedDBFactoryImpl::Open(const base::string16& name,
    415                                 const IndexedDBPendingConnection& connection,
    416                                 net::URLRequestContext* request_context,
    417                                 const GURL& origin_url,
    418                                 const base::FilePath& data_directory) {
    419   IDB_TRACE("IndexedDBFactoryImpl::Open");
    420   scoped_refptr<IndexedDBDatabase> database;
    421   IndexedDBDatabase::Identifier unique_identifier(origin_url, name);
    422   IndexedDBDatabaseMap::iterator it = database_map_.find(unique_identifier);
    423   blink::WebIDBDataLoss data_loss =
    424       blink::WebIDBDataLossNone;
    425   std::string data_loss_message;
    426   bool disk_full = false;
    427   bool was_open = (it != database_map_.end());
    428   if (!was_open) {
    429     leveldb::Status s;
    430     scoped_refptr<IndexedDBBackingStore> backing_store =
    431         OpenBackingStore(origin_url,
    432                          data_directory,
    433                          request_context,
    434                          &data_loss,
    435                          &data_loss_message,
    436                          &disk_full,
    437                          &s);
    438     if (!backing_store.get()) {
    439       if (disk_full) {
    440         connection.callbacks->OnError(
    441             IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError,
    442                                    ASCIIToUTF16(
    443                                        "Encountered full disk while opening "
    444                                        "backing store for indexedDB.open.")));
    445         return;
    446       }
    447       IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
    448                                    ASCIIToUTF16(
    449                                        "Internal error opening backing store"
    450                                        " for indexedDB.open."));
    451       connection.callbacks->OnError(error);
    452       if (s.IsCorruption()) {
    453         HandleBackingStoreCorruption(origin_url, error);
    454       }
    455       return;
    456     }
    457 
    458     database = IndexedDBDatabase::Create(
    459         name, backing_store.get(), this, unique_identifier, &s);
    460     if (!database.get()) {
    461       DLOG(ERROR) << "Unable to create the database";
    462       IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
    463                                    ASCIIToUTF16(
    464                                        "Internal error creating "
    465                                        "database backend for "
    466                                        "indexedDB.open."));
    467       connection.callbacks->OnError(error);
    468       if (leveldb_env::IsCorruption(s)) {
    469         backing_store = NULL;  // Closes the LevelDB so that it can be deleted
    470         HandleBackingStoreCorruption(origin_url, error);
    471       }
    472       return;
    473     }
    474   } else {
    475     database = it->second;
    476   }
    477 
    478   if (data_loss != blink::WebIDBDataLossNone)
    479     connection.callbacks->OnDataLoss(data_loss, data_loss_message);
    480 
    481   database->OpenConnection(connection);
    482 
    483   if (!was_open && database->ConnectionCount() > 0) {
    484     database_map_[unique_identifier] = database.get();
    485     origin_dbs_.insert(std::make_pair(origin_url, database.get()));
    486   }
    487 }
    488 
    489 std::pair<IndexedDBFactoryImpl::OriginDBMapIterator,
    490           IndexedDBFactoryImpl::OriginDBMapIterator>
    491 IndexedDBFactoryImpl::GetOpenDatabasesForOrigin(const GURL& origin_url) const {
    492   return origin_dbs_.equal_range(origin_url);
    493 }
    494 
    495 size_t IndexedDBFactoryImpl::GetConnectionCount(const GURL& origin_url) const {
    496   size_t count(0);
    497 
    498   OriginDBs range = GetOpenDatabasesForOrigin(origin_url);
    499   for (OriginDBMapIterator it = range.first; it != range.second; ++it)
    500     count += it->second->ConnectionCount();
    501 
    502   return count;
    503 }
    504 
    505 }  // namespace content
    506