Respond with QuotaExceededError when IndexedDB has no disk space on open.
[chromium-blink-merge.git] / content / browser / indexed_db / indexed_db_factory.cc
blob4e411119bde9487bd96f675bddba0d3f5e8b3e39
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.
5 #include "content/browser/indexed_db/indexed_db_factory.h"
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_tracing.h"
12 #include "content/browser/indexed_db/indexed_db_transaction_coordinator.h"
13 #include "third_party/WebKit/public/platform/WebIDBDatabaseException.h"
15 namespace content {
17 const int64 kBackingStoreGracePeriodMs = 2000;
19 static std::string ComputeFileIdentifier(const std::string& origin_identifier) {
20 return origin_identifier + "@1";
23 IndexedDBFactory::IndexedDBFactory() {}
25 IndexedDBFactory::~IndexedDBFactory() {}
27 void IndexedDBFactory::ReleaseDatabase(
28 const IndexedDBDatabase::Identifier& identifier,
29 bool forcedClose) {
30 DCHECK(database_map_.find(identifier) != database_map_.end());
31 std::string backing_store_identifier =
32 database_map_[identifier]->backing_store()->identifier();
33 database_map_.erase(identifier);
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(backing_store_identifier, forcedClose);
41 void IndexedDBFactory::ReleaseBackingStore(const std::string& identifier,
42 bool immediate) {
43 // Only close if this is the last reference.
44 if (!HasLastBackingStoreReference(identifier))
45 return;
47 if (immediate) {
48 CloseBackingStore(identifier);
49 return;
52 DCHECK(!backing_store_map_[identifier]->close_timer()->IsRunning());
53 backing_store_map_[identifier]->close_timer()->Start(
54 FROM_HERE,
55 base::TimeDelta::FromMilliseconds(kBackingStoreGracePeriodMs),
56 base::Bind(&IndexedDBFactory::MaybeCloseBackingStore, this, identifier));
59 void IndexedDBFactory::MaybeCloseBackingStore(const std::string& identifier) {
60 // Another reference may have opened since the maybe-close was posted,
61 // so it is necessary to check again.
62 if (HasLastBackingStoreReference(identifier))
63 CloseBackingStore(identifier);
66 void IndexedDBFactory::CloseBackingStore(const std::string& identifier) {
67 backing_store_map_.erase(identifier);
70 bool IndexedDBFactory::HasLastBackingStoreReference(
71 const std::string& identifier) const {
72 IndexedDBBackingStore* ptr;
74 // Scope so that the implicit scoped_refptr<> is freed.
75 IndexedDBBackingStoreMap::const_iterator it =
76 backing_store_map_.find(identifier);
77 DCHECK(it != backing_store_map_.end());
78 ptr = it->second.get();
80 return ptr->HasOneRef();
83 void IndexedDBFactory::GetDatabaseNames(
84 scoped_refptr<IndexedDBCallbacks> callbacks,
85 const std::string& origin_identifier,
86 const base::FilePath& data_directory) {
87 IDB_TRACE("IndexedDBFactory::GetDatabaseNames");
88 // TODO(dgrogan): Plumb data_loss back to script eventually?
89 WebKit::WebIDBCallbacks::DataLoss data_loss;
90 bool disk_full;
91 scoped_refptr<IndexedDBBackingStore> backing_store = OpenBackingStore(
92 origin_identifier, data_directory, &data_loss, &disk_full);
93 if (!backing_store) {
94 callbacks->OnError(
95 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
96 "Internal error opening backing store for "
97 "indexedDB.webkitGetDatabaseNames."));
98 return;
101 callbacks->OnSuccess(backing_store->GetDatabaseNames());
104 void IndexedDBFactory::DeleteDatabase(
105 const string16& name,
106 scoped_refptr<IndexedDBCallbacks> callbacks,
107 const std::string& origin_identifier,
108 const base::FilePath& data_directory) {
109 IDB_TRACE("IndexedDBFactory::DeleteDatabase");
110 IndexedDBDatabase::Identifier unique_identifier(origin_identifier, name);
111 IndexedDBDatabaseMap::iterator it = database_map_.find(unique_identifier);
112 if (it != database_map_.end()) {
113 // If there are any connections to the database, directly delete the
114 // database.
115 it->second->DeleteDatabase(callbacks);
116 return;
119 // TODO(dgrogan): Plumb data_loss back to script eventually?
120 WebKit::WebIDBCallbacks::DataLoss data_loss;
121 bool disk_full = false;
122 scoped_refptr<IndexedDBBackingStore> backing_store = OpenBackingStore(
123 origin_identifier, data_directory, &data_loss, &disk_full);
124 if (!backing_store) {
125 callbacks->OnError(
126 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionUnknownError,
127 ASCIIToUTF16(
128 "Internal error opening backing store "
129 "for indexedDB.deleteDatabase.")));
130 return;
133 scoped_refptr<IndexedDBDatabase> database =
134 IndexedDBDatabase::Create(name, backing_store, this, unique_identifier);
135 if (!database) {
136 callbacks->OnError(IndexedDBDatabaseError(
137 WebKit::WebIDBDatabaseExceptionUnknownError,
138 ASCIIToUTF16(
139 "Internal error creating database backend for "
140 "indexedDB.deleteDatabase.")));
141 return;
144 database_map_[unique_identifier] = database;
145 database->DeleteDatabase(callbacks);
146 database_map_.erase(unique_identifier);
149 scoped_refptr<IndexedDBBackingStore> IndexedDBFactory::OpenBackingStore(
150 const std::string& origin_identifier,
151 const base::FilePath& data_directory,
152 WebKit::WebIDBCallbacks::DataLoss* data_loss,
153 bool* disk_full) {
154 const std::string file_identifier = ComputeFileIdentifier(origin_identifier);
155 const bool open_in_memory = data_directory.empty();
157 IndexedDBBackingStoreMap::iterator it2 =
158 backing_store_map_.find(file_identifier);
159 if (it2 != backing_store_map_.end()) {
160 it2->second->close_timer()->Stop();
161 return it2->second;
164 scoped_refptr<IndexedDBBackingStore> backing_store;
165 if (open_in_memory) {
166 backing_store = IndexedDBBackingStore::OpenInMemory(file_identifier);
167 } else {
168 backing_store = IndexedDBBackingStore::Open(origin_identifier,
169 data_directory,
170 file_identifier,
171 data_loss,
172 disk_full);
175 if (backing_store.get()) {
176 backing_store_map_[file_identifier] = backing_store;
177 // If an in-memory database, bind lifetime to this factory instance.
178 if (open_in_memory)
179 session_only_backing_stores_.insert(backing_store);
181 // All backing stores associated with this factory should be of the same
182 // type.
183 DCHECK(session_only_backing_stores_.empty() || open_in_memory);
185 return backing_store;
188 return 0;
191 void IndexedDBFactory::Open(
192 const string16& name,
193 int64 version,
194 int64 transaction_id,
195 scoped_refptr<IndexedDBCallbacks> callbacks,
196 scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
197 const std::string& origin_identifier,
198 const base::FilePath& data_directory) {
199 IDB_TRACE("IndexedDBFactory::Open");
200 scoped_refptr<IndexedDBDatabase> database;
201 IndexedDBDatabase::Identifier unique_identifier(origin_identifier, name);
202 IndexedDBDatabaseMap::iterator it = database_map_.find(unique_identifier);
203 WebKit::WebIDBCallbacks::DataLoss data_loss =
204 WebKit::WebIDBCallbacks::DataLossNone;
205 bool disk_full = false;
206 if (it == database_map_.end()) {
207 scoped_refptr<IndexedDBBackingStore> backing_store = OpenBackingStore(
208 origin_identifier, data_directory, &data_loss, &disk_full);
209 if (!backing_store) {
210 if (disk_full) {
211 callbacks->OnError(
212 IndexedDBDatabaseError(WebKit::WebIDBDatabaseExceptionQuotaError,
213 ASCIIToUTF16(
214 "Encountered full disk while opening "
215 "backing store for indexedDB.open.")));
216 return;
218 callbacks->OnError(IndexedDBDatabaseError(
219 WebKit::WebIDBDatabaseExceptionUnknownError,
220 ASCIIToUTF16(
221 "Internal error opening backing store for indexedDB.open.")));
222 return;
225 database =
226 IndexedDBDatabase::Create(name, backing_store, this, unique_identifier);
227 if (!database) {
228 callbacks->OnError(IndexedDBDatabaseError(
229 WebKit::WebIDBDatabaseExceptionUnknownError,
230 ASCIIToUTF16(
231 "Internal error creating database backend for indexedDB.open.")));
232 return;
235 database_map_[unique_identifier] = database;
236 } else {
237 database = it->second;
240 database->OpenConnection(
241 callbacks, database_callbacks, transaction_id, version, data_loss);
244 std::vector<IndexedDBDatabase*> IndexedDBFactory::GetOpenDatabasesForOrigin(
245 const std::string& origin_identifier) const {
246 std::vector<IndexedDBDatabase*> result;
247 for (IndexedDBDatabaseMap::const_iterator it = database_map_.begin();
248 it != database_map_.end();
249 ++it) {
250 if (it->first.first == origin_identifier)
251 result.push_back(it->second.get());
253 return result;
256 } // namespace content