Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(650)

Side by Side Diff: content/browser/indexed_db/indexed_db_factory.cc

Issue 313883003: Split IndexedDBFactory into virtual base + impl. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Added protected IndexedDBFactory constructor prototype. Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 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
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
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
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
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
305 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
310 bool IndexedDBFactory::IsBackingStoreOpen(const GURL& origin_url) const {
311 return backing_store_map_.find(origin_url) != backing_store_map_.end();
312 }
313
314 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
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
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
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>
464 IndexedDBFactory::GetOpenDatabasesForOrigin(const GURL& origin_url) const {
465 return origin_dbs_.equal_range(origin_url);
466 }
467
468 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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698