Diff
Modified: trunk/Source/WTF/ChangeLog (244686 => 244687)
--- trunk/Source/WTF/ChangeLog 2019-04-26 16:00:04 UTC (rev 244686)
+++ trunk/Source/WTF/ChangeLog 2019-04-26 16:08:40 UTC (rev 244687)
@@ -1,3 +1,19 @@
+2019-04-26 Sihui Liu <[email protected]>
+
+ Stop IDB transactions to release locked database files when network process is ready to suspend
+ https://bugs.webkit.org/show_bug.cgi?id=196372
+ <rdar://problem/48930116>
+
+ Reviewed by Brady Eidson.
+
+ Provide a method to suspend the thread and block main thread until the thread is suspended.
+
+ * wtf/CrossThreadTaskHandler.cpp:
+ (WTF::CrossThreadTaskHandler::taskRunLoop):
+ (WTF::CrossThreadTaskHandler::suspendAndWait):
+ (WTF::CrossThreadTaskHandler::resume):
+ * wtf/CrossThreadTaskHandler.h:
+
2019-04-25 Fujii Hironori <[email protected]>
Unreviewed, rolling out r244669.
Modified: trunk/Source/WTF/wtf/CrossThreadTaskHandler.cpp (244686 => 244687)
--- trunk/Source/WTF/wtf/CrossThreadTaskHandler.cpp 2019-04-26 16:00:04 UTC (rev 244686)
+++ trunk/Source/WTF/wtf/CrossThreadTaskHandler.cpp 2019-04-26 16:08:40 UTC (rev 244687)
@@ -68,8 +68,20 @@
Locker<Lock> locker(m_taskThreadCreationLock);
}
- while (!m_taskQueue.isKilled())
+ while (!m_taskQueue.isKilled()) {
m_taskQueue.waitForMessage().performTask();
+
+ Locker<Lock> shouldSuspendLocker(m_shouldSuspendLock);
+ while (m_shouldSuspend) {
+ m_suspendedLock.lock();
+ if (!m_suspended) {
+ m_suspended = true;
+ m_suspendedCondition.notifyOne();
+ }
+ m_suspendedLock.unlock();
+ m_shouldSuspendCondition.wait(m_shouldSuspendLock);
+ }
+ }
}
void CrossThreadTaskHandler::handleTaskRepliesOnMainThread()
@@ -83,5 +95,34 @@
task->performTask();
}
+void CrossThreadTaskHandler::suspendAndWait()
+{
+ ASSERT(isMainThread());
+ {
+ Locker<Lock> locker(m_shouldSuspendLock);
+ m_shouldSuspend = true;
+ }
+ // Post an empty task to ensure database thread knows m_shouldSuspend and sets m_suspended.
+ postTask(CrossThreadTask([]() { }));
+
+ Locker<Lock> locker(m_suspendedLock);
+ while (!m_suspended)
+ m_suspendedCondition.wait(m_suspendedLock);
+}
+
+void CrossThreadTaskHandler::resume()
+{
+ ASSERT(isMainThread());
+ Locker<Lock> locker(m_shouldSuspendLock);
+ if (m_shouldSuspend) {
+ m_suspendedLock.lock();
+ if (m_suspended)
+ m_suspended = false;
+ m_suspendedLock.unlock();
+ m_shouldSuspend = false;
+ m_shouldSuspendCondition.notifyOne();
+ }
+}
+
} // namespace WTF
Modified: trunk/Source/WTF/wtf/CrossThreadTaskHandler.h (244686 => 244687)
--- trunk/Source/WTF/wtf/CrossThreadTaskHandler.h 2019-04-26 16:00:04 UTC (rev 244686)
+++ trunk/Source/WTF/wtf/CrossThreadTaskHandler.h 2019-04-26 16:08:40 UTC (rev 244687)
@@ -44,6 +44,8 @@
WTF_EXPORT_PRIVATE void postTask(CrossThreadTask&&);
WTF_EXPORT_PRIVATE void postTaskReply(CrossThreadTask&&);
+ WTF_EXPORT_PRIVATE void suspendAndWait();
+ WTF_EXPORT_PRIVATE void resume();
private:
void handleTaskRepliesOnMainThread();
@@ -53,6 +55,14 @@
Lock m_mainThreadReplyLock;
bool m_mainThreadReplyScheduled { false };
+ bool m_shouldSuspend { false };
+ Condition m_shouldSuspendCondition;
+ Lock m_shouldSuspendLock;
+
+ bool m_suspended { false };
+ Condition m_suspendedCondition;
+ Lock m_suspendedLock;
+
CrossThreadQueue<CrossThreadTask> m_taskQueue;
CrossThreadQueue<CrossThreadTask> m_taskReplyQueue;
};
Modified: trunk/Source/WebCore/ChangeLog (244686 => 244687)
--- trunk/Source/WebCore/ChangeLog 2019-04-26 16:00:04 UTC (rev 244686)
+++ trunk/Source/WebCore/ChangeLog 2019-04-26 16:08:40 UTC (rev 244687)
@@ -1,3 +1,65 @@
+2019-04-26 Sihui Liu <[email protected]>
+
+ Stop IDB transactions to release locked database files when network process is ready to suspend
+ https://bugs.webkit.org/show_bug.cgi?id=196372
+ <rdar://problem/48930116>
+
+ Reviewed by Brady Eidson.
+
+ Suspend IDB database thread and finish ongoing IDB transactions on the main thread before suspending network
+ process.
+
+ API test: IndexedDB.IndexedDBSuspendImminently
+
+ * Modules/indexeddb/server/IDBBackingStore.h:
+ * Modules/indexeddb/server/IDBServer.cpp:
+ (WebCore::IDBServer::IDBServer::tryStop):
+ (WebCore::IDBServer::IDBServer::resume):
+ * Modules/indexeddb/server/IDBServer.h:
+ * Modules/indexeddb/server/MemoryIDBBackingStore.h:
+ * Modules/indexeddb/server/SQLiteIDBBackingStore.cpp: Remove some error log messages, because now we may try
+ performing database operations without an active transaction if the transaction is finished on the main thread.
+ (WebCore::IDBServer::SQLiteIDBBackingStore::createObjectStore):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::deleteObjectStore):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::renameObjectStore):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::clearObjectStore):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::createIndex):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::deleteIndex):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::renameIndex):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::keyExistsInObjectStore):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::deleteRange):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::addRecord):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::getRecord):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::getAllObjectStoreRecords):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::getAllIndexRecords):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::getIndexRecord):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::getCount):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::generateKeyNumber):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::revertGeneratedKeyNumber):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::maybeUpdateKeyGeneratorNumber):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::openCursor):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::iterateCursor):
+ (WebCore::IDBServer::SQLiteIDBBackingStore::hasTransaction const):
+ * Modules/indexeddb/server/SQLiteIDBBackingStore.h:
+ * Modules/indexeddb/server/UniqueIDBDatabase.cpp:
+ (WebCore::IDBServer::UniqueIDBDatabase::prepareToFinishTransaction):
+ (WebCore::IDBServer::UniqueIDBDatabase::commitTransactionAfterQuotaCheck):
+ (WebCore::IDBServer::UniqueIDBDatabase::didPerformCommitTransaction):
+ (WebCore::IDBServer::UniqueIDBDatabase::abortTransaction):
+ (WebCore::IDBServer::UniqueIDBDatabase::didPerformAbortTransaction):
+ (WebCore::IDBServer::UniqueIDBDatabase::abortTransactionOnMainThread):
+ (WebCore::IDBServer::UniqueIDBDatabase::commitTransactionOnMainThread):
+ (WebCore::IDBServer::UniqueIDBDatabase::finishActiveTransactions):
+ * Modules/indexeddb/server/UniqueIDBDatabase.h:
+ * Modules/indexeddb/server/UniqueIDBDatabaseTransaction.h:
+ (WebCore::IDBServer::UniqueIDBDatabaseTransaction::setState):
+ (WebCore::IDBServer::UniqueIDBDatabaseTransaction::state const):
+ (WebCore::IDBServer::UniqueIDBDatabaseTransaction::setResult):
+ (WebCore::IDBServer::UniqueIDBDatabaseTransaction::result const):
+ * platform/sql/SQLiteDatabaseTracker.cpp:
+ (WebCore::SQLiteDatabaseTracker::hasTransactionInProgress):
+ * platform/sql/SQLiteDatabaseTracker.h:
+
2019-04-26 Takashi Komori <[email protected]>
[Curl] Fix Curl Request Scheduler not to release wrong Curl handle when request is cancelled.
Modified: trunk/Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h (244686 => 244687)
--- trunk/Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h 2019-04-26 16:00:04 UTC (rev 244686)
+++ trunk/Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h 2019-04-26 16:08:40 UTC (rev 244687)
@@ -101,6 +101,8 @@
virtual uint64_t databasesSizeForOrigin() const = 0;
virtual void setQuota(uint64_t) = 0;
+
+ virtual bool hasTransaction(const IDBResourceIdentifier&) const = 0;
protected:
IDBBackingStore() { RELEASE_ASSERT(!isMainThread()); }
};
Modified: trunk/Source/WebCore/Modules/indexeddb/server/IDBServer.cpp (244686 => 244687)
--- trunk/Source/WebCore/Modules/indexeddb/server/IDBServer.cpp 2019-04-26 16:00:04 UTC (rev 244686)
+++ trunk/Source/WebCore/Modules/indexeddb/server/IDBServer.cpp 2019-04-26 16:08:40 UTC (rev 244687)
@@ -33,6 +33,7 @@
#include "Logging.h"
#include "MemoryIDBBackingStore.h"
#include "SQLiteDatabase.h"
+#include "SQLiteDatabaseTracker.h"
#include "SQLiteFileSystem.h"
#include "SQLiteIDBBackingStore.h"
#include "SQLiteStatement.h"
@@ -828,6 +829,30 @@
FileSystem::makeAllDirectories(newVersionDirectory);
}
+void IDBServer::tryStop(ShouldForceStop shouldForceStop)
+{
+ // Only stop non-ephemeral IDBServers that can hold locked database files.
+ if (m_sessionID.isEphemeral())
+ return;
+
+ suspendAndWait();
+ if (shouldForceStop == ShouldForceStop::No && SQLiteDatabaseTracker::hasTransactionInProgress()) {
+ CrossThreadTaskHandler::resume();
+ return;
+ }
+
+ for (auto& database : m_uniqueIDBDatabaseMap.values())
+ database->finishActiveTransactions();
+}
+
+void IDBServer::resume()
+{
+ if (m_sessionID.isEphemeral())
+ return;
+
+ CrossThreadTaskHandler::resume();
+}
+
} // namespace IDBServer
} // namespace WebCore
Modified: trunk/Source/WebCore/Modules/indexeddb/server/IDBServer.h (244686 => 244687)
--- trunk/Source/WebCore/Modules/indexeddb/server/IDBServer.h 2019-04-26 16:00:04 UTC (rev 244686)
+++ trunk/Source/WebCore/Modules/indexeddb/server/IDBServer.h 2019-04-26 16:08:40 UTC (rev 244687)
@@ -58,6 +58,8 @@
class IDBBackingStoreTemporaryFileHandler;
+enum class ShouldForceStop : bool { No, Yes };
+
class IDBServer : public RefCounted<IDBServer>, public CrossThreadTaskHandler, public CanMakeWeakPtr<IDBServer> {
public:
using QuotaManagerGetter = WTF::Function<StorageQuotaManager*(PAL::SessionID, const ClientOrigin&)>;
@@ -124,6 +126,9 @@
void initializeQuotaUser(const ClientOrigin& origin) { ensureQuotaUser(origin); }
+ WEBCORE_EXPORT void tryStop(ShouldForceStop);
+ WEBCORE_EXPORT void resume();
+
private:
IDBServer(PAL::SessionID, IDBBackingStoreTemporaryFileHandler&, QuotaManagerGetter&&);
IDBServer(PAL::SessionID, const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler&, QuotaManagerGetter&&);
Modified: trunk/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.h (244686 => 244687)
--- trunk/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.h 2019-04-26 16:00:04 UTC (rev 244686)
+++ trunk/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.h 2019-04-26 16:08:40 UTC (rev 244687)
@@ -85,6 +85,8 @@
void removeObjectStoreForVersionChangeAbort(MemoryObjectStore&);
void restoreObjectStoreForVersionChangeAbort(Ref<MemoryObjectStore>&&);
+ bool hasTransaction(const IDBResourceIdentifier& identifier) const final { return m_transactions.contains(identifier); }
+
private:
RefPtr<MemoryObjectStore> takeObjectStoreByIdentifier(uint64_t identifier);
Modified: trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp (244686 => 244687)
--- trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp 2019-04-26 16:00:04 UTC (rev 244686)
+++ trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp 2019-04-26 16:08:40 UTC (rev 244687)
@@ -1005,10 +1005,9 @@
ASSERT(m_sqliteDB->isOpen());
auto* transaction = m_transactions.get(transactionIdentifier);
- if (!transaction || !transaction->inProgress()) {
- LOG_ERROR("Attempt to create an object store without an in-progress transaction");
+ if (!transaction || !transaction->inProgress())
return IDBError { UnknownError, "Attempt to create an object store without an in-progress transaction"_s };
- }
+
if (transaction->mode() != IDBTransactionMode::Versionchange) {
LOG_ERROR("Attempt to create an object store in a non-version-change transaction");
return IDBError { UnknownError, "Attempt to create an object store in a non-version-change transaction"_s };
@@ -1061,10 +1060,9 @@
ASSERT(m_sqliteDB->isOpen());
auto* transaction = m_transactions.get(transactionIdentifier);
- if (!transaction || !transaction->inProgress()) {
- LOG_ERROR("Attempt to delete an object store without an in-progress transaction");
+ if (!transaction || !transaction->inProgress())
return IDBError { UnknownError, "Attempt to delete an object store without an in-progress transaction"_s };
- }
+
if (transaction->mode() != IDBTransactionMode::Versionchange) {
LOG_ERROR("Attempt to delete an object store in a non-version-change transaction");
return IDBError { UnknownError, "Attempt to delete an object store in a non-version-change transaction"_s };
@@ -1153,10 +1151,9 @@
ASSERT(m_sqliteDB->isOpen());
auto* transaction = m_transactions.get(transactionIdentifier);
- if (!transaction || !transaction->inProgress()) {
- LOG_ERROR("Attempt to rename an object store without an in-progress transaction");
+ if (!transaction || !transaction->inProgress())
return IDBError { UnknownError, "Attempt to rename an object store without an in-progress transaction"_s };
- }
+
if (transaction->mode() != IDBTransactionMode::Versionchange) {
LOG_ERROR("Attempt to rename an object store in a non-version-change transaction");
return IDBError { UnknownError, "Attempt to rename an object store in a non-version-change transaction"_s };
@@ -1188,10 +1185,9 @@
ASSERT(m_sqliteDB->isOpen());
auto* transaction = m_transactions.get(transactionIdentifier);
- if (!transaction || !transaction->inProgress()) {
- LOG_ERROR("Attempt to clear an object store without an in-progress transaction");
+ if (!transaction || !transaction->inProgress())
return IDBError { UnknownError, "Attempt to clear an object store without an in-progress transaction"_s };
- }
+
if (transaction->mode() == IDBTransactionMode::Readonly) {
LOG_ERROR("Attempt to clear an object store in a read-only transaction");
return IDBError { UnknownError, "Attempt to clear an object store in a read-only transaction"_s };
@@ -1229,10 +1225,9 @@
ASSERT(m_sqliteDB->isOpen());
auto* transaction = m_transactions.get(transactionIdentifier);
- if (!transaction || !transaction->inProgress()) {
- LOG_ERROR("Attempt to create an index without an in-progress transaction");
+ if (!transaction || !transaction->inProgress())
return IDBError { UnknownError, "Attempt to create an index without an in-progress transaction"_s };
- }
+
if (transaction->mode() != IDBTransactionMode::Versionchange) {
LOG_ERROR("Attempt to create an index in a non-version-change transaction");
return IDBError { UnknownError, "Attempt to create an index in a non-version-change transaction"_s };
@@ -1422,10 +1417,8 @@
ASSERT(m_sqliteDB->isOpen());
auto* transaction = m_transactions.get(transactionIdentifier);
- if (!transaction || !transaction->inProgress()) {
- LOG_ERROR("Attempt to delete index without an in-progress transaction");
+ if (!transaction || !transaction->inProgress())
return IDBError { UnknownError, "Attempt to delete index without an in-progress transaction"_s };
- }
if (transaction->mode() != IDBTransactionMode::Versionchange) {
LOG_ERROR("Attempt to delete index during a non-version-change transaction");
@@ -1477,10 +1470,8 @@
return IDBError { UnknownError, "Could not rename index"_s };
auto* transaction = m_transactions.get(transactionIdentifier);
- if (!transaction || !transaction->inProgress()) {
- LOG_ERROR("Attempt to rename an index without an in-progress transaction");
+ if (!transaction || !transaction->inProgress())
return IDBError { UnknownError, "Attempt to rename an index without an in-progress transaction"_s };
- }
if (transaction->mode() != IDBTransactionMode::Versionchange) {
LOG_ERROR("Attempt to rename an index in a non-version-change transaction");
@@ -1516,10 +1507,8 @@
keyExists = false;
auto* transaction = m_transactions.get(transactionIdentifier);
- if (!transaction || !transaction->inProgress()) {
- LOG_ERROR("Attempt to see if key exists in objectstore without an in-progress transaction");
+ if (!transaction || !transaction->inProgress())
return IDBError { UnknownError, "Attempt to see if key exists in objectstore without an in-progress transaction"_s };
- }
RefPtr<SharedBuffer> keyBuffer = serializeIDBKeyData(keyData);
if (!keyBuffer) {
@@ -1691,10 +1680,9 @@
ASSERT(m_sqliteDB->isOpen());
auto* transaction = m_transactions.get(transactionIdentifier);
- if (!transaction || !transaction->inProgress()) {
- LOG_ERROR("Attempt to delete range from database without an in-progress transaction");
+ if (!transaction || !transaction->inProgress())
return IDBError { UnknownError, "Attempt to delete range from database without an in-progress transaction"_s };
- }
+
if (transaction->mode() == IDBTransactionMode::Readonly) {
LOG_ERROR("Attempt to delete records from an object store in a read-only transaction");
return IDBError { UnknownError, "Attempt to delete records from an object store in a read-only transaction"_s };
@@ -1814,10 +1802,9 @@
ASSERT(value.blobURLs().size() == value.blobFilePaths().size());
auto* transaction = m_transactions.get(transactionIdentifier);
- if (!transaction || !transaction->inProgress()) {
- LOG_ERROR("Attempt to store a record in an object store without an in-progress transaction");
+ if (!transaction || !transaction->inProgress())
return IDBError { UnknownError, "Attempt to store a record in an object store without an in-progress transaction"_s };
- }
+
if (transaction->mode() == IDBTransactionMode::Readonly) {
LOG_ERROR("Attempt to store a record in an object store in a read-only transaction");
return IDBError { UnknownError, "Attempt to store a record in an object store in a read-only transaction"_s };
@@ -1983,10 +1970,8 @@
ASSERT(m_sqliteDB->isOpen());
auto* transaction = m_transactions.get(transactionIdentifier);
- if (!transaction || !transaction->inProgress()) {
- LOG_ERROR("Attempt to get a record from database without an in-progress transaction");
+ if (!transaction || !transaction->inProgress())
return IDBError { UnknownError, "Attempt to get a record from database without an in-progress transaction"_s };
- }
auto key = keyRange.lowerKey;
if (key.isNull())
@@ -2160,10 +2145,8 @@
ASSERT(m_sqliteDB->isOpen());
auto* transaction = m_transactions.get(transactionIdentifier);
- if (!transaction || !transaction->inProgress()) {
- LOG_ERROR("Attempt to get records from database without an in-progress transaction");
+ if (!transaction || !transaction->inProgress())
return IDBError { UnknownError, "Attempt to get records from database without an in-progress transaction"_s };
- }
auto key = getAllRecordsData.keyRangeData.lowerKey;
if (key.isNull())
@@ -2256,10 +2239,8 @@
ASSERT(m_sqliteDB->isOpen());
auto* transaction = m_transactions.get(transactionIdentifier);
- if (!transaction || !transaction->inProgress()) {
- LOG_ERROR("Attempt to get all index records from database without an in-progress transaction");
+ if (!transaction || !transaction->inProgress())
return IDBError { UnknownError, "Attempt to get all index records from database without an in-progress transaction"_s };
- }
auto cursor = transaction->maybeOpenBackingStoreCursor(getAllRecordsData.objectStoreIdentifier, getAllRecordsData.indexIdentifier, getAllRecordsData.keyRangeData);
if (!cursor) {
@@ -2306,10 +2287,8 @@
ASSERT(m_sqliteDB->isOpen());
auto* transaction = m_transactions.get(transactionIdentifier);
- if (!transaction || !transaction->inProgress()) {
- LOG_ERROR("Attempt to get an index record from database without an in-progress transaction");
+ if (!transaction || !transaction->inProgress())
return IDBError { UnknownError, "Attempt to get an index record from database without an in-progress transaction"_s };
- }
if (range.isExactlyOneKey())
return uncheckedGetIndexRecordForOneKey(indexID, objectStoreID, type, range.lowerKey, getResult);
@@ -2412,10 +2391,8 @@
outCount = 0;
auto* transaction = m_transactions.get(transactionIdentifier);
- if (!transaction || !transaction->inProgress()) {
- LOG_ERROR("Attempt to get count from database without an in-progress transaction");
+ if (!transaction || !transaction->inProgress())
return IDBError { UnknownError, "Attempt to get count from database without an in-progress transaction"_s };
- }
auto cursor = transaction->maybeOpenBackingStoreCursor(objectStoreIdentifier, indexIdentifier, range);
if (!cursor) {
@@ -2475,10 +2452,9 @@
ASSERT(m_sqliteDB->isOpen());
auto* transaction = m_transactions.get(transactionIdentifier);
- if (!transaction || !transaction->inProgress()) {
- LOG_ERROR("Attempt to generate key in database without an in-progress transaction");
+ if (!transaction || !transaction->inProgress())
return IDBError { UnknownError, "Attempt to generate key in database without an in-progress transaction"_s };
- }
+
if (transaction->mode() == IDBTransactionMode::Readonly) {
LOG_ERROR("Attempt to generate key in a read-only transaction");
return IDBError { UnknownError, "Attempt to generate key in a read-only transaction"_s };
@@ -2504,10 +2480,9 @@
ASSERT(m_sqliteDB->isOpen());
auto* transaction = m_transactions.get(transactionIdentifier);
- if (!transaction || !transaction->inProgress()) {
- LOG_ERROR("Attempt to revert key generator value in database without an in-progress transaction");
+ if (!transaction || !transaction->inProgress())
return IDBError { UnknownError, "Attempt to revert key generator value in database without an in-progress transaction"_s };
- }
+
if (transaction->mode() == IDBTransactionMode::Readonly) {
LOG_ERROR("Attempt to revert key generator value in a read-only transaction");
return IDBError { UnknownError, "Attempt to revert key generator value in a read-only transaction"_s };
@@ -2525,10 +2500,9 @@
ASSERT(m_sqliteDB->isOpen());
auto* transaction = m_transactions.get(transactionIdentifier);
- if (!transaction || !transaction->inProgress()) {
- LOG_ERROR("Attempt to update key generator value in database without an in-progress transaction");
+ if (!transaction || !transaction->inProgress())
return IDBError { UnknownError, "Attempt to update key generator value in database without an in-progress transaction"_s };
- }
+
if (transaction->mode() == IDBTransactionMode::Readonly) {
LOG_ERROR("Attempt to update key generator value in a read-only transaction");
return IDBError { UnknownError, "Attempt to update key generator value in a read-only transaction"_s };
@@ -2551,10 +2525,8 @@
ASSERT(m_sqliteDB->isOpen());
auto* transaction = m_transactions.get(transactionIdentifier);
- if (!transaction || !transaction->inProgress()) {
- LOG_ERROR("Attempt to open a cursor in database without an in-progress transaction");
+ if (!transaction || !transaction->inProgress())
return IDBError { UnknownError, "Attempt to open a cursor in database without an in-progress transaction"_s };
- }
auto* cursor = transaction->maybeOpenCursor(info);
if (!cursor) {
@@ -2585,10 +2557,8 @@
ASSERT_UNUSED(transactionIdentifier, cursor->transaction()->transactionIdentifier() == transactionIdentifier);
- if (!cursor->transaction() || !cursor->transaction()->inProgress()) {
- LOG_ERROR("Attempt to iterate a cursor without an in-progress transaction");
+ if (!cursor->transaction() || !cursor->transaction()->inProgress())
return IDBError { UnknownError, "Attempt to iterate a cursor without an in-progress transaction"_s };
- }
auto key = data.keyData;
auto primaryKey = data.primaryKeyData;
@@ -2718,6 +2688,12 @@
m_sqliteDB = nullptr;
}
+bool SQLiteIDBBackingStore::hasTransaction(const IDBResourceIdentifier& transactionIdentifier) const
+{
+ ASSERT(isMainThread());
+ return m_transactions.contains(transactionIdentifier);
+}
+
} // namespace IDBServer
} // namespace WebCore
Modified: trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h (244686 => 244687)
--- trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h 2019-04-26 16:00:04 UTC (rev 244686)
+++ trunk/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h 2019-04-26 16:08:40 UTC (rev 244687)
@@ -100,6 +100,7 @@
static String fullDatabasePathForDirectory(const String&);
static String databaseNameFromFile(const String&);
+ bool hasTransaction(const IDBResourceIdentifier&) const final;
private:
String filenameForDatabaseName() const;
String fullDatabasePath() const;
Modified: trunk/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp (244686 => 244687)
--- trunk/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp 2019-04-26 16:00:04 UTC (rev 244686)
+++ trunk/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp 2019-04-26 16:08:40 UTC (rev 244687)
@@ -1567,7 +1567,7 @@
performGetResultCallback(callbackIdentifier, error, result);
}
-bool UniqueIDBDatabase::prepareToFinishTransaction(UniqueIDBDatabaseTransaction& transaction)
+bool UniqueIDBDatabase::prepareToFinishTransaction(UniqueIDBDatabaseTransaction& transaction, UniqueIDBDatabaseTransaction::State state)
{
auto takenTransaction = m_inProgressTransactions.take(transaction.info().identifier());
if (!takenTransaction)
@@ -1574,6 +1574,7 @@
return false;
ASSERT(!m_finishingTransactions.contains(transaction.info().identifier()));
+ takenTransaction->setState(state);
m_finishingTransactions.set(transaction.info().identifier(), WTFMove(takenTransaction));
return true;
@@ -1601,7 +1602,7 @@
if (!callbackID)
return;
- if (!prepareToFinishTransaction(transaction)) {
+ if (!prepareToFinishTransaction(transaction, UniqueIDBDatabaseTransaction::State::Committing)) {
if (!m_openDatabaseConnections.contains(&transaction.databaseConnection())) {
// This database connection is closing or has already closed, so there is no point in messaging back to it about the commit failing.
forgetErrorCallback(callbackID);
@@ -1629,8 +1630,24 @@
ASSERT(isMainThread());
LOG(IndexedDB, "(main) UniqueIDBDatabase::didPerformCommitTransaction - %s", transactionIdentifier.loggingString().utf8().data());
- performErrorCallback(callbackIdentifier, error);
+ IDBError result = error;
+ auto transaction = m_finishingTransactions.get(transactionIdentifier);
+ switch (transaction->state()) {
+ case UniqueIDBDatabaseTransaction::State::Aborted:
+ result = IDBError { UnknownError, "Transaction is already aborted"_s };
+ break;
+ case UniqueIDBDatabaseTransaction::State::Committed:
+ result = transaction->result();
+ break;
+ case UniqueIDBDatabaseTransaction::State::Committing:
+ break;
+ case UniqueIDBDatabaseTransaction::State::Running:
+ case UniqueIDBDatabaseTransaction::State::Aborting:
+ ASSERT_NOT_REACHED();
+ }
+ performErrorCallback(callbackIdentifier, result);
+
transactionCompleted(m_finishingTransactions.take(transactionIdentifier));
}
@@ -1656,7 +1673,7 @@
if (!callbackID)
return;
- if (!prepareToFinishTransaction(transaction)) {
+ if (!prepareToFinishTransaction(transaction, UniqueIDBDatabaseTransaction::State::Aborting)) {
if (!m_openDatabaseConnections.contains(&transaction.databaseConnection())) {
// This database connection is closing or has already closed, so there is no point in messaging back to it about the abort failing.
forgetErrorCallback(callbackID);
@@ -1713,7 +1730,8 @@
m_databaseInfo = std::make_unique<IDBDatabaseInfo>(*m_versionChangeTransaction->originalDatabaseInfo());
}
- performErrorCallback(callbackIdentifier, error);
+ IDBError result = transaction->state() == UniqueIDBDatabaseTransaction::State::Aborted ? transaction->result() : error;
+ performErrorCallback(callbackIdentifier, result);
transactionCompleted(WTFMove(transaction));
}
@@ -2277,6 +2295,47 @@
m_backingStore->setQuota(quota);
}
+void UniqueIDBDatabase::abortTransactionOnMainThread(UniqueIDBDatabaseTransaction& transaction)
+{
+ transaction.setResult(m_backingStore->abortTransaction(transaction.info().identifier()));
+ transaction.setState(UniqueIDBDatabaseTransaction::State::Aborted);
+}
+
+void UniqueIDBDatabase::commitTransactionOnMainThread(UniqueIDBDatabaseTransaction& transaction)
+{
+ transaction.setResult(m_backingStore->commitTransaction(transaction.info().identifier()));
+ transaction.setState(UniqueIDBDatabaseTransaction::State::Committed);
+}
+
+void UniqueIDBDatabase::finishActiveTransactions()
+{
+ ASSERT(isMainThread());
+
+ for (auto& identifier : copyToVector(m_inProgressTransactions.keys())) {
+ auto transaction = m_inProgressTransactions.get(identifier);
+ abortTransactionOnMainThread(*transaction);
+ }
+
+ for (auto& identifier : copyToVector(m_finishingTransactions.keys())) {
+ if (!m_backingStore->hasTransaction(identifier))
+ continue;
+
+ auto transaction = m_finishingTransactions.get(identifier);
+ switch (transaction->state()) {
+ case UniqueIDBDatabaseTransaction::State::Aborting:
+ abortTransactionOnMainThread(*transaction);
+ break;
+ case UniqueIDBDatabaseTransaction::State::Committing:
+ commitTransactionOnMainThread(*transaction);
+ break;
+ case UniqueIDBDatabaseTransaction::State::Running:
+ case UniqueIDBDatabaseTransaction::State::Aborted:
+ case UniqueIDBDatabaseTransaction::State::Committed:
+ ASSERT_NOT_REACHED();
+ }
+ }
+}
+
} // namespace IDBServer
} // namespace WebCore
Modified: trunk/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.h (244686 => 244687)
--- trunk/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.h 2019-04-26 16:00:04 UTC (rev 244686)
+++ trunk/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.h 2019-04-26 16:08:40 UTC (rev 244687)
@@ -127,6 +127,8 @@
void setQuota(uint64_t);
+ void finishActiveTransactions();
+
private:
void handleDatabaseOperations();
void handleCurrentOperation();
@@ -237,7 +239,9 @@
void operationAndTransactionTimerFired();
RefPtr<UniqueIDBDatabaseTransaction> takeNextRunnableTransaction(bool& hadDeferredTransactions);
- bool prepareToFinishTransaction(UniqueIDBDatabaseTransaction&);
+ bool prepareToFinishTransaction(UniqueIDBDatabaseTransaction&, UniqueIDBDatabaseTransaction::State);
+ void abortTransactionOnMainThread(UniqueIDBDatabaseTransaction&);
+ void commitTransactionOnMainThread(UniqueIDBDatabaseTransaction&);
void clearStalePendingOpenDBRequests();
Modified: trunk/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseTransaction.h (244686 => 244687)
--- trunk/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseTransaction.h 2019-04-26 16:00:04 UTC (rev 244686)
+++ trunk/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabaseTransaction.h 2019-04-26 16:08:40 UTC (rev 244687)
@@ -56,6 +56,8 @@
class UniqueIDBDatabaseTransaction : public RefCounted<UniqueIDBDatabaseTransaction> {
public:
+ enum class State { Running, Aborting, Committing, Aborted, Committed };
+
static Ref<UniqueIDBDatabaseTransaction> create(UniqueIDBDatabaseConnection&, const IDBTransactionInfo&);
~UniqueIDBDatabaseTransaction();
@@ -90,6 +92,11 @@
const Vector<uint64_t>& objectStoreIdentifiers();
+ void setState(State state) { m_state = state; }
+ State state() const { return m_state; }
+ void setResult(const IDBError& error) { m_result = error; }
+ const IDBError& result() const { return m_result; }
+
private:
UniqueIDBDatabaseTransaction(UniqueIDBDatabaseConnection&, const IDBTransactionInfo&);
@@ -100,6 +107,9 @@
std::unique_ptr<IDBDatabaseInfo> m_originalDatabaseInfo;
Vector<uint64_t> m_objectStoreIdentifiers;
+
+ State m_state { State::Running };
+ IDBError m_result;
};
} // namespace IDBServer
Modified: trunk/Source/WebCore/platform/sql/SQLiteDatabaseTracker.cpp (244686 => 244687)
--- trunk/Source/WebCore/platform/sql/SQLiteDatabaseTracker.cpp 2019-04-26 16:00:04 UTC (rev 244686)
+++ trunk/Source/WebCore/platform/sql/SQLiteDatabaseTracker.cpp 2019-04-26 16:08:40 UTC (rev 244687)
@@ -68,13 +68,11 @@
s_staticSQLiteDatabaseTrackerClient->didFinishLastTransaction();
}
-#if !ASSERT_DISABLED
bool hasTransactionInProgress()
{
std::lock_guard<Lock> lock(transactionInProgressMutex);
return !s_staticSQLiteDatabaseTrackerClient || s_transactionInProgressCounter > 0;
}
-#endif
} // namespace SQLiteDatabaseTracker
Modified: trunk/Source/WebCore/platform/sql/SQLiteDatabaseTracker.h (244686 => 244687)
--- trunk/Source/WebCore/platform/sql/SQLiteDatabaseTracker.h 2019-04-26 16:00:04 UTC (rev 244686)
+++ trunk/Source/WebCore/platform/sql/SQLiteDatabaseTracker.h 2019-04-26 16:08:40 UTC (rev 244687)
@@ -37,9 +37,7 @@
WEBCORE_EXPORT void setClient(SQLiteDatabaseTrackerClient*);
-#if !ASSERT_DISABLED
WEBCORE_EXPORT bool hasTransactionInProgress();
-#endif
};
Modified: trunk/Source/WebKit/ChangeLog (244686 => 244687)
--- trunk/Source/WebKit/ChangeLog 2019-04-26 16:00:04 UTC (rev 244686)
+++ trunk/Source/WebKit/ChangeLog 2019-04-26 16:08:40 UTC (rev 244687)
@@ -1,3 +1,16 @@
+2019-04-26 Sihui Liu <[email protected]>
+
+ Stop IDB transactions to release locked database files when network process is ready to suspend
+ https://bugs.webkit.org/show_bug.cgi?id=196372
+ <rdar://problem/48930116>
+
+ Reviewed by Brady Eidson.
+
+ * NetworkProcess/NetworkProcess.cpp:
+ (WebKit::NetworkProcess::processWillSuspendImminently):
+ (WebKit::NetworkProcess::prepareToSuspend):
+ (WebKit::NetworkProcess::resume):
+
2019-04-25 Myles C. Maxfield <[email protected]>
[iOS] Add internal setting to force -webkit-text-size-adjust to "auto"
Modified: trunk/Source/WebKit/NetworkProcess/NetworkProcess.cpp (244686 => 244687)
--- trunk/Source/WebKit/NetworkProcess/NetworkProcess.cpp 2019-04-26 16:00:04 UTC (rev 244686)
+++ trunk/Source/WebKit/NetworkProcess/NetworkProcess.cpp 2019-04-26 16:08:40 UTC (rev 244687)
@@ -2007,6 +2007,10 @@
void NetworkProcess::processWillSuspendImminently(CompletionHandler<void(bool)>&& completionHandler)
{
+#if PLATFORM(IOS_FAMILY) && ENABLE(INDEXED_DATABASE)
+ for (auto& server : m_idbServers.values())
+ server->tryStop(IDBServer::ShouldForceStop::Yes);
+#endif
actualPrepareToSuspend(ShouldAcknowledgeWhenReadyToSuspend::No);
completionHandler(true);
}
@@ -2014,6 +2018,11 @@
void NetworkProcess::prepareToSuspend()
{
RELEASE_LOG(ProcessSuspension, "%p - NetworkProcess::prepareToSuspend()", this);
+
+#if PLATFORM(IOS_FAMILY) && ENABLE(INDEXED_DATABASE)
+ for (auto& server : m_idbServers.values())
+ server->tryStop(IDBServer::ShouldForceStop::No);
+#endif
actualPrepareToSuspend(ShouldAcknowledgeWhenReadyToSuspend::Yes);
}
@@ -2053,6 +2062,10 @@
for (auto& server : m_swServers.values())
server->endSuspension();
#endif
+#if PLATFORM(IOS_FAMILY) && ENABLE(INDEXED_DATABASE)
+ for (auto& server : m_idbServers.values())
+ server->resume();
+#endif
}
void NetworkProcess::prefetchDNS(const String& hostname)
Modified: trunk/Tools/ChangeLog (244686 => 244687)
--- trunk/Tools/ChangeLog 2019-04-26 16:00:04 UTC (rev 244686)
+++ trunk/Tools/ChangeLog 2019-04-26 16:08:40 UTC (rev 244687)
@@ -1,3 +1,19 @@
+2019-04-26 Sihui Liu <[email protected]>
+
+ Stop IDB transactions to release locked database files when network process is ready to suspend
+ https://bugs.webkit.org/show_bug.cgi?id=196372
+ <rdar://problem/48930116>
+
+ Reviewed by Brady Eidson.
+
+ * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+ * TestWebKitAPI/Tests/WebKitCocoa/IndexedDBSuspendImminently.html: Added.
+ * TestWebKitAPI/Tests/WebKitCocoa/IndexedDBSuspendImminently.mm: Added.
+ (-[IndexedDBSuspendImminentlyMessageHandler userContentController:didReceiveScriptMessage:]):
+ (runTestAndCheckResult):
+ (keepNetworkProcessActive):
+ (TEST):
+
2019-04-25 Simon Fraser <[email protected]>
REGRESSION (r234330): 3 legacy-animation-engine/compositing tests are flaky failures
Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (244686 => 244687)
--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2019-04-26 16:00:04 UTC (rev 244686)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2019-04-26 16:08:40 UTC (rev 244687)
@@ -783,6 +783,8 @@
C9C9CD4321E6A6860019DB96 /* autoplaying-multiple-media-elements.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = C9C9CD4221E6A6670019DB96 /* autoplaying-multiple-media-elements.html */; };
C9E6DD351EA97D0800DD78AA /* FirstResponderSuppression.mm in Sources */ = {isa = PBXBuildFile; fileRef = C9E6DD311EA972D800DD78AA /* FirstResponderSuppression.mm */; };
C9E8EE7521DED94300797765 /* long-test.mp4 in Copy Resources */ = {isa = PBXBuildFile; fileRef = C9E8EE7421DED91E00797765 /* long-test.mp4 */; };
+ CA2879DB226E69A5001026F5 /* IndexedDBSuspendImminently.mm in Sources */ = {isa = PBXBuildFile; fileRef = CA2879DA226E6986001026F5 /* IndexedDBSuspendImminently.mm */; };
+ CA2879DC226E69B6001026F5 /* IndexedDBSuspendImminently.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CA2879D9226E6978001026F5 /* IndexedDBSuspendImminently.html */; };
CA38459620AE17A900990D3B /* LocalStorageDatabaseTracker.mm in Sources */ = {isa = PBXBuildFile; fileRef = CA38459520AE012E00990D3B /* LocalStorageDatabaseTracker.mm */; };
CA5B94D22190C0F40059FE38 /* IndexedDBTempFileSize-1.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CA5B94D02190C0E00059FE38 /* IndexedDBTempFileSize-1.html */; };
CA5B94D32190C0F40059FE38 /* IndexedDBTempFileSize-2.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = CA5B94D12190C0E00059FE38 /* IndexedDBTempFileSize-2.html */; };
@@ -1177,6 +1179,7 @@
571F7FD01F2961FB00946648 /* IndexedDBStructuredCloneBackwardCompatibility.sqlite3-wal in Copy Resources */,
57599E2A1F071AA000A3FB8C /* IndexedDBStructuredCloneBackwardCompatibilityRead.html in Copy Resources */,
57599E2B1F071AA000A3FB8C /* IndexedDBStructuredCloneBackwardCompatibilityWrite.html in Copy Resources */,
+ CA2879DC226E69B6001026F5 /* IndexedDBSuspendImminently.html in Copy Resources */,
CA5B94D22190C0F40059FE38 /* IndexedDBTempFileSize-1.html in Copy Resources */,
CA5B94D32190C0F40059FE38 /* IndexedDBTempFileSize-2.html in Copy Resources */,
CA97B3952193667A0045DF6F /* IndexedDBUserDelete.html in Copy Resources */,
@@ -2132,6 +2135,8 @@
C9C9CD4221E6A6670019DB96 /* autoplaying-multiple-media-elements.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "autoplaying-multiple-media-elements.html"; sourceTree = "<group>"; };
C9E6DD311EA972D800DD78AA /* FirstResponderSuppression.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = FirstResponderSuppression.mm; sourceTree = "<group>"; };
C9E8EE7421DED91E00797765 /* long-test.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = "long-test.mp4"; sourceTree = "<group>"; };
+ CA2879D9226E6978001026F5 /* IndexedDBSuspendImminently.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = IndexedDBSuspendImminently.html; sourceTree = "<group>"; };
+ CA2879DA226E6986001026F5 /* IndexedDBSuspendImminently.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = IndexedDBSuspendImminently.mm; sourceTree = "<group>"; };
CA38459520AE012E00990D3B /* LocalStorageDatabaseTracker.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LocalStorageDatabaseTracker.mm; sourceTree = "<group>"; };
CA5B94D02190C0E00059FE38 /* IndexedDBTempFileSize-1.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "IndexedDBTempFileSize-1.html"; sourceTree = "<group>"; };
CA5B94D12190C0E00059FE38 /* IndexedDBTempFileSize-2.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "IndexedDBTempFileSize-2.html"; sourceTree = "<group>"; };
@@ -2597,6 +2602,7 @@
51BCEE491C84F4AF0042C82E /* IndexedDBMultiProcess.mm */,
51B1EE8D1C80F5880064FB98 /* IndexedDBPersistence.mm */,
57599E201F07191700A3FB8C /* IndexedDBStructuredCloneBackwardCompatibility.mm */,
+ CA2879DA226E6986001026F5 /* IndexedDBSuspendImminently.mm */,
CA5B94D62191005B0059FE38 /* IndexedDBTempFileSize.mm */,
CA97B3922193663B0045DF6F /* IndexedDBUserDelete.mm */,
0E404A8A2166DDF8008271BA /* InjectedBundleNodeHandleIsSelectElement.mm */,
@@ -2999,6 +3005,7 @@
571F7FCF1F2961E100946648 /* IndexedDBStructuredCloneBackwardCompatibility.sqlite3-wal */,
57599E251F07192C00A3FB8C /* IndexedDBStructuredCloneBackwardCompatibilityRead.html */,
57599E231F07192C00A3FB8C /* IndexedDBStructuredCloneBackwardCompatibilityWrite.html */,
+ CA2879D9226E6978001026F5 /* IndexedDBSuspendImminently.html */,
CA5B94D02190C0E00059FE38 /* IndexedDBTempFileSize-1.html */,
CA5B94D12190C0E00059FE38 /* IndexedDBTempFileSize-2.html */,
CA97B393219366470045DF6F /* IndexedDBUserDelete.html */,
@@ -4179,6 +4186,7 @@
7C83E0BE1D0A651300FEBCF3 /* IndexedDBMultiProcess.mm in Sources */,
7C83E0BF1D0A652200FEBCF3 /* IndexedDBPersistence.mm in Sources */,
57599E211F07191900A3FB8C /* IndexedDBStructuredCloneBackwardCompatibility.mm in Sources */,
+ CA2879DB226E69A5001026F5 /* IndexedDBSuspendImminently.mm in Sources */,
CA5B94D72191005B0059FE38 /* IndexedDBTempFileSize.mm in Sources */,
CA97B394219366600045DF6F /* IndexedDBUserDelete.mm in Sources */,
7A95BDE11E9BEC5F00865498 /* InjectedBundleAppleEvent.cpp in Sources */,
Added: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/IndexedDBSuspendImminently.html (0 => 244687)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/IndexedDBSuspendImminently.html (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/IndexedDBSuspendImminently.html 2019-04-26 16:08:40 UTC (rev 244687)
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<script>
+
+var db;
+var shouldSendSuccessMessage = false;
+var hasAbort = false;
+var request = window.indexedDB.open("IndexedDBSuspendImminently");
+request._onsuccess_ = function(event) {
+ window.webkit.messageHandlers.testHandler.postMessage("Continue");
+ db = event.target.result;
+ for (let i = 0; i < 10; i++) {
+ transaction = db.transaction("TestObjectStore", "readwrite");
+ objectStore = transaction.objectStore("TestObjectStore");
+ for (let times = 0; times < 100; times++)
+ objectStore.put(i, i);
+ transaction._onabort_ = ()=> {
+ if (!hasAbort) {
+ hasAbort = true;
+ shouldSendSuccessMessage = true;
+ window.webkit.messageHandlers.testHandler.postMessage("Expected Abort For Suspension");
+ } else {
+ window.webkit.messageHandlers.testHandler.postMessage("Unexpected Abort");
+ }
+ }
+ transaction._oncomplete_ = ()=> {
+ if (shouldSendSuccessMessage) {
+ window.webkit.messageHandlers.testHandler.postMessage("Expected Success After Resume");
+ shouldSendSuccessMessage = false;
+ }
+ }
+ }
+}
+
+request._onupgradeneeded_ = function(event) {
+ var createRequest = event.target.result.createObjectStore("TestObjectStore");
+ createRequest._onerror_ = function(event) {
+ window.webkit.messageHandlers.testHandler.postMessage("ObjectStore Error");
+ }
+}
+
+request._onerror_ = function(event) {
+ window.webkit.messageHandlers.testHandler.postMessage("Open Error");
+}
+
+</script>
Added: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/IndexedDBSuspendImminently.mm (0 => 244687)
--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/IndexedDBSuspendImminently.mm (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/IndexedDBSuspendImminently.mm 2019-04-26 16:08:40 UTC (rev 244687)
@@ -0,0 +1,97 @@
+/*
+* Copyright (C) 2019 Apple Inc. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and/or other materials provided with the distribution.
+*
+* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+* THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#import "config.h"
+
+#import "PlatformUtilities.h"
+#import "Test.h"
+
+#import <WebKit/WKProcessPoolPrivate.h>
+#import <WebKit/WKUserContentControllerPrivate.h>
+#import <WebKit/WKWebViewConfigurationPrivate.h>
+#import <WebKit/WebKit.h>
+#import <WebKit/_WKProcessPoolConfiguration.h>
+#import <wtf/RetainPtr.h>
+
+#if PLATFORM(IOS_FAMILY)
+
+static bool receivedScriptMessage;
+static bool idbAcitivitiesStarted;
+static RetainPtr<WKScriptMessage> lastScriptMessage;
+
+@interface IndexedDBSuspendImminentlyMessageHandler : NSObject <WKScriptMessageHandler>
+@end
+
+@implementation IndexedDBSuspendImminentlyMessageHandler
+
+- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
+{
+ idbAcitivitiesStarted = true;
+ receivedScriptMessage = true;
+ lastScriptMessage = message;
+}
+
+@end
+
+static void runTestAndCheckResult(NSString* expectedResult)
+{
+ receivedScriptMessage = false;
+ TestWebKitAPI::Util::run(&receivedScriptMessage);
+ RetainPtr<NSString> string = (NSString *)[lastScriptMessage body];
+ EXPECT_WK_STREQ(expectedResult, string.get());
+}
+
+static void keepNetworkProcessActive()
+{
+ [[WKWebsiteDataStore defaultDataStore] fetchDataRecordsOfTypes:[WKWebsiteDataStore allWebsiteDataTypes] completionHandler:^(NSArray<WKWebsiteDataRecord *> *dataRecords) {
+ if (!idbAcitivitiesStarted)
+ keepNetworkProcessActive();
+ }];
+}
+
+TEST(IndexedDB, IndexedDBSuspendImminently)
+{
+ auto handler = adoptNS([[IndexedDBSuspendImminentlyMessageHandler alloc] init]);
+ auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+ [[configuration userContentController] addScriptMessageHandler:handler.get() name:@"testHandler"];
+
+ auto webView = adoptNS([[WKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
+
+ NSURLRequest *request = [NSURLRequest requestWithURL:[[NSBundle mainBundle] URLForResource:@"IndexedDBSuspendImminently" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"]];
+ idbAcitivitiesStarted = false;
+ [webView loadRequest:request];
+
+ keepNetworkProcessActive();
+
+ runTestAndCheckResult(@"Continue");
+
+ [configuration.get().processPool _sendNetworkProcessWillSuspendImminently];
+ [configuration.get().processPool _sendNetworkProcessDidResume];
+
+ runTestAndCheckResult(@"Expected Abort For Suspension");
+ runTestAndCheckResult(@"Expected Success After Resume");
+}
+
+#endif // PLATFORM(IOS_FAMILY)