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.h"
      6 
      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_tracing.h"
     13 #include "content/browser/indexed_db/indexed_db_transaction_coordinator.h"
     14 #include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
     15 #include "webkit/common/database/database_identifier.h"
     16 
     17 namespace content {
     18 
     19 const int64 kBackingStoreGracePeriodMs = 2000;
     20 
     21 IndexedDBFactory::IndexedDBFactory(IndexedDBContextImpl* context)
     22     : context_(context) {}
     23 
     24 IndexedDBFactory::~IndexedDBFactory() {}
     25 
     26 void IndexedDBFactory::ReleaseDatabase(
     27     const IndexedDBDatabase::Identifier& identifier,
     28     const GURL& origin_url,
     29     bool forcedClose) {
     30   IndexedDBDatabaseMap::iterator it = database_map_.find(identifier);
     31   DCHECK(it != database_map_.end());
     32   DCHECK(!it->second->backing_store());
     33   database_map_.erase(it);
     34 
     35   // No grace period on a forced-close, as the initiator is
     36   // assuming the backing store will be released once all
     37   // connections are closed.
     38   ReleaseBackingStore(origin_url, forcedClose);
     39 }
     40 
     41 void IndexedDBFactory::ReleaseBackingStore(const GURL& origin_url,
     42                                            bool immediate) {
     43   // Only close if this is the last reference.
     44   if (!HasLastBackingStoreReference(origin_url))
     45     return;
     46 
     47   // If this factory does hold the last reference to the backing store, it can
     48   // be closed - but unless requested to close it immediately, keep it around
     49   // for a short period so that a re-open is fast.
     50   if (immediate) {
     51     CloseBackingStore(origin_url);
     52     return;
     53   }
     54 
     55   // Start a timer to close the backing store, unless something else opens it
     56   // in the mean time.
     57   DCHECK(!backing_store_map_[origin_url]->close_timer()->IsRunning());
     58   backing_store_map_[origin_url]->close_timer()->Start(
     59       FROM_HERE,
     60       base::TimeDelta::FromMilliseconds(kBackingStoreGracePeriodMs),
     61       base::Bind(&IndexedDBFactory::MaybeCloseBackingStore, this, origin_url));
     62 }
     63 
     64 void IndexedDBFactory::MaybeCloseBackingStore(const GURL& origin_url) {
     65   // Another reference may have opened since the maybe-close was posted, so it
     66   // is necessary to check again.
     67   if (HasLastBackingStoreReference(origin_url))
     68     CloseBackingStore(origin_url);
     69 }
     70 
     71 void IndexedDBFactory::CloseBackingStore(const GURL& origin_url) {
     72   IndexedDBBackingStoreMap::iterator it = backing_store_map_.find(origin_url);
     73   DCHECK(it != backing_store_map_.end());
     74   // Stop the timer (if it's running) - this may happen if the timer was started
     75   // and then a forced close occurs.
     76   it->second->close_timer()->Stop();
     77   backing_store_map_.erase(it);
     78 }
     79 
     80 bool IndexedDBFactory::HasLastBackingStoreReference(const GURL& origin_url)
     81     const {
     82   IndexedDBBackingStore* ptr;
     83   {
     84     // Scope so that the implicit scoped_refptr<> is freed.
     85     IndexedDBBackingStoreMap::const_iterator it =
     86         backing_store_map_.find(origin_url);
     87     DCHECK(it != backing_store_map_.end());
     88     ptr = it->second.get();
     89   }
     90   return ptr->HasOneRef();
     91 }
     92 
     93 void IndexedDBFactory::ForceClose(const GURL& origin_url) {
     94   if (backing_store_map_.find(origin_url) != backing_store_map_.end())
     95     ReleaseBackingStore(origin_url, true /* immediate */);
     96 }
     97 
     98 void IndexedDBFactory::ContextDestroyed() {
     99   // Timers on backing stores hold a reference to this factory. When the
    100   // context (which nominally owns this factory) is destroyed during thread
    101   // termination the timers must be stopped so that this factory and the
    102   // stores can be disposed of.
    103   for (IndexedDBBackingStoreMap::iterator it = backing_store_map_.begin();
    104        it != backing_store_map_.end();
    105        ++it)
    106     it->second->close_timer()->Stop();
    107   backing_store_map_.clear();
    108   context_ = NULL;
    109 }
    110 
    111 void IndexedDBFactory::GetDatabaseNames(
    112     scoped_refptr<IndexedDBCallbacks> callbacks,
    113     const GURL& origin_url,
    114     const base::FilePath& data_directory) {
    115   IDB_TRACE("IndexedDBFactory::GetDatabaseNames");
    116   // TODO(dgrogan): Plumb data_loss back to script eventually?
    117   blink::WebIDBDataLoss data_loss;
    118   std::string data_loss_message;
    119   bool disk_full;
    120   scoped_refptr<IndexedDBBackingStore> backing_store =
    121       OpenBackingStore(origin_url,
    122                        data_directory,
    123                        &data_loss,
    124                        &data_loss_message,
    125                        &disk_full);
    126   if (!backing_store) {
    127     callbacks->OnError(
    128         IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
    129                                "Internal error opening backing store for "
    130                                "indexedDB.webkitGetDatabaseNames."));
    131     return;
    132   }
    133 
    134   callbacks->OnSuccess(backing_store->GetDatabaseNames());
    135   ReleaseBackingStore(origin_url, false /* immediate */);
    136 }
    137 
    138 void IndexedDBFactory::DeleteDatabase(
    139     const base::string16& name,
    140     scoped_refptr<IndexedDBCallbacks> callbacks,
    141     const GURL& origin_url,
    142     const base::FilePath& data_directory) {
    143   IDB_TRACE("IndexedDBFactory::DeleteDatabase");
    144   IndexedDBDatabase::Identifier unique_identifier(origin_url, name);
    145   IndexedDBDatabaseMap::iterator it = database_map_.find(unique_identifier);
    146   if (it != database_map_.end()) {
    147     // If there are any connections to the database, directly delete the
    148     // database.
    149     it->second->DeleteDatabase(callbacks);
    150     return;
    151   }
    152 
    153   // TODO(dgrogan): Plumb data_loss back to script eventually?
    154   blink::WebIDBDataLoss data_loss;
    155   std::string data_loss_message;
    156   bool disk_full = false;
    157   scoped_refptr<IndexedDBBackingStore> backing_store =
    158       OpenBackingStore(origin_url,
    159                        data_directory,
    160                        &data_loss,
    161                        &data_loss_message,
    162                        &disk_full);
    163   if (!backing_store) {
    164     callbacks->OnError(
    165         IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
    166                                ASCIIToUTF16(
    167                                    "Internal error opening backing store "
    168                                    "for indexedDB.deleteDatabase.")));
    169     return;
    170   }
    171 
    172   scoped_refptr<IndexedDBDatabase> database =
    173       IndexedDBDatabase::Create(name, backing_store, this, unique_identifier);
    174   if (!database) {
    175     callbacks->OnError(IndexedDBDatabaseError(
    176         blink::WebIDBDatabaseExceptionUnknownError,
    177         ASCIIToUTF16(
    178             "Internal error creating database backend for "
    179             "indexedDB.deleteDatabase.")));
    180     return;
    181   }
    182 
    183   database_map_[unique_identifier] = database;
    184   database->DeleteDatabase(callbacks);
    185   database_map_.erase(unique_identifier);
    186   ReleaseBackingStore(origin_url, false /* immediate */);
    187 }
    188 
    189 void IndexedDBFactory::HandleBackingStoreFailure(const GURL& origin_url) {
    190   // NULL after ContextDestroyed() called, and in some unit tests.
    191   if (!context_)
    192     return;
    193   context_->ForceClose(origin_url);
    194 }
    195 
    196 bool IndexedDBFactory::IsDatabaseOpen(const GURL& origin_url,
    197                                       const base::string16& name) const {
    198 
    199   return !!database_map_.count(IndexedDBDatabase::Identifier(origin_url, name));
    200 }
    201 
    202 bool IndexedDBFactory::IsBackingStoreOpen(const GURL& origin_url) const {
    203   return backing_store_map_.find(origin_url) != backing_store_map_.end();
    204 }
    205 
    206 bool IndexedDBFactory::IsBackingStorePendingClose(const GURL& origin_url)
    207     const {
    208   IndexedDBBackingStoreMap::const_iterator it =
    209       backing_store_map_.find(origin_url);
    210   if (it == backing_store_map_.end())
    211     return false;
    212   return it->second->close_timer()->IsRunning();
    213 }
    214 
    215 scoped_refptr<IndexedDBBackingStore> IndexedDBFactory::OpenBackingStore(
    216     const GURL& origin_url,
    217     const base::FilePath& data_directory,
    218     blink::WebIDBDataLoss* data_loss,
    219     std::string* data_loss_message,
    220     bool* disk_full) {
    221   const bool open_in_memory = data_directory.empty();
    222 
    223   IndexedDBBackingStoreMap::iterator it2 = backing_store_map_.find(origin_url);
    224   if (it2 != backing_store_map_.end()) {
    225     it2->second->close_timer()->Stop();
    226     return it2->second;
    227   }
    228 
    229   scoped_refptr<IndexedDBBackingStore> backing_store;
    230   if (open_in_memory) {
    231     backing_store = IndexedDBBackingStore::OpenInMemory(origin_url);
    232   } else {
    233     backing_store = IndexedDBBackingStore::Open(origin_url,
    234                                                 data_directory,
    235                                                 data_loss,
    236                                                 data_loss_message,
    237                                                 disk_full);
    238   }
    239 
    240   if (backing_store.get()) {
    241     backing_store_map_[origin_url] = backing_store;
    242     // If an in-memory database, bind lifetime to this factory instance.
    243     if (open_in_memory)
    244       session_only_backing_stores_.insert(backing_store);
    245 
    246     // All backing stores associated with this factory should be of the same
    247     // type.
    248     DCHECK(session_only_backing_stores_.empty() || open_in_memory);
    249 
    250     return backing_store;
    251   }
    252 
    253   return 0;
    254 }
    255 
    256 void IndexedDBFactory::Open(
    257     const base::string16& name,
    258     int64 version,
    259     int64 transaction_id,
    260     scoped_refptr<IndexedDBCallbacks> callbacks,
    261     scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
    262     const GURL& origin_url,
    263     const base::FilePath& data_directory) {
    264   IDB_TRACE("IndexedDBFactory::Open");
    265   scoped_refptr<IndexedDBDatabase> database;
    266   IndexedDBDatabase::Identifier unique_identifier(origin_url, name);
    267   IndexedDBDatabaseMap::iterator it = database_map_.find(unique_identifier);
    268   blink::WebIDBDataLoss data_loss =
    269       blink::WebIDBDataLossNone;
    270   std::string data_loss_message;
    271   bool disk_full = false;
    272   bool was_open = (it != database_map_.end());
    273   if (!was_open) {
    274     scoped_refptr<IndexedDBBackingStore> backing_store =
    275         OpenBackingStore(origin_url,
    276                          data_directory,
    277                          &data_loss,
    278                          &data_loss_message,
    279                          &disk_full);
    280     if (!backing_store) {
    281       if (disk_full) {
    282         callbacks->OnError(
    283             IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError,
    284                                    ASCIIToUTF16(
    285                                        "Encountered full disk while opening "
    286                                        "backing store for indexedDB.open.")));
    287         return;
    288       }
    289       callbacks->OnError(IndexedDBDatabaseError(
    290           blink::WebIDBDatabaseExceptionUnknownError,
    291           ASCIIToUTF16(
    292               "Internal error opening backing store for indexedDB.open.")));
    293       return;
    294     }
    295 
    296     database =
    297         IndexedDBDatabase::Create(name, backing_store, this, unique_identifier);
    298     if (!database) {
    299       callbacks->OnError(IndexedDBDatabaseError(
    300           blink::WebIDBDatabaseExceptionUnknownError,
    301           ASCIIToUTF16(
    302               "Internal error creating database backend for indexedDB.open.")));
    303       return;
    304     }
    305   } else {
    306     database = it->second;
    307   }
    308 
    309   database->OpenConnection(callbacks,
    310                            database_callbacks,
    311                            transaction_id,
    312                            version,
    313                            data_loss,
    314                            data_loss_message);
    315 
    316   if (!was_open && database->ConnectionCount() > 0)
    317     database_map_[unique_identifier] = database;
    318 }
    319 
    320 std::vector<IndexedDBDatabase*> IndexedDBFactory::GetOpenDatabasesForOrigin(
    321     const GURL& origin_url) const {
    322   std::vector<IndexedDBDatabase*> result;
    323   for (IndexedDBDatabaseMap::const_iterator it = database_map_.begin();
    324        it != database_map_.end();
    325        ++it) {
    326     if (it->first.first == origin_url)
    327       result.push_back(it->second.get());
    328   }
    329   return result;
    330 }
    331 
    332 }  // namespace content
    333