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

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: Use pending member, other review nits 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), pending_(pending_connection) {}
120
121 void Perform() override {
122 if (db_->metadata_.id == kInvalidId) {
123 // The database was deleted then immediately re-opened; OpenInternal()
124 // recreates it in the backing store.
125 if (!db_->OpenInternal().ok()) {
126 // TODO(jsbell): Consider including sanitized leveldb status message.
127 base::string16 message;
128 if (pending_.version == IndexedDBDatabaseMetadata::NO_VERSION) {
129 message = ASCIIToUTF16(
130 "Internal error opening database with no version specified.");
131 } else {
132 message =
133 ASCIIToUTF16("Internal error opening database with version ") +
134 Int64ToString16(pending_.version);
135 }
136 pending_.callbacks->OnError(IndexedDBDatabaseError(
137 blink::WebIDBDatabaseExceptionUnknownError, message));
138 db_->RequestComplete(this);
139 return;
140 }
141
142 DCHECK_EQ(IndexedDBDatabaseMetadata::NO_VERSION, db_->metadata_.version);
143 }
144
145 const int64_t old_version = db_->metadata_.version;
146 int64_t& new_version = pending_.version;
147
148 bool is_new_database = old_version == IndexedDBDatabaseMetadata::NO_VERSION;
149
150 if (new_version == IndexedDBDatabaseMetadata::DEFAULT_VERSION) {
151 // For unit tests only - skip upgrade steps. (Calling from script with
152 // DEFAULT_VERSION throws exception.)
153 DCHECK(is_new_database);
154 pending_.callbacks->OnSuccess(
155 db_->CreateConnection(pending_.database_callbacks,
156 pending_.child_process_id),
157 db_->metadata_);
158 db_->RequestComplete(this);
159 return;
160 }
161
162 if (!is_new_database &&
163 (new_version == old_version ||
164 new_version == IndexedDBDatabaseMetadata::NO_VERSION)) {
165 pending_.callbacks->OnSuccess(
166 db_->CreateConnection(pending_.database_callbacks,
167 pending_.child_process_id),
168 db_->metadata_);
169 db_->RequestComplete(this);
170 return;
171 }
172
173 if (new_version == IndexedDBDatabaseMetadata::NO_VERSION) {
174 // If no version is specified and no database exists, upgrade the
175 // database version to 1.
176 DCHECK(is_new_database);
177 new_version = 1;
178 } else if (new_version < old_version) {
179 // Requested version is lower than current version - fail the request.
180 DCHECK(!is_new_database);
181 pending_.callbacks->OnError(IndexedDBDatabaseError(
182 blink::WebIDBDatabaseExceptionVersionError,
183 ASCIIToUTF16("The requested version (") +
184 Int64ToString16(pending_.version) +
185 ASCIIToUTF16(") is less than the existing version (") +
186 Int64ToString16(db_->metadata_.version) + ASCIIToUTF16(").")));
187 db_->RequestComplete(this);
188 return;
189 }
190
191 // Requested version is higher than current version - upgrade needed.
192 DCHECK_GT(new_version, old_version);
193
194 if (db_->connections_.empty()) {
195 StartUpgrade();
196 return;
197 }
198
199 // There are outstanding connections - fire "versionchange" events and
200 // wait for the connections to close. Front end ensures the event is not
201 // fired at connections that have close_pending set. A "blocked" event
202 // will be fired at the request when one of the connections acks that the
203 // "versionchange" event was ignored.
204 DCHECK_NE(pending_.callbacks->data_loss_info().status,
205 blink::WebIDBDataLossTotal);
206 for (const auto* connection : db_->connections_)
207 connection->callbacks()->OnVersionChange(old_version, new_version);
208
209 // When all connections have closed the upgrade can proceed.
210 }
211
212 void OnVersionChangeIgnored() const override {
213 pending_.callbacks->OnBlocked(db_->metadata_.version);
214 }
215
216 void OnConnectionClosed(IndexedDBConnection* connection) override {
217 // This connection closed prematurely; signal an error and complete.
218 if (connection && connection->callbacks() == pending_.database_callbacks) {
219 pending_.callbacks->OnError(
220 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError,
221 "The connection was closed."));
222 db_->RequestComplete(this);
223 return;
224 }
225
226 if (!db_->connections_.empty())
227 return;
228
229 StartUpgrade();
230 }
231
232 // Initiate the upgrade. The bulk of the work actually happens in
233 // IndexedDBDatabase::VersionChangeOperation in order to kick the
234 // transaction into the correct state.
235 void StartUpgrade() {
236 connection_ = db_->CreateConnection(pending_.database_callbacks,
237 pending_.child_process_id);
238 DCHECK_EQ(db_->connections_.count(connection_.get()), 1UL);
239
240 std::vector<int64_t> object_store_ids;
241 IndexedDBTransaction* transaction = db_->CreateTransaction(
242 pending_.transaction_id, connection_.get(), object_store_ids,
243 blink::WebIDBTransactionModeVersionChange);
244
245 DCHECK(db_->transaction_coordinator_.IsRunningVersionChangeTransaction());
246 transaction->ScheduleTask(
247 base::Bind(&IndexedDBDatabase::VersionChangeOperation, db_,
248 pending_.version, pending_.callbacks));
249 }
250
251 // Called when the upgrade transaction has started executing.
252 void UpgradeTransactionStarted(int64_t old_version) override {
253 DCHECK(connection_);
254 pending_.callbacks->OnUpgradeNeeded(old_version, std::move(connection_),
255 db_->metadata_);
256 }
257
258 void UpgradeTransactionFinished(bool committed) override {
259 // Ownership of connection was already passed along in OnUpgradeNeeded.
260 DCHECK(!connection_);
261
262 if (committed) {
263 DCHECK_EQ(pending_.version, db_->metadata_.version);
264 pending_.callbacks->OnSuccess(std::unique_ptr<IndexedDBConnection>(),
265 db_->metadata());
266 } else {
267 DCHECK_NE(pending_.version, db_->metadata_.version);
268 pending_.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 IndexedDBPendingConnection pending_;
278
279 // If an upgrade is needed, holds the pending connection until ownership is
280 // transferred to the IndexedDBDispatcherHost via OnUpgradeNeeded.
281 std::unique_ptr<IndexedDBConnection> connection_;
282
283 DISALLOW_COPY_AND_ASSIGN(OpenRequest);
284 };
285
286 class IndexedDBDatabase::DeleteRequest
287 : public IndexedDBDatabase::OpenOrDeleteRequest {
288 public:
289 DeleteRequest(scoped_refptr<IndexedDBDatabase> db,
290 scoped_refptr<IndexedDBCallbacks> callbacks)
291 : OpenOrDeleteRequest(db), callbacks_(callbacks) {}
292
293 void Perform() override {
294 if (db_->connections_.empty()) {
295 // No connections, so delete immediately.
296 DoDelete();
297 return;
298 }
299
300 // Front end ensures the event is not fired at connections that have
301 // close_pending set.
302 const int64_t old_version = db_->metadata_.version;
303 const int64_t new_version = IndexedDBDatabaseMetadata::NO_VERSION;
304 DCHECK_NE(callbacks_->data_loss_info().status, blink::WebIDBDataLossTotal);
305 for (const auto* connection : db_->connections_)
306 connection->callbacks()->OnVersionChange(old_version, new_version);
307 }
308
309 void OnVersionChangeIgnored() const override {
310 callbacks_->OnBlocked(db_->metadata_.version);
311 }
312
313 void OnConnectionClosed(IndexedDBConnection* connection) override {
314 if (!db_->connections_.empty())
315 return;
316 DoDelete();
317 }
318
319 void DoDelete() {
320 leveldb::Status s =
321 db_->backing_store_->DeleteDatabase(db_->metadata_.name);
322 if (!s.ok()) {
323 // TODO(jsbell): Consider including sanitized leveldb status message.
324 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
325 "Internal error deleting database.");
326 callbacks_->OnError(error);
327 if (s.IsCorruption()) {
328 url::Origin origin = db_->backing_store_->origin();
329 db_->backing_store_ = nullptr;
330 db_->factory_->HandleBackingStoreCorruption(origin, error);
331 }
332 db_->RequestComplete(this);
333 return;
334 }
335
336 int64_t old_version = db_->metadata_.version;
337 db_->metadata_.id = kInvalidId;
338 db_->metadata_.version = IndexedDBDatabaseMetadata::NO_VERSION;
339 db_->metadata_.max_object_store_id = kInvalidId;
340 db_->metadata_.object_stores.clear();
341 callbacks_->OnSuccess(old_version);
342 db_->factory_->DatabaseDeleted(db_->identifier_);
343
344 db_->RequestComplete(this);
345 }
346
347 void UpgradeTransactionStarted(int64_t old_version) override { NOTREACHED(); }
348
349 void UpgradeTransactionFinished(bool committed) override { NOTREACHED(); }
100 350
101 private: 351 private:
102 scoped_refptr<IndexedDBCallbacks> callbacks_; 352 scoped_refptr<IndexedDBCallbacks> callbacks_;
103 std::unique_ptr<IndexedDBConnection> connection_; 353
104 int64_t version_; 354 DISALLOW_COPY_AND_ASSIGN(DeleteRequest);
105 const int64_t transaction_id_;
106 }; 355 };
107 356
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( 357 scoped_refptr<IndexedDBDatabase> IndexedDBDatabase::Create(
138 const base::string16& name, 358 const base::string16& name,
139 IndexedDBBackingStore* backing_store, 359 IndexedDBBackingStore* backing_store,
140 IndexedDBFactory* factory, 360 IndexedDBFactory* factory,
141 const Identifier& unique_identifier, 361 const Identifier& unique_identifier,
142 leveldb::Status* s) { 362 leveldb::Status* s) {
143 scoped_refptr<IndexedDBDatabase> database = 363 scoped_refptr<IndexedDBDatabase> database =
144 IndexedDBClassFactory::Get()->CreateIndexedDBDatabase( 364 IndexedDBClassFactory::Get()->CreateIndexedDBDatabase(
145 name, backing_store, factory, unique_identifier); 365 name, backing_store, factory, unique_identifier);
146 *s = database->OpenInternal(); 366 *s = database->OpenInternal();
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
224 if (success) 444 if (success)
225 return backing_store_->GetObjectStores(metadata_.id, 445 return backing_store_->GetObjectStores(metadata_.id,
226 &metadata_.object_stores); 446 &metadata_.object_stores);
227 447
228 return backing_store_->CreateIDBDatabaseMetaData( 448 return backing_store_->CreateIDBDatabaseMetaData(
229 metadata_.name, metadata_.version, &metadata_.id); 449 metadata_.name, metadata_.version, &metadata_.id);
230 } 450 }
231 451
232 IndexedDBDatabase::~IndexedDBDatabase() { 452 IndexedDBDatabase::~IndexedDBDatabase() {
233 DCHECK(transactions_.empty()); 453 DCHECK(transactions_.empty());
234 DCHECK(pending_open_calls_.empty()); 454 DCHECK(!active_request_);
235 DCHECK(pending_delete_calls_.empty()); 455 DCHECK(pending_requests_.empty());
236 DCHECK(blocked_delete_calls_.empty());
237 } 456 }
238 457
239 size_t IndexedDBDatabase::GetMaxMessageSizeInBytes() const { 458 size_t IndexedDBDatabase::GetMaxMessageSizeInBytes() const {
240 return kMaxIDBMessageSizeInBytes; 459 return kMaxIDBMessageSizeInBytes;
241 } 460 }
242 461
243 std::unique_ptr<IndexedDBConnection> IndexedDBDatabase::CreateConnection( 462 std::unique_ptr<IndexedDBConnection> IndexedDBDatabase::CreateConnection(
244 scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks, 463 scoped_refptr<IndexedDBDatabaseCallbacks> database_callbacks,
245 int child_process_id) { 464 int child_process_id) {
246 std::unique_ptr<IndexedDBConnection> connection( 465 std::unique_ptr<IndexedDBConnection> connection(
(...skipping 1275 matching lines...) Expand 10 before | Expand all | Expand 10 after
1522 RemoveObjectStore(object_store_id); 1741 RemoveObjectStore(object_store_id);
1523 transaction->ScheduleAbortTask( 1742 transaction->ScheduleAbortTask(
1524 base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation, 1743 base::Bind(&IndexedDBDatabase::DeleteObjectStoreAbortOperation,
1525 this, 1744 this,
1526 object_store_metadata)); 1745 object_store_metadata));
1527 } 1746 }
1528 1747
1529 void IndexedDBDatabase::VersionChangeOperation( 1748 void IndexedDBDatabase::VersionChangeOperation(
1530 int64_t version, 1749 int64_t version,
1531 scoped_refptr<IndexedDBCallbacks> callbacks, 1750 scoped_refptr<IndexedDBCallbacks> callbacks,
1532 std::unique_ptr<IndexedDBConnection> connection,
1533 IndexedDBTransaction* transaction) { 1751 IndexedDBTransaction* transaction) {
1534 IDB_TRACE1( 1752 IDB_TRACE1(
1535 "IndexedDBDatabase::VersionChangeOperation", "txn.id", transaction->id()); 1753 "IndexedDBDatabase::VersionChangeOperation", "txn.id", transaction->id());
1536 int64_t old_version = metadata_.version; 1754 int64_t old_version = metadata_.version;
1537 DCHECK_GT(version, old_version); 1755 DCHECK_GT(version, old_version);
1538 1756
1539 if (!backing_store_->UpdateIDBDatabaseIntVersion( 1757 if (!backing_store_->UpdateIDBDatabaseIntVersion(
1540 transaction->BackingStoreTransaction(), id(), version)) { 1758 transaction->BackingStoreTransaction(), id(), version)) {
1541 IndexedDBDatabaseError error( 1759 IndexedDBDatabaseError error(
1542 blink::WebIDBDatabaseExceptionUnknownError, 1760 blink::WebIDBDatabaseExceptionUnknownError,
1543 ASCIIToUTF16( 1761 ASCIIToUTF16(
1544 "Internal error writing data to stable storage when " 1762 "Internal error writing data to stable storage when "
1545 "updating version.")); 1763 "updating version."));
1546 callbacks->OnError(error); 1764 callbacks->OnError(error);
1547 transaction->Abort(error); 1765 transaction->Abort(error);
1548 return; 1766 return;
1549 } 1767 }
1550 1768
1551 transaction->ScheduleAbortTask( 1769 transaction->ScheduleAbortTask(
1552 base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation, this, 1770 base::Bind(&IndexedDBDatabase::VersionChangeAbortOperation, this,
1553 metadata_.version)); 1771 metadata_.version));
1554 metadata_.version = version; 1772 metadata_.version = version;
1555 1773
1556 DCHECK(!pending_second_half_open_); 1774 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 } 1775 }
1561 1776
1562 void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction* transaction, 1777 void IndexedDBDatabase::TransactionFinished(IndexedDBTransaction* transaction,
1563 bool committed) { 1778 bool committed) {
1564 IDB_TRACE1("IndexedDBTransaction::TransactionFinished", "txn.id", id()); 1779 IDB_TRACE1("IndexedDBTransaction::TransactionFinished", "txn.id", id());
1565 DCHECK(transactions_.find(transaction->id()) != transactions_.end()); 1780 DCHECK(transactions_.find(transaction->id()) != transactions_.end());
1566 DCHECK_EQ(transactions_[transaction->id()], transaction); 1781 DCHECK_EQ(transactions_[transaction->id()], transaction);
1567 transactions_.erase(transaction->id()); 1782 transactions_.erase(transaction->id());
1568 1783
1569 if (transaction->mode() == blink::WebIDBTransactionModeVersionChange) { 1784 // This may be an unrelated transaction finishing while waiting for
1570 if (pending_second_half_open_) { 1785 // connections to close, or the actual upgrade transaction from an active
1571 if (committed) { 1786 // request. Notify the active request if it's the latter.
1572 DCHECK_EQ(pending_second_half_open_->version(), metadata_.version); 1787 if (active_request_ &&
1573 DCHECK(metadata_.id != kInvalidId); 1788 transaction->mode() == blink::WebIDBTransactionModeVersionChange) {
1574 1789 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 } 1790 }
1591 } 1791 }
1592 1792
1593 void IndexedDBDatabase::TransactionCommitFailed(const leveldb::Status& status) { 1793 void IndexedDBDatabase::TransactionCommitFailed(const leveldb::Status& status) {
1594 if (status.IsCorruption()) { 1794 if (status.IsCorruption()) {
1595 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError, 1795 IndexedDBDatabaseError error(blink::WebIDBDatabaseExceptionUnknownError,
1596 "Error committing transaction"); 1796 "Error committing transaction");
1597 factory_->HandleBackingStoreCorruption(backing_store_->origin(), error); 1797 factory_->HandleBackingStoreCorruption(backing_store_->origin(), error);
1598 } else { 1798 } else {
1599 factory_->HandleBackingStoreFailure(backing_store_->origin()); 1799 factory_->HandleBackingStoreFailure(backing_store_->origin());
1600 } 1800 }
1601 } 1801 }
1602 1802
1603 size_t IndexedDBDatabase::ConnectionCount() const { 1803 void IndexedDBDatabase::AppendRequest(
1604 // This does not include pending open calls, as those should not block version 1804 std::unique_ptr<OpenOrDeleteRequest> request) {
1605 // changes and deletes. 1805 pending_requests_.push(std::move(request));
1606 return connections_.size(); 1806
1807 if (!active_request_)
1808 ProcessRequestQueue();
1607 } 1809 }
1608 1810
1609 size_t IndexedDBDatabase::PendingOpenCount() const { 1811 void IndexedDBDatabase::RequestComplete(OpenOrDeleteRequest* request) {
1610 return pending_open_calls_.size(); 1812 DCHECK_EQ(request, active_request_.get());
1813 active_request_.reset();
1814
1815 if (!pending_requests_.empty())
1816 ProcessRequestQueue();
1611 } 1817 }
1612 1818
1613 size_t IndexedDBDatabase::PendingUpgradeCount() const { 1819 void IndexedDBDatabase::ProcessRequestQueue() {
1614 return pending_run_version_change_transaction_call_ ? 1 : 0; 1820 // Don't run re-entrantly to avoid exploding call stacks for requests that
1821 // complete synchronously. The loop below will process requests until one is
1822 // blocked.
1823 if (processing_pending_requests_)
1824 return;
1825
1826 DCHECK(!active_request_);
1827 DCHECK(!pending_requests_.empty());
1828
1829 base::AutoReset<bool> processing(&processing_pending_requests_, true);
1830 do {
1831 active_request_ = std::move(pending_requests_.front());
1832 pending_requests_.pop();
1833 active_request_->Perform();
1834 // If the active request completed synchronously, keep going.
1835 } while (!active_request_ && !pending_requests_.empty());
1615 } 1836 }
1616 1837
1617 size_t IndexedDBDatabase::RunningUpgradeCount() const { 1838 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, 1839 int64_t transaction_id,
1698 IndexedDBConnection* connection, 1840 IndexedDBConnection* connection,
1699 const std::vector<int64_t>& object_store_ids, 1841 const std::vector<int64_t>& object_store_ids,
1700 blink::WebIDBTransactionMode mode) { 1842 blink::WebIDBTransactionMode mode) {
1701 IDB_TRACE1("IndexedDBDatabase::CreateTransaction", "txn.id", transaction_id); 1843 IDB_TRACE1("IndexedDBDatabase::CreateTransaction", "txn.id", transaction_id);
1702 DCHECK(connections_.count(connection)); 1844 DCHECK(connections_.count(connection));
1703 DCHECK(transactions_.find(transaction_id) == transactions_.end()); 1845 DCHECK(transactions_.find(transaction_id) == transactions_.end());
1704 if (transactions_.find(transaction_id) != transactions_.end()) 1846 if (transactions_.find(transaction_id) != transactions_.end())
1705 return; 1847 return nullptr;
1706 1848
1707 UMA_HISTOGRAM_COUNTS_1000( 1849 UMA_HISTOGRAM_COUNTS_1000(
1708 "WebCore.IndexedDB.Database.OutstandingTransactionCount", 1850 "WebCore.IndexedDB.Database.OutstandingTransactionCount",
1709 transactions_.size()); 1851 transactions_.size());
1710 1852
1711 // The transaction will add itself to this database's coordinator, which 1853 // The transaction will add itself to this database's coordinator, which
1712 // manages the lifetime of the object. 1854 // manages the lifetime of the object.
1713 TransactionCreated(IndexedDBClassFactory::Get()->CreateIndexedDBTransaction( 1855 IndexedDBTransaction* transaction =
1714 transaction_id, connection->GetWeakPtr(), 1856 IndexedDBClassFactory::Get()->CreateIndexedDBTransaction(
1715 std::set<int64_t>(object_store_ids.begin(), object_store_ids.end()), mode, 1857 transaction_id, connection->GetWeakPtr(),
1716 new IndexedDBBackingStore::Transaction(backing_store_.get()))); 1858 std::set<int64_t>(object_store_ids.begin(), object_store_ids.end()),
1859 mode, new IndexedDBBackingStore::Transaction(backing_store_.get()));
1860 TransactionCreated(transaction);
1861 return transaction;
1717 } 1862 }
1718 1863
1719 void IndexedDBDatabase::TransactionCreated(IndexedDBTransaction* transaction) { 1864 void IndexedDBDatabase::TransactionCreated(IndexedDBTransaction* transaction) {
1720 transactions_[transaction->id()] = transaction; 1865 transactions_[transaction->id()] = transaction;
1721 } 1866 }
1722 1867
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( 1868 void IndexedDBDatabase::OpenConnection(
1736 const IndexedDBPendingConnection& connection) { 1869 const IndexedDBPendingConnection& connection) {
1737 DCHECK(backing_store_.get()); 1870 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 } 1871 }
1875 1872
1876 void IndexedDBDatabase::DeleteDatabase( 1873 void IndexedDBDatabase::DeleteDatabase(
1877 scoped_refptr<IndexedDBCallbacks> callbacks) { 1874 scoped_refptr<IndexedDBCallbacks> callbacks) {
1878 // If there's a running upgrade, the delete calls should be deferred so that 1875 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 } 1876 }
1930 1877
1931 void IndexedDBDatabase::ForceClose() { 1878 void IndexedDBDatabase::ForceClose() {
1932 // IndexedDBConnection::ForceClose() may delete this database, so hold ref. 1879 // IndexedDBConnection::ForceClose() may delete this database, so hold ref.
1933 scoped_refptr<IndexedDBDatabase> protect(this); 1880 scoped_refptr<IndexedDBDatabase> protect(this);
1934 auto it = connections_.begin(); 1881 auto it = connections_.begin();
1935 while (it != connections_.end()) { 1882 while (it != connections_.end()) {
1936 IndexedDBConnection* connection = *it++; 1883 IndexedDBConnection* connection = *it++;
1937 connection->ForceClose(); 1884 connection->ForceClose();
1938 } 1885 }
1939 DCHECK(connections_.empty()); 1886 DCHECK(connections_.empty());
1940 } 1887 }
1941 1888
1942 void IndexedDBDatabase::VersionChangeIgnored() { 1889 void IndexedDBDatabase::VersionChangeIgnored() {
1943 if (pending_run_version_change_transaction_call_) { 1890 if (active_request_)
1944 pending_run_version_change_transaction_call_->callbacks()->OnBlocked( 1891 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 } 1892 }
1951 1893
1952 void IndexedDBDatabase::Close(IndexedDBConnection* connection, bool forced) { 1894 void IndexedDBDatabase::Close(IndexedDBConnection* connection, bool forced) {
1953 DCHECK(connections_.count(connection)); 1895 DCHECK(connections_.count(connection));
1954 DCHECK(connection->IsConnected()); 1896 DCHECK(connection->IsConnected());
1955 DCHECK(connection->database() == this); 1897 DCHECK(connection->database() == this);
1956 1898
1957 IDB_TRACE("IndexedDBDatabase::Close"); 1899 IDB_TRACE("IndexedDBDatabase::Close");
1958 1900
1959 connections_.erase(connection); 1901 // Abort outstanding transactions from the closing connection. This can not
1960 1902 // happen if the close is requested by the connection itself as the
1961 // Abort outstanding transactions from the closing connection. This 1903 // 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 1904 // 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 { 1905 {
1966 auto transactions(transactions_); 1906 auto transactions(transactions_);
1967 for (const auto& it : transactions) { 1907 for (const auto& it : transactions) {
1968 if (it.second->callbacks() == connection->callbacks()) 1908 if (it.second->callbacks() == connection->callbacks())
1969 it.second->Abort( 1909 it.second->Abort(
1970 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError, 1910 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionUnknownError,
1971 "Connection is closing.")); 1911 "Connection is closing."));
1972 } 1912 }
1973 } 1913 }
1974 1914
1975 if (pending_second_half_open_ && 1915 // Abort transactions before removing the connection; aborting may complete
1976 pending_second_half_open_->connection() == connection) { 1916 // an upgrade, and thus allow the next open/delete requests to proceed. The
1977 pending_second_half_open_->callbacks()->OnError( 1917 // new active_request_ should see the old connection count until explicitly
1978 IndexedDBDatabaseError(blink::WebIDBDatabaseExceptionAbortError, 1918 // notified below.
1979 "The connection was closed.")); 1919 connections_.erase(connection);
1980 pending_second_half_open_.reset();
1981 }
1982 1920
1983 ProcessPendingCalls(); 1921 // Notify the active request, which may need to do cleanup or proceed with
1922 // the operation. This may trigger other work, such as more connections or
1923 // deletions, so |active_request_| itself may change.
1924 if (active_request_)
1925 active_request_->OnConnectionClosed(connection);
1984 1926
1985 // TODO(jsbell): Add a test for the pending_open_calls_ cases below. 1927 // If there are no more connections (current, active, or pending), tell the
1986 if (!ConnectionCount() && pending_open_calls_.empty() && 1928 // factory to clean us up.
1987 blocked_delete_calls_.empty()) { 1929 if (connections_.empty() && !active_request_ && pending_requests_.empty()) {
1988 DCHECK(transactions_.empty()); 1930 DCHECK(transactions_.empty());
1989 backing_store_ = NULL; 1931 backing_store_ = nullptr;
1990 factory_->ReleaseDatabase(identifier_, forced); 1932 factory_->ReleaseDatabase(identifier_, forced);
1991 } 1933 }
1992 } 1934 }
1993 1935
1994 void IndexedDBDatabase::CreateObjectStoreAbortOperation( 1936 void IndexedDBDatabase::CreateObjectStoreAbortOperation(
1995 int64_t object_store_id, 1937 int64_t object_store_id,
1996 IndexedDBTransaction* transaction) { 1938 IndexedDBTransaction* transaction) {
1997 DCHECK(!transaction); 1939 DCHECK(!transaction);
1998 IDB_TRACE("IndexedDBDatabase::CreateObjectStoreAbortOperation"); 1940 IDB_TRACE("IndexedDBDatabase::CreateObjectStoreAbortOperation");
1999 RemoveObjectStore(object_store_id); 1941 RemoveObjectStore(object_store_id);
(...skipping 10 matching lines...) Expand all
2010 1952
2011 void IndexedDBDatabase::VersionChangeAbortOperation( 1953 void IndexedDBDatabase::VersionChangeAbortOperation(
2012 int64_t previous_version, 1954 int64_t previous_version,
2013 IndexedDBTransaction* transaction) { 1955 IndexedDBTransaction* transaction) {
2014 DCHECK(!transaction); 1956 DCHECK(!transaction);
2015 IDB_TRACE("IndexedDBDatabase::VersionChangeAbortOperation"); 1957 IDB_TRACE("IndexedDBDatabase::VersionChangeAbortOperation");
2016 metadata_.version = previous_version; 1958 metadata_.version = previous_version;
2017 } 1959 }
2018 1960
2019 } // namespace content 1961 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/indexed_db/indexed_db_database.h ('k') | content/browser/indexed_db/indexed_db_database_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698