• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
IndexedDBFactory(IndexedDBContextImpl * context)27 IndexedDBFactory::IndexedDBFactory(IndexedDBContextImpl* context)
28     : context_(context) {}
29 
~IndexedDBFactory()30 IndexedDBFactory::~IndexedDBFactory() {}
31 
RemoveDatabaseFromMaps(const IndexedDBDatabase::Identifier & identifier)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 
ReleaseDatabase(const IndexedDBDatabase::Identifier & identifier,bool forcedClose)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 
ReleaseBackingStore(const GURL & origin_url,bool immediate)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 
MaybeCloseBackingStore(const GURL & origin_url)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 
CloseBackingStore(const GURL & origin_url)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 
HasLastBackingStoreReference(const GURL & origin_url) const112 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 
ForceClose(const GURL & origin_url)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 
ContextDestroyed()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 
ReportOutstandingBlobs(const GURL & origin_url,bool blobs_outstanding)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 
GetDatabaseNames(scoped_refptr<IndexedDBCallbacks> callbacks,const GURL & origin_url,const base::FilePath & data_directory,net::URLRequestContext * request_context)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 
DeleteDatabase(const base::string16 & name,net::URLRequestContext * request_context,scoped_refptr<IndexedDBCallbacks> callbacks,const GURL & origin_url,const base::FilePath & data_directory)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 
DatabaseDeleted(const IndexedDBDatabase::Identifier & identifier)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 
HandleBackingStoreFailure(const GURL & origin_url)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 
HandleBackingStoreCorruption(const GURL & origin_url,const IndexedDBDatabaseError & error)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 
IsDatabaseOpen(const GURL & origin_url,const base::string16 & name) const305 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 
IsBackingStoreOpen(const GURL & origin_url) const310 bool IndexedDBFactory::IsBackingStoreOpen(const GURL& origin_url) const {
311   return backing_store_map_.find(origin_url) != backing_store_map_.end();
312 }
313 
IsBackingStorePendingClose(const GURL & origin_url) const314 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 
OpenBackingStoreHelper(const GURL & origin_url,const base::FilePath & data_directory,net::URLRequestContext * request_context,blink::WebIDBDataLoss * data_loss,std::string * data_loss_message,bool * disk_full,bool first_time)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 
OpenBackingStore(const GURL & origin_url,const base::FilePath & data_directory,net::URLRequestContext * request_context,blink::WebIDBDataLoss * data_loss,std::string * data_loss_message,bool * disk_full)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 
Open(const base::string16 & name,const IndexedDBPendingConnection & connection,net::URLRequestContext * request_context,const GURL & origin_url,const base::FilePath & data_directory)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>
GetOpenDatabasesForOrigin(const GURL & origin_url) const464 IndexedDBFactory::GetOpenDatabasesForOrigin(const GURL& origin_url) const {
465   return origin_dbs_.equal_range(origin_url);
466 }
467 
GetConnectionCount(const GURL & origin_url) const468 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
480