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

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

Issue 2148253003: IndexedDB: Implement explicit queue for IDBOpenDBRequests (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Base class needs virtual dtor Created 4 years, 5 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
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/browser/indexed_db/indexed_db_database.h" 5 #include "content/browser/indexed_db/indexed_db_database.h"
6 6
7 #include <math.h> 7 #include <math.h>
8 8
9 #include <limits> 9 #include <limits>
10 #include <memory> 10 #include <memory>
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
70 return KEY_PATH_TYPE_STRING; 70 return KEY_PATH_TYPE_STRING;
71 case blink::WebIDBKeyPathTypeArray: 71 case blink::WebIDBKeyPathTypeArray:
72 return KEY_PATH_TYPE_ARRAY; 72 return KEY_PATH_TYPE_ARRAY;
73 } 73 }
74 NOTREACHED(); 74 NOTREACHED();
75 return KEY_PATH_TYPE_NONE; 75 return KEY_PATH_TYPE_NONE;
76 } 76 }
77 77
78 } // namespace 78 } // namespace
79 79
80 // PendingUpgradeCall has a std::unique_ptr<IndexedDBConnection> because it owns 80 // This represents what script calls an 'IDBOpenDBRequest' - either a database
81 // the 81 // open or delete call. These may be blocked on other connections. After every
82 // in-progress connection. 82 // callback, the request must call IndexedDBDatabase::RequestComplete() or be
83 class IndexedDBDatabase::PendingUpgradeCall { 83 // expecting a further callback.
84 class IndexedDBDatabase::OpenOrDeleteRequest {
84 public: 85 public:
85 PendingUpgradeCall(scoped_refptr<IndexedDBCallbacks> callbacks, 86 explicit OpenOrDeleteRequest(scoped_refptr<IndexedDBDatabase> db) : db_(db) {}
86 std::unique_ptr<IndexedDBConnection> connection, 87
87 int64_t transaction_id, 88 virtual ~OpenOrDeleteRequest() {}
88 int64_t version) 89
89 : callbacks_(callbacks), 90 // Called when the request makes it to the front of the queue.
90 connection_(std::move(connection)), 91 virtual void Perform() = 0;
91 version_(version), 92
92 transaction_id_(transaction_id) {} 93 // Called if a front-end signals that it is ignoring a "versionchange"
93 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; } 94 // event. This should result in firing a "blocked" event at the request.
94 // Takes ownership of the connection object. 95 virtual void OnVersionChangeIgnored() const = 0;
95 std::unique_ptr<IndexedDBConnection> ReleaseConnection() WARN_UNUSED_RESULT { 96
96 return std::move(connection_); 97 // Called when a connection is closed; if it corresponds to this connection,
97 } 98 // need to do cleanup. Otherwise, it may unblock further steps.
98 int64_t version() const { return version_; } 99 virtual void OnConnectionClosed(IndexedDBConnection* connection) = 0;
99 int64_t transaction_id() const { return transaction_id_; } 100
101 // Called when the upgrade transaction has started executing.
102 virtual void UpgradeTransactionStarted(int64_t old_version) = 0;
103
104 // Called when the upgrade transaction has finished.
105 virtual void UpgradeTransactionFinished(bool committed) = 0;
106
107 protected:
108 scoped_refptr<IndexedDBDatabase> db_;
109
110 private:
111 DISALLOW_COPY_AND_ASSIGN(OpenOrDeleteRequest);
112 };
113
114 class IndexedDBDatabase::OpenRequest
115 : public IndexedDBDatabase::OpenOrDeleteRequest {
116 public:
117 OpenRequest(scoped_refptr<IndexedDBDatabase> db,
118 const IndexedDBPendingConnection& pending_connection)
119 : OpenOrDeleteRequest(db),
120 child_process_id_(pending_connection.child_process_id),
121 transaction_id_(pending_connection.transaction_id),
122 version_(pending_connection.version),
123 callbacks_(pending_connection.callbacks),
124 database_callbacks_(pending_connection.database_callbacks) {}
125
126 void Perform() override {
127 if (db_->metadata_.id == kInvalidId) {
128 // The database was deleted then immediately re-opened; OpenInternal()
129 // recreates it in the backing store.
130 if (!db_->OpenInternal().ok()) {
cmumford 2016/07/15 14:55:36 We're throwing away the message in leveldb::Status
jsbell 2016/07/15 17:22:22 Behavior change, making that a TODO.
131 base::string16 message;
132 if (version_ == IndexedDBDatabaseMetadata::NO_VERSION) {
133 message = ASCIIToUTF16(
134 "Internal error opening database with no version specified.");
135 } else {
136 message =
137 ASCIIToUTF16("Internal error opening database with version ") +
138 Int64ToString16(version_);
139 }
140 callbacks_->OnError(IndexedDBDatabaseError(
141 blink::WebIDBDatabaseExceptionUnknownError, message));
142 db_->RequestComplete(this);
143 return;
144 }
145
146 DCHECK_EQ(IndexedDBDatabaseMetadata::NO_VERSION, db_->metadata_.version);
147 }
148
149 const int64_t old_version = db_->metadata_.version;
150 int64_t& new_version = version_;
151
152 bool is_new_database = old_version == IndexedDBDatabaseMetadata::NO_VERSION;
153
154 if (new_version == IndexedDBDatabaseMetadata::DEFAULT_VERSION) {
155 // For unit tests only - skip upgrade steps. (Calling from script with
156 // DEFAULT_VERSION throws exception.)
157 DCHECK(is_new_database);
158 callbacks_->OnSuccess(
159 db_->CreateConnection(database_callbacks_, child_process_id_),
160 db_->metadata_);
161 db_->RequestComplete(this);
162 return;
163 }
164
165 // Database already exists and either requested version is equal to
cmumford 2016/07/15 14:55:36 The comment is a restatement of the if (...) condi
jsbell 2016/07/15 17:22:22 Removed.
166 // current version or no version was specified: no upgrade needed.
167 if (!is_new_database &&
168 (new_version == old_version ||
169 new_version == IndexedDBDatabaseMetadata::NO_VERSION)) {
170 callbacks_->OnSuccess(
171 db_->CreateConnection(database_callbacks_, child_process_id_),
172 db_->metadata_);
173 db_->RequestComplete(this);
174 return;
175 }
176
177 if (new_version == IndexedDBDatabaseMetadata::NO_VERSION) {
178 // If no version is specified and no database exists, upgrade the
179 // database version to 1.
180 DCHECK(is_new_database);
181 new_version = 1;
182 } else if (new_version < old_version) {
183 // Requested version is lower than current version - fail the request.
184 DCHECK(!is_new_database);
185 callbacks_->OnError(IndexedDBDatabaseError(
186 blink::WebIDBDatabaseExceptionVersionError,
187 ASCIIToUTF16("The requested version (") + Int64ToString16(version_) +
188 ASCIIToUTF16(") is less than the existing version (") +
189 Int64ToString16(db_->metadata_.version) + ASCIIToUTF16(").")));
190 db_->RequestComplete(this);
191 return;
192 }
193
194 // Requested version is higher than current version - upgrade needed.
195 DCHECK_GT(new_version, old_version);
196
197 if (db_->connections_.empty()) {
198 StartUpgrade();
199 return;
200 }
201
202 // There are outstanding connections - fire "versionchange" events and
203 // wait for the connections to close. Front end ensures the event is not
204 // fired at connections that have close_pending set. A "blocked" event
205 // will be fired at the request when one of the connections acks that the
206 // "versionchange" event was ignored.
207 DCHECK_NE(callbacks_->data_loss_info().status, blink::WebIDBDataLossTotal);
208 for (const auto* connection : db_->connections_)
209 connection->callbacks()->OnVersionChange(old_version, new_version);
210
211 // When all connections have closed the upgrade can proceed.
212 }
213
214 void OnVersionChangeIgnored() const override {
215 callbacks_->OnBlocked(db_->metadata_.version);
216 }
217
218 void OnConnectionClosed(IndexedDBConnection* connection) override {
219 // This connection closed prematurely; signal an error and complete.
220 if (connection && connection->callbacks() == database_callbacks_) {
221 callbacks_->OnError(
222 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError,
223 "The connection was closed."));
224 db_->RequestComplete(this);
225 return;
226 }
227
228 if (!db_->connections_.empty())
229 return;
230
231 StartUpgrade();
232 }
233
234 // Initiate the upgrade. The bulk of the work actually happens in
235 // IndexedDBDatabase::VersionChangeOperation in order to kick the
236 // transaction into the correct state.
237 void StartUpgrade() {
238 connection_ = db_->CreateConnection(database_callbacks_, child_process_id_);
239 DCHECK_EQ(db_->connections_.count(connection_.get()), 1UL);
240
241 std::vector<int64_t> object_store_ids;
242 IndexedDBTransaction* transaction = db_->CreateTransaction(
243 transaction_id_, connection_.get(), object_store_ids,
244 blink::WebIDBTransactionModeVersionChange);
245
246 DCHECK(db_->transaction_coordinator_.IsRunningVersionChangeTransaction());
247 transaction->ScheduleTask(base::Bind(
248 &IndexedDBDatabase::VersionChangeOperation, db_, version_, callbacks_));
249 }
250
251 // Called when the upgrade transaction has started executing.
252 void UpgradeTransactionStarted(int64_t old_version) override {
253 DCHECK(connection_);
254 callbacks_->OnUpgradeNeeded(old_version, std::move(connection_),
255 db_->metadata_);
256 }
257
258 void UpgradeTransactionFinished(bool committed) override {
259 // Ownership of connection was already pased along in OnUpgradeNeeded.
cmumford 2016/07/15 14:55:36 s/pased/passed/
jsbell 2016/07/15 17:22:22 Done.
260 DCHECK(!connection_);
261
262 if (committed) {
263 DCHECK_EQ(version_, db_->metadata_.version);
264 callbacks_->OnSuccess(std::unique_ptr<IndexedDBConnection>(),
265 db_->metadata());
266 } else {
267 DCHECK_NE(version_, db_->metadata_.version);
268 callbacks_->OnError(
269 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError,
270 "Version change transaction was aborted in "
271 "upgradeneeded event handler."));
272 }
273 db_->RequestComplete(this);
274 }
275
276 private:
277 const int child_process_id_;
cmumford 2016/07/15 14:55:36 This class is passed (in the constructor) a Indexe
jsbell 2016/07/15 17:22:22 Done.
278 const int64_t transaction_id_;
279 int64_t version_;
280 scoped_refptr<IndexedDBCallbacks> callbacks_;
281 scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks_;
282
283 // If an upgrade is needed, holds the pending connection until ownership is
284 // transferred to the IndexedDBDispatcherHost via OnUpgradeNeeded.
285 std::unique_ptr<IndexedDBConnection> connection_;
286
287 DISALLOW_COPY_AND_ASSIGN(OpenRequest);
288 };
289
290 class IndexedDBDatabase::DeleteRequest
291 : public IndexedDBDatabase::OpenOrDeleteRequest {
292 public:
293 DeleteRequest(scoped_refptr<IndexedDBDatabase> db,
294 scoped_refptr<IndexedDBCallbacks> callbacks)
295 : OpenOrDeleteRequest(db), callbacks_(callbacks) {}
296
297 void Perform() override {
298 if (db_->connections_.empty()) {
299 // No connections, so delete immediately.
300 DoDelete();
301 return;
302 }
303
304 // Front end ensures the event is not fired at connections that have
305 // close_pending set.
306 const int64_t old_version = db_->metadata_.version;
307 const int64_t new_version = IndexedDBDatabaseMetadata::NO_VERSION;
308 DCHECK_NE(callbacks_->data_loss_info().status, blink::WebIDBDataLossTotal);
309 for (const auto* connection : db_->connections_)
310 connection->callbacks()->OnVersionChange(old_version, new_version);
311 }
312
313 void OnVersionChangeIgnored() const override {
314 callbacks_->OnBlocked(db_->metadata_.version);
315 }
316
317 void OnConnectionClosed(IndexedDBConnection* connection) override {
318 if (!db_->connections_.empty())
319 return;
320 DoDelete();
321 }
322
323 void DoDelete() {
324 leveldb::Status s =
325 db_->backing_store_->DeleteDatabase(db_->metadata_.name);
326 if (!s.ok()) {
327 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
328 "Internal error deleting database.");
cmumford 2016/07/15 14:55:36 Can we sanitize and use message from |s|?
jsbell 2016/07/15 17:22:22 Added a TODO.
329 callbacks_->OnError(error);
330 if (s.IsCorruption()) {
331 url::Origin origin = db_->backing_store_->origin();
332 db_->backing_store_ = NULL;
cmumford 2016/07/15 14:55:36 nullptr
jsbell 2016/07/15 17:22:22 Done.
333 db_->factory_->HandleBackingStoreCorruption(origin, error);
334 }
335 db_->RequestComplete(this);
336 return;
337 }
338
339 int64_t old_version = db_->metadata_.version;
340 db_->metadata_.id = kInvalidId;
341 db_->metadata_.version = IndexedDBDatabaseMetadata::NO_VERSION;
342 db_->metadata_.object_stores.clear();
cmumford 2016/07/15 14:55:36 Reset max_object_store_id?
jsbell 2016/07/15 17:22:22 Interesting that this wasn't done before. Done!
343 callbacks_->OnSuccess(old_version);
344 db_->factory_->DatabaseDeleted(db_->identifier_);
345
346 db_->RequestComplete(this);
347 }
348
349 void UpgradeTransactionStarted(int64_t old_version) override { NOTREACHED(); }
350
351 void UpgradeTransactionFinished(bool committed) override { NOTREACHED(); }
100 352
101 private: 353 private:
102 scoped_refptr<IndexedDBCallbacks> callbacks_; 354 scoped_refptr<IndexedDBCallbacks> callbacks_;
103 std::unique_ptr<IndexedDBConnection> connection_; 355
104 int64_t version_; 356 DISALLOW_COPY_AND_ASSIGN(DeleteRequest);
105 const int64_t transaction_id_;
106 }; 357 };
107 358
108 // PendingSuccessCall has a IndexedDBConnection* because the connection is now
109 // owned elsewhere, but we need to cancel the success call if that connection
110 // closes before it is sent.
111 class IndexedDBDatabase::PendingSuccessCall {
112 public:
113 PendingSuccessCall(scoped_refptr<IndexedDBCallbacks> callbacks,
114 IndexedDBConnection* connection,
115 int64_t version)
116 : callbacks_(callbacks), connection_(connection), version_(version) {}
117 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
118 IndexedDBConnection* connection() const { return connection_; }
119 int64_t version() const { return version_; }
120
121 private:
122 scoped_refptr<IndexedDBCallbacks> callbacks_;
123 IndexedDBConnection* connection_;
124 int64_t version_;
125 };
126
127 class IndexedDBDatabase::PendingDeleteCall {
128 public:
129 explicit PendingDeleteCall(scoped_refptr<IndexedDBCallbacks> callbacks)
130 : callbacks_(callbacks) {}
131 scoped_refptr<IndexedDBCallbacks> callbacks() const { return callbacks_; }
132
133 private:
134 scoped_refptr<IndexedDBCallbacks> callbacks_;
135 };
136
137 scoped_refptr<IndexedDBDatabase> IndexedDBDatabase::Create( 359 scoped_refptr<IndexedDBDatabase> IndexedDBDatabase::Create(
138 const base::string16& name, 360 const base::string16& name,
139 IndexedDBBackingStore* backing_store, 361 IndexedDBBackingStore* backing_store,
140 IndexedDBFactory* factory, 362 IndexedDBFactory* factory,
141 const Identifier& unique_identifier, 363 const Identifier& unique_identifier,
142 leveldb::Status* s) { 364 leveldb::Status* s) {
143 scoped_refptr<IndexedDBDatabase> database = 365 scoped_refptr<IndexedDBDatabase> database =
144 IndexedDBClassFactory::Get()->CreateIndexedDBDatabase( 366 IndexedDBClassFactory::Get()->CreateIndexedDBDatabase(
145 name, backing_store, factory, unique_identifier); 367 name, backing_store, factory, unique_identifier);
146 *s = database->OpenInternal(); 368 *s = database->OpenInternal();
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
224 if (success) 446 if (success)
225 return backing_store_->GetObjectStores(metadata_.id, 447 return backing_store_->GetObjectStores(metadata_.id,
226 &metadata_.object_stores); 448 &metadata_.object_stores);
227 449
228 return backing_store_->CreateIDBDatabaseMetaData( 450 return backing_store_->CreateIDBDatabaseMetaData(
229 metadata_.name, metadata_.version, &metadata_.id); 451 metadata_.name, metadata_.version, &metadata_.id);
230 } 452 }
231 453
232 IndexedDBDatabase::~IndexedDBDatabase() { 454 IndexedDBDatabase::~IndexedDBDatabase() {
233 DCHECK(transactions_.empty()); 455 DCHECK(transactions_.empty());
234 DCHECK(pending_open_calls_.empty()); 456 DCHECK(!active_request_);
235 DCHECK(pending_delete_calls_.empty()); 457 DCHECK(pending_requests_.empty());
236 DCHECK(blocked_delete_calls_.empty());
237 } 458 }
238 459
239 size_t IndexedDBDatabase::GetMaxMessageSizeInBytes() const { 460 size_t IndexedDBDatabase::GetMaxMessageSizeInBytes() const {
240 return kMaxIDBMessageSizeInBytes; 461 return kMaxIDBMessageSizeInBytes;
241 } 462 }
242 463
243 std::unique_ptr<IndexedDBConnection> IndexedDBDatabase::CreateConnection( 464 std::unique_ptr<IndexedDBConnection> IndexedDBDatabase::CreateConnection(
244 scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks, 465 scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
245 int child_process_id) { 466 int child_process_id) {
246 std::unique_ptr<IndexedDBConnection> connection( 467 std::unique_ptr<IndexedDBConnection> connection(
(...skipping 1275 matching lines...) Expand 10 before | Expand all | Expand 10 after
1522 RemoveObjectStore(object_store_id); 1743 RemoveObjectStore(object_store_id);
1523 transaction->ScheduleAbortTask( 1744 transaction->ScheduleAbortTask(
1524 base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation, 1745 base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation,
1525 this, 1746 this,
1526 object_store_metadata)); 1747 object_store_metadata));
1527 } 1748 }
1528 1749
1529 void IndexedDBDatabase::VersionChangeOperation( 1750 void IndexedDBDatabase::VersionChangeOperation(
1530 int64_t version, 1751 int64_t version,
1531 scoped_refptr<IndexedDBCallbacks> callbacks, 1752 scoped_refptr<IndexedDBCallbacks> callbacks,
1532 std::unique_ptr<IndexedDBConnection> connection,
1533 IndexedDBTransaction* transaction) { 1753 IndexedDBTransaction* transaction) {
1534 IDB_TRACE1( 1754 IDB_TRACE1(
1535 "IndexedDBDatabase::VersionChangeOperation", "txn.id", transaction->id()); 1755 "IndexedDBDatabase::VersionChangeOperation", "txn.id", transaction->id());
1536 int64_t old_version = metadata_.version; 1756 int64_t old_version = metadata_.version;
1537 DCHECK_GT(version, old_version); 1757 DCHECK_GT(version, old_version);
1538 1758
1539 if (!backing_store_->UpdateIDBDatabaseIntVersion( 1759 if (!backing_store_->UpdateIDBDatabaseIntVersion(
1540 transaction->BackingStoreTransaction(), id(), version)) { 1760 transaction->BackingStoreTransaction(), id(), version)) {
1541 IndexedDBDatabaseError error( 1761 IndexedDBDatabaseError error(
1542 blink::WebIDBDatabaseExceptionUnknownError, 1762 blink::WebIDBDatabaseExceptionUnknownError,
1543 ASCIIToUTF16( 1763 ASCIIToUTF16(
1544 "Internal error writing data to stable storage when " 1764 "Internal error writing data to stable storage when "
1545 "updating version.")); 1765 "updating version."));
1546 callbacks->OnError(error); 1766 callbacks->OnError(error);
1547 transaction->Abort(error); 1767 transaction->Abort(error);
1548 return; 1768 return;
1549 } 1769 }
1550 1770
1551 transaction->ScheduleAbortTask( 1771 transaction->ScheduleAbortTask(
1552 base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation, this, 1772 base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation, this,
1553 metadata_.version)); 1773 metadata_.version));
1554 metadata_.version = version; 1774 metadata_.version = version;
1555 1775
1556 DCHECK(!pending_second_half_open_); 1776 active_request_->UpgradeTransactionStarted(old_version);
1557 pending_second_half_open_.reset(
1558 new PendingSuccessCall(callbacks, connection.get(), version));
1559 callbacks->OnUpgradeNeeded(old_version, std::move(connection), metadata());
1560 } 1777 }
1561 1778
1562 void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction* transaction, 1779 void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction* transaction,
1563 bool committed) { 1780 bool committed) {
1564 IDB_TRACE1("IndexedDBTransaction::TransactionFinished", "txn.id", id()); 1781 IDB_TRACE1("IndexedDBTransaction::TransactionFinished", "txn.id", id());
1565 DCHECK(transactions_.find(transaction->id()) != transactions_.end()); 1782 DCHECK(transactions_.find(transaction->id()) != transactions_.end());
1566 DCHECK_EQ(transactions_[transaction->id()], transaction); 1783 DCHECK_EQ(transactions_[transaction->id()], transaction);
1567 transactions_.erase(transaction->id()); 1784 transactions_.erase(transaction->id());
1568 1785
1569 if (transaction->mode() == blink::WebIDBTransactionModeVersionChange) { 1786 // This may be an unrelated transaction finishing while waiting for
1570 if (pending_second_half_open_) { 1787 // connections to close, or the actual upgrade transaction from an active
1571 if (committed) { 1788 // request. Notify the active request if it's the latter.
1572 DCHECK_EQ(pending_second_half_open_->version(), metadata_.version); 1789 if (active_request_ &&
1573 DCHECK(metadata_.id != kInvalidId); 1790 transaction->mode() == blink::WebIDBTransactionModeVersionChange) {
1574 1791 active_request_->UpgradeTransactionFinished(committed);
1575 // Connection was already minted for OnUpgradeNeeded callback.
1576 std::unique_ptr<IndexedDBConnection> connection;
1577 pending_second_half_open_->callbacks()->OnSuccess(std::move(connection),
1578 this->metadata());
1579 } else {
1580 pending_second_half_open_->callbacks()->OnError(
1581 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError,
1582 "Version change transaction was aborted in "
1583 "upgradeneeded event handler."));
1584 }
1585 pending_second_half_open_.reset();
1586 }
1587
1588 // Connection queue is now unblocked.
1589 ProcessPendingCalls();
1590 } 1792 }
1591 } 1793 }
1592 1794
1593 void IndexedDBDatabase::TransactionCommitFailed(const leveldb::Status& status) { 1795 void IndexedDBDatabase::TransactionCommitFailed(const leveldb::Status& status) {
1594 if (status.IsCorruption()) { 1796 if (status.IsCorruption()) {
1595 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 1797 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1596 "Error committing transaction"); 1798 "Error committing transaction");
1597 factory_->HandleBackingStoreCorruption(backing_store_->origin(), error); 1799 factory_->HandleBackingStoreCorruption(backing_store_->origin(), error);
1598 } else { 1800 } else {
1599 factory_->HandleBackingStoreFailure(backing_store_->origin()); 1801 factory_->HandleBackingStoreFailure(backing_store_->origin());
1600 } 1802 }
1601 } 1803 }
1602 1804
1603 size_t IndexedDBDatabase::ConnectionCount() const { 1805 void IndexedDBDatabase::AppendRequest(
1604 // This does not include pending open calls, as those should not block version 1806 std::unique_ptr<OpenOrDeleteRequest> request) {
1605 // changes and deletes. 1807 pending_requests_.push(std::move(request));
1606 return connections_.size(); 1808
1809 if (!active_request_)
1810 ProcessRequestQueue();
1607 } 1811 }
1608 1812
1609 size_t IndexedDBDatabase::PendingOpenCount() const { 1813 void IndexedDBDatabase::RequestComplete(OpenOrDeleteRequest* request) {
1610 return pending_open_calls_.size(); 1814 DCHECK_EQ(request, active_request_.get());
1815 active_request_.reset();
1816
1817 if (!pending_requests_.empty())
1818 ProcessRequestQueue();
1611 } 1819 }
1612 1820
1613 size_t IndexedDBDatabase::PendingUpgradeCount() const { 1821 void IndexedDBDatabase::ProcessRequestQueue() {
1614 return pending_run_version_change_transaction_call_ ? 1 : 0; 1822 // Don't run re-entrantly to avoid exploding call stacks for requests that
1823 // complete synchronously. The loop below will process requests until one is
1824 // blocked.
1825 if (processing_pending_requests_)
1826 return;
1827
1828 DCHECK(!active_request_);
1829 DCHECK(!pending_requests_.empty());
1830
1831 base::AutoReset<bool> processing(&processing_pending_requests_, true);
1832 do {
1833 active_request_ = std::move(pending_requests_.front());
1834 pending_requests_.pop();
1835 active_request_->Perform();
1836 // If the active request completed synchronously, keep going.
1837 } while (!active_request_ && !pending_requests_.empty());
1615 } 1838 }
1616 1839
1617 size_t IndexedDBDatabase::RunningUpgradeCount() const { 1840 IndexedDBTransaction* IndexedDBDatabase::CreateTransaction(
1618 return pending_second_half_open_ ? 1 : 0;
1619 }
1620
1621 size_t IndexedDBDatabase::PendingDeleteCount() const {
1622 return pending_delete_calls_.size();
1623 }
1624
1625 void IndexedDBDatabase::ProcessPendingCalls() {
1626 if (pending_run_version_change_transaction_call_ && ConnectionCount() == 1) {
1627 DCHECK(pending_run_version_change_transaction_call_->version() >
1628 metadata_.version);
1629 std::unique_ptr<PendingUpgradeCall> pending_call =
1630 std::move(pending_run_version_change_transaction_call_);
1631 RunVersionChangeTransactionFinal(pending_call->callbacks(),
1632 pending_call->ReleaseConnection(),
1633 pending_call->transaction_id(),
1634 pending_call->version());
1635 DCHECK_EQ(1u, ConnectionCount());
1636 // Fall through would be a no-op, since transaction must complete
1637 // asynchronously.
1638 DCHECK(IsUpgradeRunning());
1639 DCHECK(IsDeleteDatabaseBlocked());
1640 DCHECK(IsOpenConnectionBlocked());
1641 return;
1642 }
1643
1644 if (IsUpgradeRunning()) {
1645 DCHECK_EQ(ConnectionCount(), 1UL);
1646 return;
1647 }
1648
1649 // These delete calls were blocked by a running upgrade, and did not even
1650 // get as far as broadcasting OnVersionChange (if needed).
1651 {
1652 std::list<std::unique_ptr<PendingDeleteCall>> pending_delete_calls;
1653 pending_delete_calls_.swap(pending_delete_calls);
1654 while (!pending_delete_calls.empty()) {
1655 std::unique_ptr<PendingDeleteCall> pending_delete_call(
1656 std::move(pending_delete_calls.front()));
1657 pending_delete_calls.pop_front();
1658 DeleteDatabase(pending_delete_call->callbacks());
1659 }
1660 // DeleteDatabase should never re-queue these calls.
1661 DCHECK(pending_delete_calls_.empty());
1662 // Fall through when complete, as pending deletes/opens may be unblocked.
1663 }
1664
1665 // These delete calls broadcast OnVersionChange (if needed) but were blocked
1666 // by open connections.
1667 if (!IsDeleteDatabaseBlocked()) {
1668 std::list<std::unique_ptr<PendingDeleteCall>> blocked_delete_calls;
1669 blocked_delete_calls_.swap(blocked_delete_calls);
1670 while (!blocked_delete_calls.empty()) {
1671 // Only the first delete call will delete the database, but each must fire
1672 // callbacks.
1673 std::unique_ptr<PendingDeleteCall> pending_delete_call(
1674 std::move(blocked_delete_calls.front()));
1675 blocked_delete_calls.pop_front();
1676 DeleteDatabaseFinal(pending_delete_call->callbacks());
1677 }
1678 // DeleteDatabaseFinal should never re-queue these calls.
1679 DCHECK(blocked_delete_calls_.empty());
1680 // Fall through when complete, as pending opens may be unblocked.
1681 }
1682
1683 // These open calls were blocked by a pending/running upgrade or a pending
1684 // delete.
1685 if (!IsOpenConnectionBlocked()) {
1686 std::queue<IndexedDBPendingConnection> pending_open_calls;
1687 pending_open_calls_.swap(pending_open_calls);
1688 while (!pending_open_calls.empty()) {
1689 // This may re-queue open calls if an upgrade is necessary.
1690 OpenConnection(pending_open_calls.front());
1691 pending_open_calls.pop();
1692 }
1693 }
1694 }
1695
1696 void IndexedDBDatabase::CreateTransaction(
1697 int64_t transaction_id, 1841 int64_t transaction_id,
1698 IndexedDBConnection* connection, 1842 IndexedDBConnection* connection,
1699 const std::vector<int64_t>& object_store_ids, 1843 const std::vector<int64_t>& object_store_ids,
1700 blink::WebIDBTransactionMode mode) { 1844 blink::WebIDBTransactionMode mode) {
1701 IDB_TRACE1("IndexedDBDatabase::CreateTransaction", "txn.id", transaction_id); 1845 IDB_TRACE1("IndexedDBDatabase::CreateTransaction", "txn.id", transaction_id);
1702 DCHECK(connections_.count(connection)); 1846 DCHECK(connections_.count(connection));
1703 DCHECK(transactions_.find(transaction_id) == transactions_.end()); 1847 DCHECK(transactions_.find(transaction_id) == transactions_.end());
1704 if (transactions_.find(transaction_id) != transactions_.end()) 1848 if (transactions_.find(transaction_id) != transactions_.end())
1705 return; 1849 return nullptr;
1706 1850
1707 UMA_HISTOGRAM_COUNTS_1000( 1851 UMA_HISTOGRAM_COUNTS_1000(
1708 "WebCore.IndexedDB.Database.OutstandingTransactionCount", 1852 "WebCore.IndexedDB.Database.OutstandingTransactionCount",
1709 transactions_.size()); 1853 transactions_.size());
1710 1854
1711 // The transaction will add itself to this database's coordinator, which 1855 // The transaction will add itself to this database's coordinator, which
1712 // manages the lifetime of the object. 1856 // manages the lifetime of the object.
1713 TransactionCreated(IndexedDBClassFactory::Get()->CreateIndexedDBTransaction( 1857 IndexedDBTransaction* transaction =
1714 transaction_id, connection->GetWeakPtr(), 1858 IndexedDBClassFactory::Get()->CreateIndexedDBTransaction(
1715 std::set<int64_t>(object_store_ids.begin(), object_store_ids.end()), mode, 1859 transaction_id, connection->GetWeakPtr(),
1716 new IndexedDBBackingStore::Transaction(backing_store_.get()))); 1860 std::set<int64_t>(object_store_ids.begin(), object_store_ids.end()),
1861 mode, new IndexedDBBackingStore::Transaction(backing_store_.get()));
1862 TransactionCreated(transaction);
1863 return transaction;
1717 } 1864 }
1718 1865
1719 void IndexedDBDatabase::TransactionCreated(IndexedDBTransaction* transaction) { 1866 void IndexedDBDatabase::TransactionCreated(IndexedDBTransaction* transaction) {
1720 transactions_[transaction->id()] = transaction; 1867 transactions_[transaction->id()] = transaction;
1721 } 1868 }
1722 1869
1723 bool IndexedDBDatabase::IsUpgradeRunning() const {
1724 return transaction_coordinator_.IsRunningVersionChangeTransaction();
1725 }
1726
1727 bool IndexedDBDatabase::IsUpgradePendingOrRunning() const {
1728 return pending_run_version_change_transaction_call_ || IsUpgradeRunning();
1729 }
1730
1731 bool IndexedDBDatabase::IsOpenConnectionBlocked() const {
1732 return IsUpgradePendingOrRunning() || !blocked_delete_calls_.empty();
1733 }
1734
1735 void IndexedDBDatabase::OpenConnection( 1870 void IndexedDBDatabase::OpenConnection(
1736 const IndexedDBPendingConnection& connection) { 1871 const IndexedDBPendingConnection& connection) {
1737 DCHECK(backing_store_.get()); 1872 AppendRequest(base::MakeUnique<OpenRequest>(this, connection));
1738
1739 if (IsOpenConnectionBlocked()) {
1740 // The backing store only detects data loss when it is first opened. The
1741 // presence of existing connections means we didn't even check for data loss
1742 // so there'd better not be any.
1743 DCHECK_NE(blink::WebIDBDataLossTotal,
1744 connection.callbacks->data_loss_info().status);
1745 pending_open_calls_.push(connection);
1746 return;
1747 }
1748
1749 if (metadata_.id == kInvalidId) {
1750 // The database was deleted then immediately re-opened; OpenInternal()
1751 // recreates it in the backing store.
1752 if (OpenInternal().ok()) {
1753 DCHECK_EQ(IndexedDBDatabaseMetadata::NO_VERSION, metadata_.version);
1754 } else {
1755 base::string16 message;
1756 if (connection.version == IndexedDBDatabaseMetadata::NO_VERSION) {
1757 message = ASCIIToUTF16(
1758 "Internal error opening database with no version specified.");
1759 } else {
1760 message =
1761 ASCIIToUTF16("Internal error opening database with version ") +
1762 Int64ToString16(connection.version);
1763 }
1764 connection.callbacks->OnError(IndexedDBDatabaseError(
1765 blink::WebIDBDatabaseExceptionUnknownError, message));
1766 return;
1767 }
1768 }
1769
1770 // We infer that the database didn't exist from its lack of either type of
1771 // version.
1772 bool is_new_database =
1773 metadata_.version == IndexedDBDatabaseMetadata::NO_VERSION;
1774
1775 if (connection.version == IndexedDBDatabaseMetadata::DEFAULT_VERSION) {
1776 // For unit tests only - skip upgrade steps. Calling from script with
1777 // DEFAULT_VERSION throws exception.
1778 // TODO(jsbell): DCHECK that not in unit tests.
1779 DCHECK(is_new_database);
1780 connection.callbacks->OnSuccess(
1781 CreateConnection(connection.database_callbacks,
1782 connection.child_process_id),
1783 this->metadata());
1784 return;
1785 }
1786
1787 // We may need to change the version.
1788 int64_t local_version = connection.version;
1789 if (local_version == IndexedDBDatabaseMetadata::NO_VERSION) {
1790 if (!is_new_database) {
1791 connection.callbacks->OnSuccess(
1792 CreateConnection(connection.database_callbacks,
1793 connection.child_process_id),
1794 this->metadata());
1795 return;
1796 }
1797 // Spec says: If no version is specified and no database exists, set
1798 // database version to 1.
1799 local_version = 1;
1800 }
1801
1802 if (local_version > metadata_.version) {
1803 RunVersionChangeTransaction(connection.callbacks,
1804 CreateConnection(connection.database_callbacks,
1805 connection.child_process_id),
1806 connection.transaction_id,
1807 local_version);
1808 return;
1809 }
1810 if (local_version < metadata_.version) {
1811 connection.callbacks->OnError(IndexedDBDatabaseError(
1812 blink::WebIDBDatabaseExceptionVersionError,
1813 ASCIIToUTF16("The requested version (") +
1814 Int64ToString16(local_version) +
1815 ASCIIToUTF16(") is less than the existing version (") +
1816 Int64ToString16(metadata_.version) + ASCIIToUTF16(").")));
1817 return;
1818 }
1819 DCHECK_EQ(local_version, metadata_.version);
1820 connection.callbacks->OnSuccess(
1821 CreateConnection(connection.database_callbacks,
1822 connection.child_process_id),
1823 this->metadata());
1824 }
1825
1826 void IndexedDBDatabase::RunVersionChangeTransaction(
1827 scoped_refptr<IndexedDBCallbacks> callbacks,
1828 std::unique_ptr<IndexedDBConnection> connection,
1829 int64_t transaction_id,
1830 int64_t requested_version) {
1831 DCHECK(callbacks.get());
1832 DCHECK(connections_.count(connection.get()));
1833 if (ConnectionCount() > 1) {
1834 DCHECK_NE(blink::WebIDBDataLossTotal, callbacks->data_loss_info().status);
1835 // Front end ensures the event is not fired at connections that have
1836 // close_pending set.
1837 for (const auto* iter : connections_) {
1838 if (iter != connection.get()) {
1839 iter->callbacks()->OnVersionChange(metadata_.version,
1840 requested_version);
1841 }
1842 }
1843 // OnBlocked will be fired at the request when one of the other
1844 // connections acks that the OnVersionChange was ignored.
1845
1846 DCHECK(!pending_run_version_change_transaction_call_);
1847 pending_run_version_change_transaction_call_.reset(new PendingUpgradeCall(
1848 callbacks, std::move(connection), transaction_id, requested_version));
1849 return;
1850 }
1851 RunVersionChangeTransactionFinal(callbacks, std::move(connection),
1852 transaction_id, requested_version);
1853 }
1854
1855 void IndexedDBDatabase::RunVersionChangeTransactionFinal(
1856 scoped_refptr<IndexedDBCallbacks> callbacks,
1857 std::unique_ptr<IndexedDBConnection> connection,
1858 int64_t transaction_id,
1859 int64_t requested_version) {
1860 std::vector<int64_t> object_store_ids;
1861 CreateTransaction(transaction_id,
1862 connection.get(),
1863 object_store_ids,
1864 blink::WebIDBTransactionModeVersionChange);
1865
1866 DCHECK(transaction_coordinator_.IsRunningVersionChangeTransaction());
1867 transactions_[transaction_id]->ScheduleTask(
1868 base::Bind(&IndexedDBDatabase::VersionChangeOperation,
1869 this,
1870 requested_version,
1871 callbacks,
1872 base::Passed(&connection)));
1873 DCHECK(!pending_second_half_open_);
1874 } 1873 }
1875 1874
1876 void IndexedDBDatabase::DeleteDatabase( 1875 void IndexedDBDatabase::DeleteDatabase(
1877 scoped_refptr<IndexedDBCallbacks> callbacks) { 1876 scoped_refptr<IndexedDBCallbacks> callbacks) {
1878 // If there's a running upgrade, the delete calls should be deferred so that 1877 AppendRequest(base::MakeUnique<DeleteRequest>(this, callbacks));
1879 // "versionchange" is seen after "success" fires.
1880 if (IsUpgradeRunning()) {
1881 pending_delete_calls_.push_back(
1882 std::unique_ptr<PendingDeleteCall>(new PendingDeleteCall(callbacks)));
1883 return;
1884 }
1885
1886 if (IsDeleteDatabaseBlocked()) {
1887 for (const auto* connection : connections_) {
1888 // Front end ensures the event is not fired at connections that have
1889 // close_pending set.
1890 connection->callbacks()->OnVersionChange(
1891 metadata_.version, IndexedDBDatabaseMetadata::NO_VERSION);
1892 }
1893 // OnBlocked will be fired at the request when one of the other
1894 // connections acks that the OnVersionChange was ignored.
1895
1896 blocked_delete_calls_.push_back(
1897 std::unique_ptr<PendingDeleteCall>(new PendingDeleteCall(callbacks)));
1898 return;
1899 }
1900 DeleteDatabaseFinal(callbacks);
1901 }
1902
1903 bool IndexedDBDatabase::IsDeleteDatabaseBlocked() const {
1904 return IsUpgradePendingOrRunning() || !!ConnectionCount();
1905 }
1906
1907 void IndexedDBDatabase::DeleteDatabaseFinal(
1908 scoped_refptr<IndexedDBCallbacks> callbacks) {
1909 DCHECK(!IsDeleteDatabaseBlocked());
1910 DCHECK(backing_store_.get());
1911 leveldb::Status s = backing_store_->DeleteDatabase(metadata_.name);
1912 if (!s.ok()) {
1913 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1914 "Internal error deleting database.");
1915 callbacks->OnError(error);
1916 if (s.IsCorruption()) {
1917 url::Origin origin = backing_store_->origin();
1918 backing_store_ = NULL;
1919 factory_->HandleBackingStoreCorruption(origin, error);
1920 }
1921 return;
1922 }
1923 int64_t old_version = metadata_.version;
1924 metadata_.id = kInvalidId;
1925 metadata_.version = IndexedDBDatabaseMetadata::NO_VERSION;
1926 metadata_.object_stores.clear();
1927 callbacks->OnSuccess(old_version);
1928 factory_->DatabaseDeleted(identifier_);
1929 } 1878 }
1930 1879
1931 void IndexedDBDatabase::ForceClose() { 1880 void IndexedDBDatabase::ForceClose() {
1932 // IndexedDBConnection::ForceClose() may delete this database, so hold ref. 1881 // IndexedDBConnection::ForceClose() may delete this database, so hold ref.
1933 scoped_refptr<IndexedDBDatabase> protect(this); 1882 scoped_refptr<IndexedDBDatabase> protect(this);
1934 auto it = connections_.begin(); 1883 auto it = connections_.begin();
1935 while (it != connections_.end()) { 1884 while (it != connections_.end()) {
1936 IndexedDBConnection* connection = *it++; 1885 IndexedDBConnection* connection = *it++;
1937 connection->ForceClose(); 1886 connection->ForceClose();
1938 } 1887 }
1939 DCHECK(connections_.empty()); 1888 DCHECK(connections_.empty());
1940 } 1889 }
1941 1890
1942 void IndexedDBDatabase::VersionChangeIgnored() { 1891 void IndexedDBDatabase::VersionChangeIgnored() {
1943 if (pending_run_version_change_transaction_call_) { 1892 if (active_request_)
1944 pending_run_version_change_transaction_call_->callbacks()->OnBlocked( 1893 active_request_->OnVersionChangeIgnored();
1945 metadata_.version);
1946 }
1947
1948 for (const auto& pending_delete_call : blocked_delete_calls_)
1949 pending_delete_call->callbacks()->OnBlocked(metadata_.version);
1950 } 1894 }
1951 1895
1952 void IndexedDBDatabase::Close(IndexedDBConnection* connection, bool forced) { 1896 void IndexedDBDatabase::Close(IndexedDBConnection* connection, bool forced) {
1953 DCHECK(connections_.count(connection)); 1897 DCHECK(connections_.count(connection));
1954 DCHECK(connection->IsConnected()); 1898 DCHECK(connection->IsConnected());
1955 DCHECK(connection->database() == this); 1899 DCHECK(connection->database() == this);
1956 1900
1957 IDB_TRACE("IndexedDBDatabase::Close"); 1901 IDB_TRACE("IndexedDBDatabase::Close");
1958 1902
1959 connections_.erase(connection); 1903 // Abort outstanding transactions from the closing connection. This can not
1960 1904 // happen if the close is requested by the connection itself as the
1961 // Abort outstanding transactions from the closing connection. This 1905 // front-end defers the close until all transactions are complete, but can
1962 // can not happen if the close is requested by the connection itself 1906 // occur on process termination or forced close.
1963 // as the front-end defers the close until all transactions are
1964 // complete, but can occur on process termination or forced close.
1965 { 1907 {
1966 auto transactions(transactions_); 1908 auto transactions(transactions_);
1967 for (const auto& it : transactions) { 1909 for (const auto& it : transactions) {
1968 if (it.second->callbacks() == connection->callbacks()) 1910 if (it.second->callbacks() == connection->callbacks())
1969 it.second->Abort( 1911 it.second->Abort(
1970 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError, 1912 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
1971 "Connection is closing.")); 1913 "Connection is closing."));
1972 } 1914 }
1973 } 1915 }
1974 1916
1975 if (pending_second_half_open_ && 1917 // Abort transactions before removing the connection; aborting may complete
1976 pending_second_half_open_->connection() == connection) { 1918 // an upgrade, and thus allow the next open/delete requests to proceed. The
1977 pending_second_half_open_->callbacks()->OnError( 1919 // new active_request_ should see the old connection count until explicitly
1978 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError, 1920 // notified below.
1979 "The connection was closed.")); 1921 connections_.erase(connection);
1980 pending_second_half_open_.reset();
1981 }
1982 1922
1983 ProcessPendingCalls(); 1923 // Notify the active request, which may need to do cleanup or proceed with
1924 // the operation. This may trigger other work, such as more connections or
1925 // deletions, so |active_request_| itself may change.
1926 if (active_request_)
1927 active_request_->OnConnectionClosed(connection);
1984 1928
1985 // TODO(jsbell): Add a test for the pending_open_calls_ cases below. 1929 // If there are no more connections (current, active, or pending), tell the
1986 if (!ConnectionCount() && pending_open_calls_.empty() && 1930 // factory to clean us up.
1987 blocked_delete_calls_.empty()) { 1931 if (connections_.empty() && !active_request_ && pending_requests_.empty()) {
1988 DCHECK(transactions_.empty()); 1932 DCHECK(transactions_.empty());
1989 backing_store_ = NULL; 1933 backing_store_ = nullptr;
1990 factory_->ReleaseDatabase(identifier_, forced); 1934 factory_->ReleaseDatabase(identifier_, forced);
1991 } 1935 }
1992 } 1936 }
1993 1937
1994 void IndexedDBDatabase::CreateObjectStoreAbortOperation( 1938 void IndexedDBDatabase::CreateObjectStoreAbortOperation(
1995 int64_t object_store_id, 1939 int64_t object_store_id,
1996 IndexedDBTransaction* transaction) { 1940 IndexedDBTransaction* transaction) {
1997 DCHECK(!transaction); 1941 DCHECK(!transaction);
1998 IDB_TRACE("IndexedDBDatabase::CreateObjectStoreAbortOperation"); 1942 IDB_TRACE("IndexedDBDatabase::CreateObjectStoreAbortOperation");
1999 RemoveObjectStore(object_store_id); 1943 RemoveObjectStore(object_store_id);
(...skipping 10 matching lines...) Expand all
2010 1954
2011 void IndexedDBDatabase::VersionChangeAbortOperation( 1955 void IndexedDBDatabase::VersionChangeAbortOperation(
2012 int64_t previous_version, 1956 int64_t previous_version,
2013 IndexedDBTransaction* transaction) { 1957 IndexedDBTransaction* transaction) {
2014 DCHECK(!transaction); 1958 DCHECK(!transaction);
2015 IDB_TRACE("IndexedDBDatabase::VersionChangeAbortOperation"); 1959 IDB_TRACE("IndexedDBDatabase::VersionChangeAbortOperation");
2016 metadata_.version = previous_version; 1960 metadata_.version = previous_version;
2017 } 1961 }
2018 1962
2019 } // namespace content 1963 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698