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 "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_database_error.h" | |
13 #include "content/browser/indexed_db/indexed_db_tracing.h" | |
14 #include "content/browser/indexed_db/indexed_db_transaction_coordinator.h" | |
15 #include "third_party/WebKit/public/platform/WebIDBDatabaseException.h" | |
16 #include "third_party/leveldatabase/env_chromium.h" | |
17 #include "webkit/common/database/database_identifier.h" | |
18 | |
19 using base::ASCIIToUTF16; | |
20 | |
21 namespace content { | |
22 | |
23 const int64 kBackingStoreGracePeriodMs = 2000; | |
24 | |
25 IndexedDBFactory::IndexedDBFactory(IndexedDBContextImpl* context) | |
26 : context_(context) {} | |
27 | |
28 IndexedDBFactory::~IndexedDBFactory() {} | |
29 | |
30 void IndexedDBFactory::RemoveDatabaseFromMaps( | |
31 const IndexedDBDatabase::Identifier& identifier) { | |
32 IndexedDBDatabaseMap::iterator it = database_map_.find(identifier); | |
33 DCHECK(it != database_map_.end()); | |
34 IndexedDBDatabase* database = it->second; | |
35 database_map_.erase(it); | |
36 | |
37 std::pair<OriginDBMap::iterator, OriginDBMap::iterator> range = | |
38 origin_dbs_.equal_range(database->identifier().first); | |
39 DCHECK(range.first != range.second); | |
40 for (OriginDBMap::iterator it2 = range.first; it2 != range.second; ++it2) { | |
41 if (it2->second == database) { | |
42 origin_dbs_.erase(it2); | |
43 break; | |
44 } | |
45 } | |
46 } | |
47 | |
48 void IndexedDBFactory::ReleaseDatabase( | |
49 const IndexedDBDatabase::Identifier& identifier, | |
50 bool forcedClose) { | |
51 | |
52 DCHECK(!database_map_.find(identifier)->second->backing_store()); | |
53 | |
54 RemoveDatabaseFromMaps(identifier); | |
55 | |
56 // No grace period on a forced-close, as the initiator is | |
57 // assuming the backing store will be released once all | |
58 // connections are closed. | |
59 ReleaseBackingStore(identifier.first, forcedClose); | |
60 } | |
61 | |
62 void IndexedDBFactory::ReleaseBackingStore(const GURL& origin_url, | |
63 bool immediate) { | |
64 if (immediate) { | |
65 IndexedDBBackingStoreMap::iterator it = | |
66 backing_stores_with_active_blobs_.find(origin_url); | |
67 if (it != backing_stores_with_active_blobs_.end()) { | |
68 it->second->active_blob_registry()->ForceShutdown(); | |
69 backing_stores_with_active_blobs_.erase(it); | |
70 } | |
71 } | |
72 | |
73 // Only close if this is the last reference. | |
74 if (!HasLastBackingStoreReference(origin_url)) | |
75 return; | |
76 | |
77 // If this factory does hold the last reference to the backing store, it can | |
78 // be closed - but unless requested to close it immediately, keep it around | |
79 // for a short period so that a re-open is fast. | |
80 if (immediate) { | |
81 CloseBackingStore(origin_url); | |
82 return; | |
83 } | |
84 | |
85 // Start a timer to close the backing store, unless something else opens it | |
86 // in the mean time. | |
87 DCHECK(!backing_store_map_[origin_url]->close_timer()->IsRunning()); | |
88 backing_store_map_[origin_url]->close_timer()->Start( | |
89 FROM_HERE, | |
90 base::TimeDelta::FromMilliseconds(kBackingStoreGracePeriodMs), | |
91 base::Bind(&IndexedDBFactory::MaybeCloseBackingStore, this, origin_url)); | |
92 } | |
93 | |
94 void IndexedDBFactory::MaybeCloseBackingStore(const GURL& origin_url) { | |
95 // Another reference may have opened since the maybe-close was posted, so it | |
96 // is necessary to check again. | |
97 if (HasLastBackingStoreReference(origin_url)) | |
98 CloseBackingStore(origin_url); | |
99 } | |
100 | |
101 void IndexedDBFactory::CloseBackingStore(const GURL& origin_url) { | |
102 IndexedDBBackingStoreMap::iterator it = backing_store_map_.find(origin_url); | |
103 DCHECK(it != backing_store_map_.end()); | |
104 // Stop the timer (if it's running) - this may happen if the timer was started | |
105 // and then a forced close occurs. | |
106 it->second->close_timer()->Stop(); | |
107 backing_store_map_.erase(it); | |
108 } | |
109 | |
110 bool IndexedDBFactory::HasLastBackingStoreReference(const GURL& origin_url) | |
111 const { | |
112 IndexedDBBackingStore* ptr; | |
113 { | |
114 // Scope so that the implicit scoped_refptr<> is freed. | |
115 IndexedDBBackingStoreMap::const_iterator it = | |
116 backing_store_map_.find(origin_url); | |
117 DCHECK(it != backing_store_map_.end()); | |
118 ptr = it->second.get(); | |
119 } | |
120 return ptr->HasOneRef(); | |
121 } | |
122 | |
123 void IndexedDBFactory::ForceClose(const GURL& origin_url) { | |
124 std::pair<OriginDBMapIterator, OriginDBMapIterator> range = | |
125 GetOpenDatabasesForOrigin(origin_url); | |
126 | |
127 while (range.first != range.second) { | |
128 IndexedDBDatabase* db = range.first->second; | |
129 ++range.first; | |
130 db->ForceClose(); | |
131 } | |
132 | |
133 if (backing_store_map_.find(origin_url) != backing_store_map_.end()) | |
134 ReleaseBackingStore(origin_url, true /* immediate */); | |
135 } | |
136 | |
137 void IndexedDBFactory::ContextDestroyed() { | |
138 // Timers on backing stores hold a reference to this factory. When the | |
139 // context (which nominally owns this factory) is destroyed during thread | |
140 // termination the timers must be stopped so that this factory and the | |
141 // stores can be disposed of. | |
142 for (IndexedDBBackingStoreMap::iterator it = backing_store_map_.begin(); | |
143 it != backing_store_map_.end(); | |
144 ++it) | |
145 it->second->close_timer()->Stop(); | |
146 backing_store_map_.clear(); | |
147 backing_stores_with_active_blobs_.clear(); | |
148 context_ = NULL; | |
149 } | |
150 | |
151 void IndexedDBFactory::ReportOutstandingBlobs(const GURL& origin_url, | |
152 bool blobs_outstanding) { | |
153 if (!context_) | |
154 return; | |
155 if (blobs_outstanding) { | |
156 DCHECK(!backing_stores_with_active_blobs_.count(origin_url)); | |
157 IndexedDBBackingStoreMap::iterator it = backing_store_map_.find(origin_url); | |
158 if (it != backing_store_map_.end()) | |
159 backing_stores_with_active_blobs_.insert(*it); | |
160 else | |
161 DCHECK(false); | |
162 } else { | |
163 IndexedDBBackingStoreMap::iterator it = | |
164 backing_stores_with_active_blobs_.find(origin_url); | |
165 if (it != backing_stores_with_active_blobs_.end()) { | |
166 backing_stores_with_active_blobs_.erase(it); | |
167 ReleaseBackingStore(origin_url, false /* immediate */); | |
168 } | |
169 } | |
170 } | |
171 | |
172 void IndexedDBFactory::GetDatabaseNames( | |
173 scoped_refptr<IndexedDBCallbacks> callbacks, | |
174 const GURL& origin_url, | |
175 const base::FilePath& data_directory, | |
176 net::URLRequestContext* request_context) { | |
177 IDB_TRACE("IndexedDBFactory::GetDatabaseNames"); | |
178 // TODO(dgrogan): Plumb data_loss back to script eventually? | |
179 blink::WebIDBDataLoss data_loss; | |
180 std::string data_loss_message; | |
181 bool disk_full; | |
182 scoped_refptr<IndexedDBBackingStore> backing_store = | |
183 OpenBackingStore(origin_url, | |
184 data_directory, | |
185 request_context, | |
186 &data_loss, | |
187 &data_loss_message, | |
188 &disk_full); | |
189 if (!backing_store) { | |
190 callbacks->OnError( | |
191 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError, | |
192 "Internal error opening backing store for " | |
193 "indexedDB.webkitGetDatabaseNames.")); | |
194 return; | |
195 } | |
196 | |
197 leveldb::Status s; | |
198 std::vector<base::string16> names = backing_store->GetDatabaseNames(&s); | |
199 if (!s.ok()) { | |
200 // TODO(cmumford): Handle this error | |
201 DLOG(ERROR) << "Internal error getting database names"; | |
202 } | |
203 callbacks->OnSuccess(names); | |
204 backing_store = NULL; | |
205 ReleaseBackingStore(origin_url, false /* immediate */); | |
206 } | |
207 | |
208 void IndexedDBFactory::DeleteDatabase( | |
209 const base::string16& name, | |
210 net::URLRequestContext* request_context, | |
211 scoped_refptr<IndexedDBCallbacks> callbacks, | |
212 const GURL& origin_url, | |
213 const base::FilePath& data_directory) { | |
214 IDB_TRACE("IndexedDBFactory::DeleteDatabase"); | |
215 IndexedDBDatabase::Identifier unique_identifier(origin_url, name); | |
216 IndexedDBDatabaseMap::iterator it = database_map_.find(unique_identifier); | |
217 if (it != database_map_.end()) { | |
218 // If there are any connections to the database, directly delete the | |
219 // database. | |
220 it->second->DeleteDatabase(callbacks); | |
221 return; | |
222 } | |
223 | |
224 // TODO(dgrogan): Plumb data_loss back to script eventually? | |
225 blink::WebIDBDataLoss data_loss; | |
226 std::string data_loss_message; | |
227 bool disk_full = false; | |
228 scoped_refptr<IndexedDBBackingStore> backing_store = | |
229 OpenBackingStore(origin_url, | |
230 data_directory, | |
231 request_context, | |
232 &data_loss, | |
233 &data_loss_message, | |
234 &disk_full); | |
235 if (!backing_store) { | |
236 callbacks->OnError( | |
237 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError, | |
238 ASCIIToUTF16( | |
239 "Internal error opening backing store " | |
240 "for indexedDB.deleteDatabase."))); | |
241 return; | |
242 } | |
243 | |
244 leveldb::Status s; | |
245 scoped_refptr<IndexedDBDatabase> database = IndexedDBDatabase::Create( | |
246 name, backing_store, this, unique_identifier, &s); | |
247 if (!database) { | |
248 IndexedDBDatabaseError error( | |
249 blink::WebIDBDatabaseExceptionUnknownError, | |
250 ASCIIToUTF16( | |
251 "Internal error creating database backend for " | |
252 "indexedDB.deleteDatabase.")); | |
253 callbacks->OnError(error); | |
254 if (leveldb_env::IsCorruption(s)) | |
255 HandleBackingStoreCorruption(origin_url, error); | |
256 return; | |
257 } | |
258 | |
259 database_map_[unique_identifier] = database; | |
260 origin_dbs_.insert(std::make_pair(origin_url, database)); | |
261 database->DeleteDatabase(callbacks); | |
262 RemoveDatabaseFromMaps(unique_identifier); | |
263 database = NULL; | |
264 backing_store = NULL; | |
265 ReleaseBackingStore(origin_url, false /* immediate */); | |
266 } | |
267 | |
268 void IndexedDBFactory::DatabaseDeleted( | |
269 const IndexedDBDatabase::Identifier& identifier) { | |
270 // NULL after ContextDestroyed() called, and in some unit tests. | |
271 if (!context_) | |
272 return; | |
273 context_->DatabaseDeleted(identifier.first); | |
274 } | |
275 | |
276 void IndexedDBFactory::HandleBackingStoreFailure(const GURL& origin_url) { | |
277 // NULL after ContextDestroyed() called, and in some unit tests. | |
278 if (!context_) | |
279 return; | |
280 context_->ForceClose(origin_url, | |
281 IndexedDBContextImpl::FORCE_CLOSE_BACKING_STORE_FAILURE); | |
282 } | |
283 | |
284 void IndexedDBFactory::HandleBackingStoreCorruption( | |
285 const GURL& origin_url, | |
286 const IndexedDBDatabaseError& error) { | |
287 // Make a copy of origin_url as this is likely a reference to a member of a | |
288 // backing store which this function will be deleting. | |
289 GURL saved_origin_url(origin_url); | |
290 DCHECK(context_); | |
291 base::FilePath path_base = context_->data_path(); | |
292 IndexedDBBackingStore::RecordCorruptionInfo( | |
293 path_base, saved_origin_url, base::UTF16ToUTF8(error.message())); | |
294 HandleBackingStoreFailure(saved_origin_url); | |
295 // Note: DestroyBackingStore only deletes LevelDB files, leaving all others, | |
296 // so our corruption info file will remain. | |
297 leveldb::Status s = | |
298 IndexedDBBackingStore::DestroyBackingStore(path_base, saved_origin_url); | |
299 if (!s.ok()) | |
300 DLOG(ERROR) << "Unable to delete backing store: " << s.ToString(); | |
301 } | |
302 | |
303 bool IndexedDBFactory::IsDatabaseOpen(const GURL& origin_url, | |
304 const base::string16& name) const { | |
305 | |
306 return !!database_map_.count(IndexedDBDatabase::Identifier(origin_url, name)); | |
307 } | |
308 | |
309 bool IndexedDBFactory::IsBackingStoreOpen(const GURL& origin_url) const { | |
310 return backing_store_map_.find(origin_url) != backing_store_map_.end(); | |
311 } | |
312 | |
313 bool IndexedDBFactory::IsBackingStorePendingClose(const GURL& origin_url) | |
314 const { | |
315 IndexedDBBackingStoreMap::const_iterator it = | |
316 backing_store_map_.find(origin_url); | |
317 if (it == backing_store_map_.end()) | |
318 return false; | |
319 return it->second->close_timer()->IsRunning(); | |
320 } | |
321 | |
322 scoped_refptr<IndexedDBBackingStore> IndexedDBFactory::OpenBackingStoreHelper( | |
323 const GURL& origin_url, | |
324 const base::FilePath& data_directory, | |
325 net::URLRequestContext* request_context, | |
326 blink::WebIDBDataLoss* data_loss, | |
327 std::string* data_loss_message, | |
328 bool* disk_full, | |
329 bool first_time) { | |
330 return IndexedDBBackingStore::Open(this, | |
331 origin_url, | |
332 data_directory, | |
333 request_context, | |
334 data_loss, | |
335 data_loss_message, | |
336 disk_full, | |
337 context_->TaskRunner(), | |
338 first_time); | |
339 } | |
340 | |
341 scoped_refptr<IndexedDBBackingStore> IndexedDBFactory::OpenBackingStore( | |
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 const bool open_in_memory = data_directory.empty(); | |
349 | |
350 IndexedDBBackingStoreMap::iterator it2 = backing_store_map_.find(origin_url); | |
351 if (it2 != backing_store_map_.end()) { | |
352 it2->second->close_timer()->Stop(); | |
353 return it2->second; | |
354 } | |
355 | |
356 scoped_refptr<IndexedDBBackingStore> backing_store; | |
357 bool first_time = false; | |
358 if (open_in_memory) { | |
359 backing_store = | |
360 IndexedDBBackingStore::OpenInMemory(origin_url, context_->TaskRunner()); | |
361 } else { | |
362 first_time = !backends_opened_since_boot_.count(origin_url); | |
363 | |
364 backing_store = OpenBackingStoreHelper(origin_url, | |
365 data_directory, | |
366 request_context, | |
367 data_loss, | |
368 data_loss_message, | |
369 disk_full, | |
370 first_time); | |
371 } | |
372 | |
373 if (backing_store.get()) { | |
374 if (first_time) | |
375 backends_opened_since_boot_.insert(origin_url); | |
376 backing_store_map_[origin_url] = backing_store; | |
377 // If an in-memory database, bind lifetime to this factory instance. | |
378 if (open_in_memory) | |
379 session_only_backing_stores_.insert(backing_store); | |
380 | |
381 // All backing stores associated with this factory should be of the same | |
382 // type. | |
383 DCHECK_NE(session_only_backing_stores_.empty(), open_in_memory); | |
384 | |
385 return backing_store; | |
386 } | |
387 | |
388 return 0; | |
389 } | |
390 | |
391 void IndexedDBFactory::Open(const base::string16& name, | |
392 const IndexedDBPendingConnection& connection, | |
393 net::URLRequestContext* request_context, | |
394 const GURL& origin_url, | |
395 const base::FilePath& data_directory) { | |
396 IDB_TRACE("IndexedDBFactory::Open"); | |
397 scoped_refptr<IndexedDBDatabase> database; | |
398 IndexedDBDatabase::Identifier unique_identifier(origin_url, name); | |
399 IndexedDBDatabaseMap::iterator it = database_map_.find(unique_identifier); | |
400 blink::WebIDBDataLoss data_loss = | |
401 blink::WebIDBDataLossNone; | |
402 std::string data_loss_message; | |
403 bool disk_full = false; | |
404 bool was_open = (it != database_map_.end()); | |
405 if (!was_open) { | |
406 scoped_refptr<IndexedDBBackingStore> backing_store = | |
407 OpenBackingStore(origin_url, | |
408 data_directory, | |
409 request_context, | |
410 &data_loss, | |
411 &data_loss_message, | |
412 &disk_full); | |
413 if (!backing_store) { | |
414 if (disk_full) { | |
415 connection.callbacks->OnError( | |
416 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionQuotaError, | |
417 ASCIIToUTF16( | |
418 "Encountered full disk while opening " | |
419 "backing store for indexedDB.open."))); | |
420 return; | |
421 } | |
422 connection.callbacks->OnError(IndexedDBDatabaseError( | |
423 blink::WebIDBDatabaseExceptionUnknownError, | |
424 ASCIIToUTF16( | |
425 "Internal error opening backing store for indexedDB.open."))); | |
426 return; | |
427 } | |
428 | |
429 leveldb::Status s; | |
430 database = IndexedDBDatabase::Create( | |
431 name, backing_store, this, unique_identifier, &s); | |
432 if (!database) { | |
433 DLOG(ERROR) << "Unable to create the database"; | |
434 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, | |
435 ASCIIToUTF16( | |
436 "Internal error creating " | |
437 "database backend for " | |
438 "indexedDB.open.")); | |
439 connection.callbacks->OnError(error); | |
440 if (leveldb_env::IsCorruption(s)) { | |
441 backing_store = NULL; // Closes the LevelDB so that it can be deleted | |
442 HandleBackingStoreCorruption(origin_url, error); | |
443 } | |
444 return; | |
445 } | |
446 } else { | |
447 database = it->second; | |
448 } | |
449 | |
450 if (data_loss != blink::WebIDBDataLossNone) | |
451 connection.callbacks->OnDataLoss(data_loss, data_loss_message); | |
452 | |
453 database->OpenConnection(connection); | |
454 | |
455 if (!was_open && database->ConnectionCount() > 0) { | |
456 database_map_[unique_identifier] = database; | |
457 origin_dbs_.insert(std::make_pair(origin_url, database)); | |
458 } | |
459 } | |
460 | |
461 std::pair<IndexedDBFactory::OriginDBMapIterator, | |
462 IndexedDBFactory::OriginDBMapIterator> | |
463 IndexedDBFactory::GetOpenDatabasesForOrigin(const GURL& origin_url) const { | |
464 return origin_dbs_.equal_range(origin_url); | |
465 } | |
466 | |
467 size_t IndexedDBFactory::GetConnectionCount(const GURL& origin_url) const { | |
468 size_t count(0); | |
469 | |
470 std::pair<OriginDBMapIterator, OriginDBMapIterator> range = | |
471 GetOpenDatabasesForOrigin(origin_url); | |
472 for (OriginDBMapIterator it = range.first; it != range.second; ++it) | |
473 count += it->second->ConnectionCount(); | |
474 | |
475 return count; | |
476 } | |
477 | |
478 } // namespace content | |
OLD | NEW |