Title: [244687] trunk
Revision
244687
Author
[email protected]
Date
2019-04-26 09:08:40 -0700 (Fri, 26 Apr 2019)

Log Message

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.

Source/WebCore:

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:

Source/WebKit:

* NetworkProcess/NetworkProcess.cpp:
(WebKit::NetworkProcess::processWillSuspendImminently):
(WebKit::NetworkProcess::prepareToSuspend):
(WebKit::NetworkProcess::resume):

Source/WTF:

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:

Tools:

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKitCocoa/IndexedDBSuspendImminently.html: Added.
* TestWebKitAPI/Tests/WebKitCocoa/IndexedDBSuspendImminently.mm: Added.
(-[IndexedDBSuspendImminentlyMessageHandler userContentController:didReceiveScriptMessage:]):
(runTestAndCheckResult):
(keepNetworkProcessActive):
(TEST):

Modified Paths

Added Paths

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)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to