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 leveldb::Status s; | |
185 // TODO(cmumford): Handle this error | |
186 scoped_refptr<IndexedDBBackingStore> backing_store = | |
187 OpenBackingStore(origin_url, | |
188 data_directory, | |
189 request_context, | |
190 &data_loss, | |
191 &data_loss_message, | |
192 &disk_full, | |
193 &s); | |
194 if (!backing_store) { | |
195 callbacks->OnError( | |
196 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError, | |
197 "Internal error opening backing store for " | |
198 "indexedDB.webkitGetDatabaseNames.")); | |
199 return; | |
200 } | |
201 | |
202 std::vector<base::string16> names = backing_store->GetDatabaseNames(&s); | |
203 if (!s.ok()) { | |
204 DLOG(ERROR) << "Internal error getting database names"; | |
205 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, | |
206 "Internal error opening backing store for " | |
207 "indexedDB.webkitGetDatabaseNames."); | |
208 callbacks->OnError(error); | |
209 backing_store = NULL; | |
210 if (s.IsCorruption()) | |
211 HandleBackingStoreCorruption(origin_url, error); | |
212 return; | |
213 } | |
214 callbacks->OnSuccess(names); | |
215 backing_store = NULL; | |
216 ReleaseBackingStore(origin_url, false /* immediate */); | |
217 } | |
218 | |
219 void IndexedDBFactory::DeleteDatabase( | |
220 const base::string16& name, | |
221 net::URLRequestContext* request_context, | |
222 scoped_refptr<IndexedDBCallbacks> callbacks, | |
223 const GURL& origin_url, | |
224 const base::FilePath& data_directory) { | |
225 IDB_TRACE("IndexedDBFactory::DeleteDatabase"); | |
226 IndexedDBDatabase::Identifier unique_identifier(origin_url, name); | |
227 IndexedDBDatabaseMap::iterator it = database_map_.find(unique_identifier); | |
228 if (it != database_map_.end()) { | |
229 // If there are any connections to the database, directly delete the | |
230 // database. | |
231 it->second->DeleteDatabase(callbacks); | |
232 return; | |
233 } | |
234 | |
235 // TODO(dgrogan): Plumb data_loss back to script eventually? | |
236 blink::WebIDBDataLoss data_loss; | |
237 std::string data_loss_message; | |
238 bool disk_full = false; | |
239 leveldb::Status s; | |
240 scoped_refptr<IndexedDBBackingStore> backing_store = | |
241 OpenBackingStore(origin_url, | |
242 data_directory, | |
243 request_context, | |
244 &data_loss, | |
245 &data_loss_message, | |
246 &disk_full, | |
247 &s); | |
248 if (!backing_store) { | |
249 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, | |
250 ASCIIToUTF16( | |
251 "Internal error opening backing store " | |
252 "for indexedDB.deleteDatabase.")); | |
253 callbacks->OnError(error); | |
254 if (s.IsCorruption()) { | |
255 HandleBackingStoreCorruption(origin_url, error); | |
256 } | |
257 return; | |
258 } | |
259 | |
260 scoped_refptr<IndexedDBDatabase> database = IndexedDBDatabase::Create( | |
261 name, backing_store, this, unique_identifier, &s); | |
262 if (!database) { | |
263 IndexedDBDatabaseError error( | |
264 blink::WebIDBDatabaseExceptionUnknownError, | |
265 ASCIIToUTF16( | |
266 "Internal error creating database backend for " | |
267 "indexedDB.deleteDatabase.")); | |
268 callbacks->OnError(error); | |
269 if (leveldb_env::IsCorruption(s)) { | |
270 backing_store = NULL; | |
271 HandleBackingStoreCorruption(origin_url, error); | |
272 } | |
273 return; | |
274 } | |
275 | |
276 database_map_[unique_identifier] = database; | |
277 origin_dbs_.insert(std::make_pair(origin_url, database)); | |
278 database->DeleteDatabase(callbacks); | |
279 RemoveDatabaseFromMaps(unique_identifier); | |
280 database = NULL; | |
281 backing_store = NULL; | |
282 ReleaseBackingStore(origin_url, false /* immediate */); | |
283 } | |
284 | |
285 void IndexedDBFactory::DatabaseDeleted( | |
286 const IndexedDBDatabase::Identifier& identifier) { | |
287 // NULL after ContextDestroyed() called, and in some unit tests. | |
288 if (!context_) | |
289 return; | |
290 context_->DatabaseDeleted(identifier.first); | |
291 } | |
292 | |
293 void IndexedDBFactory::HandleBackingStoreFailure(const GURL& origin_url) { | |
294 // NULL after ContextDestroyed() called, and in some unit tests. | |
295 if (!context_) | |
296 return; | |
297 context_->ForceClose(origin_url, | |
298 IndexedDBContextImpl::FORCE_CLOSE_BACKING_STORE_FAILURE); | |
299 } | |
300 | |
301 void IndexedDBFactory::HandleBackingStoreCorruption( | |
302 const GURL& origin_url, | |
303 const IndexedDBDatabaseError& error) { | |
304 // Make a copy of origin_url as this is likely a reference to a member of a | |
305 // backing store which this function will be deleting. | |
306 GURL saved_origin_url(origin_url); | |
307 DCHECK(context_); | |
308 base::FilePath path_base = context_->data_path(); | |
309 IndexedDBBackingStore::RecordCorruptionInfo( | |
310 path_base, saved_origin_url, base::UTF16ToUTF8(error.message())); | |
311 HandleBackingStoreFailure(saved_origin_url); | |
312 // Note: DestroyBackingStore only deletes LevelDB files, leaving all others, | |
313 // so our corruption info file will remain. | |
314 leveldb::Status s = | |
315 IndexedDBBackingStore::DestroyBackingStore(path_base, saved_origin_url); | |
316 if (!s.ok()) | |
317 DLOG(ERROR) << "Unable to delete backing store: " << s.ToString(); | |
318 } | |
319 | |
320 bool IndexedDBFactory::IsDatabaseOpen(const GURL& origin_url, | |
321 const base::string16& name) const { | |
322 return !!database_map_.count(IndexedDBDatabase::Identifier(origin_url, name)); | |
323 } | |
324 | |
325 bool IndexedDBFactory::IsBackingStoreOpen(const GURL& origin_url) const { | |
326 return backing_store_map_.find(origin_url) != backing_store_map_.end(); | |
327 } | |
328 | |
329 bool IndexedDBFactory::IsBackingStorePendingClose(const GURL& origin_url) | |
330 const { | |
331 IndexedDBBackingStoreMap::const_iterator it = | |
332 backing_store_map_.find(origin_url); | |
333 if (it == backing_store_map_.end()) | |
334 return false; | |
335 return it->second->close_timer()->IsRunning(); | |
336 } | |
337 | |
338 scoped_refptr<IndexedDBBackingStore> IndexedDBFactory::OpenBackingStoreHelper( | |
339 const GURL& origin_url, | |
340 const base::FilePath& data_directory, | |
341 net::URLRequestContext* request_context, | |
342 blink::WebIDBDataLoss* data_loss, | |
343 std::string* data_loss_message, | |
344 bool* disk_full, | |
345 bool first_time, | |
346 leveldb::Status* status) { | |
347 return IndexedDBBackingStore::Open(this, | |
348 origin_url, | |
349 data_directory, | |
350 request_context, | |
351 data_loss, | |
352 data_loss_message, | |
353 disk_full, | |
354 context_->TaskRunner(), | |
355 first_time, | |
356 status); | |
357 } | |
358 | |
359 scoped_refptr<IndexedDBBackingStore> IndexedDBFactory::OpenBackingStore( | |
360 const GURL& origin_url, | |
361 const base::FilePath& data_directory, | |
362 net::URLRequestContext* request_context, | |
363 blink::WebIDBDataLoss* data_loss, | |
364 std::string* data_loss_message, | |
365 bool* disk_full, | |
366 leveldb::Status* status) { | |
367 const bool open_in_memory = data_directory.empty(); | |
368 | |
369 IndexedDBBackingStoreMap::iterator it2 = backing_store_map_.find(origin_url); | |
370 if (it2 != backing_store_map_.end()) { | |
371 it2->second->close_timer()->Stop(); | |
372 return it2->second; | |
373 } | |
374 | |
375 scoped_refptr<IndexedDBBackingStore> backing_store; | |
376 bool first_time = false; | |
377 if (open_in_memory) { | |
378 backing_store = IndexedDBBackingStore::OpenInMemory( | |
379 origin_url, context_->TaskRunner(), status); | |
380 } else { | |
381 first_time = !backends_opened_since_boot_.count(origin_url); | |
382 | |
383 backing_store = OpenBackingStoreHelper(origin_url, | |
384 data_directory, | |
385 request_context, | |
386 data_loss, | |
387 data_loss_message, | |
388 disk_full, | |
389 first_time, | |
390 status); | |
391 } | |
392 | |
393 if (backing_store.get()) { | |
394 if (first_time) | |
395 backends_opened_since_boot_.insert(origin_url); | |
396 backing_store_map_[origin_url] = backing_store; | |
397 // If an in-memory database, bind lifetime to this factory instance. | |
398 if (open_in_memory) | |
399 session_only_backing_stores_.insert(backing_store); | |
400 | |
401 // All backing stores associated with this factory should be of the same | |
402 // type. | |
403 DCHECK_NE(session_only_backing_stores_.empty(), open_in_memory); | |
404 | |
405 return backing_store; | |
406 } | |
407 | |
408 return 0; | |
409 } | |
410 | |
411 void IndexedDBFactory::Open(const base::string16& name, | |
412 const IndexedDBPendingConnection& connection, | |
413 net::URLRequestContext* request_context, | |
414 const GURL& origin_url, | |
415 const base::FilePath& data_directory) { | |
416 IDB_TRACE("IndexedDBFactory::Open"); | |
417 scoped_refptr<IndexedDBDatabase> database; | |
418 IndexedDBDatabase::Identifier unique_identifier(origin_url, name); | |
419 IndexedDBDatabaseMap::iterator it = database_map_.find(unique_identifier); | |
420 blink::WebIDBDataLoss data_loss = | |
421 blink::WebIDBDataLossNone; | |
422 std::string data_loss_message; | |
423 bool disk_full = false; | |
424 bool was_open = (it != database_map_.end()); | |
425 if (!was_open) { | |
426 leveldb::Status s; | |
427 scoped_refptr<IndexedDBBackingStore> backing_store = | |
428 OpenBackingStore(origin_url, | |
429 data_directory, | |
430 request_context, | |
431 &data_loss, | |
432 &data_loss_message, | |
433 &disk_full, | |
434 &s); | |
435 if (!backing_store) { | |
436 if (disk_full) { | |
437 connection.callbacks->OnError( | |
438 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError, | |
439 ASCIIToUTF16( | |
440 "Encountered full disk while opening " | |
441 "backing store for indexedDB.open."))); | |
442 return; | |
443 } | |
444 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, | |
445 ASCIIToUTF16( | |
446 "Internal error opening backing store" | |
447 " for indexedDB.open.")); | |
448 connection.callbacks->OnError(error); | |
449 if (s.IsCorruption()) { | |
450 HandleBackingStoreCorruption(origin_url, error); | |
451 } | |
452 return; | |
453 } | |
454 | |
455 database = IndexedDBDatabase::Create( | |
456 name, backing_store, this, unique_identifier, &s); | |
457 if (!database) { | |
458 DLOG(ERROR) << "Unable to create the database"; | |
459 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, | |
460 ASCIIToUTF16( | |
461 "Internal error creating " | |
462 "database backend for " | |
463 "indexedDB.open.")); | |
464 connection.callbacks->OnError(error); | |
465 if (leveldb_env::IsCorruption(s)) { | |
466 backing_store = NULL; // Closes the LevelDB so that it can be deleted | |
467 HandleBackingStoreCorruption(origin_url, error); | |
468 } | |
469 return; | |
470 } | |
471 } else { | |
472 database = it->second; | |
473 } | |
474 | |
475 if (data_loss != blink::WebIDBDataLossNone) | |
476 connection.callbacks->OnDataLoss(data_loss, data_loss_message); | |
477 | |
478 database->OpenConnection(connection); | |
479 | |
480 if (!was_open && database->ConnectionCount() > 0) { | |
481 database_map_[unique_identifier] = database; | |
482 origin_dbs_.insert(std::make_pair(origin_url, database)); | |
483 } | |
484 } | |
485 | |
486 std::pair<IndexedDBFactory::OriginDBMapIterator, | |
487 IndexedDBFactory::OriginDBMapIterator> | |
488 IndexedDBFactory::GetOpenDatabasesForOrigin(const GURL& origin_url) const { | |
489 return origin_dbs_.equal_range(origin_url); | |
490 } | |
491 | |
492 size_t IndexedDBFactory::GetConnectionCount(const GURL& origin_url) const { | |
493 size_t count(0); | |
494 | |
495 std::pair<OriginDBMapIterator, OriginDBMapIterator> range = | |
496 GetOpenDatabasesForOrigin(origin_url); | |
497 for (OriginDBMapIterator it = range.first; it != range.second; ++it) | |
498 count += it->second->ConnectionCount(); | |
499 | |
500 return count; | |
501 } | |
502 | |
503 } // namespace content | |
OLD | NEW |