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