WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
Patch
bug-195302-20190311165226.patch (text/plain), 73.15 KB, created by
youenn fablet
on 2019-03-11 16:52:27 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
youenn fablet
Created:
2019-03-11 16:52:27 PDT
Size:
73.15 KB
patch
obsolete
>Subversion Revision: 242706 >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index b89ea29be6661c975281f83ed07703ccad0cdcf1..b9e4bd17230067970200c98977ffd12470e29e7b 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,110 @@ >+2019-03-11 Youenn Fablet <youenn@apple.com> >+ >+ Check IDB quota usage through QuotaManager >+ https://bugs.webkit.org/show_bug.cgi?id=195302 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ For every write operation, compute an estimate size and check for quota before proceeding. >+ When proceeding, store the estimate size in a map. >+ If size of the database is to be computed when the task is not done, >+ the estimate size will be added to the current size of the databases. >+ At the end of the task, the estimate size is removed from the map, >+ and the databases size is refreshed. >+ >+ This patch implements size estimation for write tasks. >+ Put/add operations might overestimate the size >+ when an old value will be replaced by a new value. >+ In that case, we do not substract the old value size since we do not know it. >+ >+ This patch implements database opening by adding a fixed small cost, >+ as we do not know whether the database is new or not. >+ >+ For the first IDB request, we have not computed the size of the database. >+ To do so, we need to go to a background thread and do that file size computation. >+ For that purpose, we add support for being-initialized quota user. >+ Quota manager is calling whenInitialized on its quota user and will >+ delay any quota check requests until its quota user is answering this callback. >+ >+ For in process IDB, use the default storage quota per origin and do not increase it. >+ Future work should move it to NetworkProcess and implement some quota checking. >+ >+ Cache API and IDB quota management are not yet fully unified. >+ If IDB is used on start-up, we should check for Cache API storage size. >+ Conversely, on Cache API first wite task, even if IDB is not being used, >+ we should compute the size of the IDB data for the given origin. >+ >+ Test: http/tests/IndexedDB/storage-limit.https.html >+ >+ * Modules/indexeddb/server/IDBBackingStore.h: >+ * Modules/indexeddb/server/IDBServer.cpp: >+ (WebCore::IDBServer::IDBServer::create): >+ (WebCore::IDBServer::IDBServer::IDBServer): >+ (WebCore::IDBServer::m_quotaManagerGetter): >+ (WebCore::IDBServer::IDBServer::QuotaUser::QuotaUser): >+ (WebCore::IDBServer::IDBServer::QuotaUser::~QuotaUser): >+ (WebCore::IDBServer::IDBServer::QuotaUser::clearSpaceUsed): >+ (WebCore::IDBServer::IDBServer::QuotaUser::whenInitialized): >+ (WebCore::IDBServer::IDBServer::QuotaUser::initializeSpaceUsed): >+ (WebCore::IDBServer::IDBServer::quotaUser): >+ (WebCore::IDBServer::IDBServer::startComputingSpaceUsedForOrigin): >+ (WebCore::IDBServer::IDBServer::computeSpaceUsedForOrigin): >+ (WebCore::IDBServer::IDBServer::finishComputingSpaceUsedForOrigin): >+ (WebCore::IDBServer::IDBServer::requestSpace): >+ (WebCore::IDBServer::IDBServer::clearSpaceUsed): >+ (WebCore::IDBServer::IDBServer::setSpaceUsed): >+ (WebCore::IDBServer::IDBServer::increasePotentialSpaceUsed): >+ (WebCore::IDBServer::IDBServer::decreasePotentialSpaceUsed): >+ * Modules/indexeddb/server/IDBServer.h: >+ (WebCore::IDBServer::IDBServer::create): >+ * Modules/indexeddb/server/MemoryIDBBackingStore.cpp: >+ (WebCore::IDBServer::MemoryIDBBackingStore::databasesSizeForOrigin const): >+ * Modules/indexeddb/server/MemoryIDBBackingStore.h: >+ * Modules/indexeddb/server/SQLiteIDBBackingStore.cpp: >+ (WebCore::IDBServer::SQLiteIDBBackingStore::databasesSizeForFolder): >+ (WebCore::IDBServer::SQLiteIDBBackingStore::databasesSizeForOrigin const): >+ (WebCore::IDBServer::SQLiteIDBBackingStore::maximumSize const): >+ * Modules/indexeddb/server/SQLiteIDBBackingStore.h: >+ * Modules/indexeddb/server/UniqueIDBDatabase.cpp: >+ (WebCore::IDBServer::estimateSize): >+ (WebCore::IDBServer::UniqueIDBDatabase::UniqueIDBDatabase): >+ (WebCore::IDBServer::quotaErrorMessageName): >+ (WebCore::IDBServer::UniqueIDBDatabase::requestSpace): >+ (WebCore::IDBServer::UniqueIDBDatabase::performCurrentOpenOperation): >+ (WebCore::IDBServer::UniqueIDBDatabase::storeCallbackOrFireError): >+ (WebCore::IDBServer::UniqueIDBDatabase::createObjectStore): >+ (WebCore::IDBServer::UniqueIDBDatabase::createObjectStoreAfterQuotaCheck): >+ (WebCore::IDBServer::UniqueIDBDatabase::renameObjectStore): >+ (WebCore::IDBServer::UniqueIDBDatabase::renameObjectStoreAfterQuotaCheck): >+ (WebCore::IDBServer::UniqueIDBDatabase::createIndex): >+ (WebCore::IDBServer::UniqueIDBDatabase::createIndexAfterQuotaCheck): >+ (WebCore::IDBServer::UniqueIDBDatabase::renameIndex): >+ (WebCore::IDBServer::UniqueIDBDatabase::renameIndexAfterQuotaCheck): >+ (WebCore::IDBServer::UniqueIDBDatabase::putOrAdd): >+ (WebCore::IDBServer::UniqueIDBDatabase::putOrAddAfterQuotaCheck): >+ (WebCore::IDBServer::UniqueIDBDatabase::postDatabaseTaskReply): >+ (WebCore::IDBServer::UniqueIDBDatabase::immediateCloseForUserDelete): >+ (WebCore::IDBServer::UniqueIDBDatabase::updateSpaceUsedIfNeeded): >+ (WebCore::IDBServer::UniqueIDBDatabase::performErrorCallback): >+ (WebCore::IDBServer::UniqueIDBDatabase::performKeyDataCallback): >+ * Modules/indexeddb/server/UniqueIDBDatabase.h: >+ (WebCore::IDBServer::UniqueIDBDatabase::server): >+ * Modules/indexeddb/shared/InProcessIDBServer.cpp: >+ (WebCore::InProcessIDBServer::create): >+ (WebCore::InProcessIDBServer::quotaManager): >+ (WebCore::storageQuotaManagerGetter): >+ (WebCore::InProcessIDBServer::InProcessIDBServer): >+ * Modules/indexeddb/shared/InProcessIDBServer.h: >+ * loader/EmptyClients.cpp: >+ * storage/StorageQuotaManager.cpp: >+ (WebCore::StorageQuotaManager::addUser): >+ (WebCore::StorageQuotaManager::requestSpace): >+ * storage/StorageQuotaManager.h: >+ (WebCore::StorageQuotaManager::defaultQuota): >+ (WebCore::StorageQuotaManager::removeUser): >+ * storage/StorageQuotaUser.h: >+ (WebCore::StorageQuotaUser::whenInitialized): >+ > 2019-03-11 Youenn Fablet <youenn@apple.com> > > Make IDBDatabaseIdentifier take a ClientOrigin as member >diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog >index fbca452a4dc820a4bf2a56f17ae91a002b22ed8e..b7b20cd15ff8e2a49da73d4250c9f641869a2f01 100644 >--- a/Source/WebKit/ChangeLog >+++ b/Source/WebKit/ChangeLog >@@ -1,3 +1,19 @@ >+2019-03-11 Youenn Fablet <youenn@apple.com> >+ >+ Check IDB quota usage through QuotaManager >+ https://bugs.webkit.org/show_bug.cgi?id=195302 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Set the quota manager getter for IDBServer at creation time. >+ >+ * NetworkProcess/NetworkProcess.cpp: >+ (WebKit::NetworkProcess::createIDBServer): >+ (WebKit::NetworkProcess::idbServer): >+ * NetworkProcess/NetworkProcess.h: >+ * WebProcess/Databases/WebDatabaseProvider.cpp: >+ (WebKit::WebDatabaseProvider::idbConnectionToServerForSession): >+ > 2019-03-11 Truitt Savell <tsavell@apple.com> > > Unreviewed, rolling out r242697. >diff --git a/Source/WebKitLegacy/ChangeLog b/Source/WebKitLegacy/ChangeLog >index de8f0f8642f72a9d54f79d94629ffb6b0da44720..50e4ea21bf8d4eae229c1961f2a671b3ce431282 100644 >--- a/Source/WebKitLegacy/ChangeLog >+++ b/Source/WebKitLegacy/ChangeLog >@@ -1,3 +1,13 @@ >+2019-03-11 Youenn Fablet <youenn@apple.com> >+ >+ Check IDB quota usage through QuotaManager >+ https://bugs.webkit.org/show_bug.cgi?id=195302 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * Storage/WebDatabaseProvider.cpp: >+ (WebDatabaseProvider::idbConnectionToServerForSession): >+ > 2019-03-06 Sam Weinig <sam@webkit.org> > > WebKitLegacy does not need to generate an export file for i386 anymore >diff --git a/Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h b/Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h >index 4f61c72518db19dc8b48b376a6035ea71d60bd96..c6d7d43f9ebadd292480cf4eac4078751009a401 100644 >--- a/Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h >+++ b/Source/WebCore/Modules/indexeddb/server/IDBBackingStore.h >@@ -99,6 +99,7 @@ public: > virtual bool supportsSimultaneousTransactions() = 0; > virtual bool isEphemeral() = 0; > >+ virtual uint64_t databasesSizeForOrigin() const = 0; > virtual void setQuota(uint64_t) = 0; > protected: > IDBBackingStore() { RELEASE_ASSERT(!isMainThread()); } >diff --git a/Source/WebCore/Modules/indexeddb/server/IDBServer.cpp b/Source/WebCore/Modules/indexeddb/server/IDBServer.cpp >index 2da4f1638edfeae402abce7af8ca3e85bba4b78e..2c7149cf9e54fe9a5c5d8b832aaeaeb77491368e 100644 >--- a/Source/WebCore/Modules/indexeddb/server/IDBServer.cpp >+++ b/Source/WebCore/Modules/indexeddb/server/IDBServer.cpp >@@ -35,6 +35,7 @@ > #include "SQLiteFileSystem.h" > #include "SQLiteIDBBackingStore.h" > #include "SecurityOrigin.h" >+#include "StorageQuotaManager.h" > #include <wtf/CrossThreadCopier.h> > #include <wtf/Locker.h> > #include <wtf/MainThread.h> >@@ -42,30 +43,34 @@ > namespace WebCore { > namespace IDBServer { > >-Ref<IDBServer> IDBServer::create(IDBBackingStoreTemporaryFileHandler& fileHandler, WTF::Function<void(bool)>&& isClosingDatabaseCallback) >+Ref<IDBServer> IDBServer::create(PAL::SessionID sessionID, IDBBackingStoreTemporaryFileHandler& fileHandler, QuotaManagerGetter&& quotaManagerGetter, WTF::Function<void(bool)>&& isClosingDatabaseCallback) > { >- return adoptRef(*new IDBServer(fileHandler, WTFMove(isClosingDatabaseCallback))); >+ return adoptRef(*new IDBServer(sessionID, fileHandler, WTFMove(quotaManagerGetter), WTFMove(isClosingDatabaseCallback))); > } > >-Ref<IDBServer> IDBServer::create(const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler& fileHandler, WTF::Function<void(bool)>&& isClosingDatabaseCallback) >+Ref<IDBServer> IDBServer::create(PAL::SessionID sessionID, const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler& fileHandler, QuotaManagerGetter&& quotaManagerGetter, WTF::Function<void(bool)>&& isClosingDatabaseCallback) > { >- return adoptRef(*new IDBServer(databaseDirectoryPath, fileHandler, WTFMove(isClosingDatabaseCallback))); >+ return adoptRef(*new IDBServer(sessionID, databaseDirectoryPath, fileHandler, WTFMove(quotaManagerGetter), WTFMove(isClosingDatabaseCallback))); > } > >-IDBServer::IDBServer(IDBBackingStoreTemporaryFileHandler& fileHandler, WTF::Function<void(bool)>&& isClosingDatabaseCallback) >+IDBServer::IDBServer(PAL::SessionID sessionID, IDBBackingStoreTemporaryFileHandler& fileHandler, QuotaManagerGetter&& quotaManagerGetter, WTF::Function<void(bool)>&& isClosingDatabaseCallback) > : CrossThreadTaskHandler("IndexedDatabase Server") >+ , m_sessionID(sessionID) > , m_backingStoreTemporaryFileHandler(fileHandler) > , m_isClosingDatabaseCallback(WTFMove(isClosingDatabaseCallback)) > , m_isClosingDatabaseHysteresis([&](PAL::HysteresisState state) { m_isClosingDatabaseCallback(state == PAL::HysteresisState::Started); }) >+ , m_quotaManagerGetter(WTFMove(quotaManagerGetter)) > { > } > >-IDBServer::IDBServer(const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler& fileHandler, WTF::Function<void(bool)>&& isClosingDatabaseCallback) >+IDBServer::IDBServer(PAL::SessionID sessionID, const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler& fileHandler, QuotaManagerGetter&& quotaManagerGetter, WTF::Function<void(bool)>&& isClosingDatabaseCallback) > : CrossThreadTaskHandler("IndexedDatabase Server") >+ , m_sessionID(sessionID) > , m_databaseDirectoryPath(databaseDirectoryPath) > , m_backingStoreTemporaryFileHandler(fileHandler) > , m_isClosingDatabaseCallback(WTFMove(isClosingDatabaseCallback)) > , m_isClosingDatabaseHysteresis([&](PAL::HysteresisState state) { m_isClosingDatabaseCallback(state == PAL::HysteresisState::Started); }) >+ , m_quotaManagerGetter(WTFMove(quotaManagerGetter)) > { > LOG(IndexedDB, "IDBServer created at path %s", databaseDirectoryPath.utf8().data()); > } >@@ -679,6 +684,127 @@ void IDBServer::didCloseDatabase(UniqueIDBDatabase* database) > } > } > >+IDBServer::QuotaUser::QuotaUser(IDBServer& server, StorageQuotaManager* manager, ClientOrigin&& origin) >+ : m_server(server) >+ , m_manager(makeWeakPtr(manager)) >+ , m_origin(WTFMove(origin)) >+ , m_isInitialized(m_server.m_sessionID.isEphemeral()) >+{ >+ if (manager) >+ manager->addUser(*this); >+} >+ >+IDBServer::QuotaUser::~QuotaUser() >+{ >+ if (m_manager) >+ m_manager->removeUser(*this); >+} >+ >+void IDBServer::QuotaUser::clearSpaceUsed() >+{ >+ m_spaceUsed = 0; >+ m_estimatedSpaceIncrease = 0; >+ >+ if (!m_manager) >+ return; >+ >+ if (m_server.m_sessionID.isEphemeral()) >+ return; >+ >+ if (!m_isInitialized) >+ return; >+ >+ ASSERT(!m_initializationCallback); >+ >+ m_isInitialized = false; >+ >+ // Do add/remove to trigger call to whenInitialized. >+ m_manager->removeUser(*this); >+ m_manager->addUser(*this); >+} >+ >+void IDBServer::QuotaUser::whenInitialized(CompletionHandler<void()>&& callback) >+{ >+ if (m_isInitialized) { >+ callback(); >+ return; >+ } >+ m_initializationCallback = WTFMove(callback); >+ m_server.startComputingSpaceUsedForOrigin(m_origin); >+} >+ >+void IDBServer::QuotaUser::initializeSpaceUsed(uint64_t spaceUsed) >+{ >+ ASSERT(m_isInitialized || !m_estimatedSpaceIncrease); >+ m_spaceUsed = spaceUsed; >+ m_isInitialized = true; >+ >+ if (auto callback = WTFMove(m_initializationCallback)) >+ callback(); >+} >+ >+IDBServer::QuotaUser& IDBServer::quotaUser(const ClientOrigin& origin) >+{ >+ return *m_quotaUsers.ensure(origin, [this, &origin] { >+ return std::make_unique<QuotaUser>(*this, m_quotaManagerGetter(m_sessionID, origin), ClientOrigin { origin }); >+ }).iterator->value; >+} >+ >+void IDBServer::startComputingSpaceUsedForOrigin(const ClientOrigin& origin) >+{ >+ ASSERT(!m_sessionID.isEphemeral()); >+ postDatabaseTask(createCrossThreadTask(*this, &IDBServer::computeSpaceUsedForOrigin, origin)); >+} >+ >+void IDBServer::computeSpaceUsedForOrigin(const ClientOrigin& origin) >+{ >+ ASSERT(!isMainThread()); >+ >+ auto path = IDBDatabaseIdentifier::databaseDirectoryRelativeToRoot(origin.topOrigin, origin.clientOrigin, m_databaseDirectoryPath); >+ auto size = SQLiteIDBBackingStore::databasesSizeForFolder(path); >+ >+ postDatabaseTaskReply(createCrossThreadTask(*this, &IDBServer::finishComputingSpaceUsedForOrigin, origin, size)); >+} >+ >+void IDBServer::finishComputingSpaceUsedForOrigin(const ClientOrigin& origin, uint64_t spaceUsed) >+{ >+ quotaUser(origin).initializeSpaceUsed(spaceUsed); >+} >+ >+void IDBServer::requestSpace(const ClientOrigin& origin, uint64_t taskSize, CompletionHandler<void(StorageQuotaManager::Decision)>&& callback) >+{ >+ auto* quotaManager = quotaUser(origin).manager(); >+ if (!quotaManager) { >+ callback(StorageQuotaManager::Decision::Deny); >+ return; >+ } >+ >+ quotaManager->requestSpace(taskSize, [callback = WTFMove(callback)](auto decision) mutable { >+ callback(decision); >+ }); >+} >+ >+void IDBServer::clearSpaceUsed(const ClientOrigin& origin) >+{ >+ if (auto* user = m_quotaUsers.get(origin)) >+ user->clearSpaceUsed(); >+} >+ >+void IDBServer::setSpaceUsed(const ClientOrigin& origin, uint64_t taskSize) >+{ >+ quotaUser(origin).setSpaceUsed(taskSize); >+} >+ >+void IDBServer::increasePotentialSpaceUsed(const ClientOrigin& origin, uint64_t taskSize) >+{ >+ quotaUser(origin).increasePotentialSpaceUsed(taskSize); >+} >+ >+void IDBServer::decreasePotentialSpaceUsed(const ClientOrigin& origin, uint64_t spaceUsed) >+{ >+ quotaUser(origin).decreasePotentialSpaceUsed(spaceUsed); >+} >+ > } // namespace IDBServer > } // namespace WebCore > >diff --git a/Source/WebCore/Modules/indexeddb/server/IDBServer.h b/Source/WebCore/Modules/indexeddb/server/IDBServer.h >index f6f55d3723e4c2258a742d7592930e9f8acd035f..6662db7e300f2261358cdbe45bd6c1bfd4019fb8 100644 >--- a/Source/WebCore/Modules/indexeddb/server/IDBServer.h >+++ b/Source/WebCore/Modules/indexeddb/server/IDBServer.h >@@ -29,9 +29,12 @@ > > #include "IDBConnectionToClient.h" > #include "IDBDatabaseIdentifier.h" >+#include "StorageQuotaManager.h" >+#include "StorageQuotaUser.h" > #include "UniqueIDBDatabase.h" > #include "UniqueIDBDatabaseConnection.h" > #include <pal/HysteresisActivity.h> >+#include <pal/SessionID.h> > #include <wtf/CrossThreadTaskHandler.h> > #include <wtf/HashMap.h> > #include <wtf/Lock.h> >@@ -44,6 +47,7 @@ namespace WebCore { > class IDBCursorInfo; > class IDBRequestData; > class IDBValue; >+class StorageQuotaManager; > > struct IDBGetRecordData; > >@@ -55,8 +59,9 @@ class IDBBackingStoreTemporaryFileHandler; > > class IDBServer : public RefCounted<IDBServer>, public CrossThreadTaskHandler { > public: >- static Ref<IDBServer> create(IDBBackingStoreTemporaryFileHandler&, WTF::Function<void(bool)>&& isClosingDatabaseCallback = [](bool) { }); >- WEBCORE_EXPORT static Ref<IDBServer> create(const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler&, WTF::Function<void(bool)>&& isClosingDatabaseCallback = [](bool) { }); >+ using QuotaManagerGetter = WTF::Function<StorageQuotaManager*(PAL::SessionID, const ClientOrigin&)>; >+ static Ref<IDBServer> create(PAL::SessionID, IDBBackingStoreTemporaryFileHandler&, QuotaManagerGetter&&, WTF::Function<void(bool)>&& isClosingDatabaseCallback = [](bool) { }); >+ WEBCORE_EXPORT static Ref<IDBServer> create(PAL::SessionID, const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler&, QuotaManagerGetter&&, WTF::Function<void(bool)>&& isClosingDatabaseCallback = [](bool) { }); > > WEBCORE_EXPORT void registerConnection(IDBConnectionToClient&); > WEBCORE_EXPORT void unregisterConnection(IDBConnectionToClient&); >@@ -114,9 +119,15 @@ public: > void didCloseDatabase(UniqueIDBDatabase*); > void hysteresisUpdated(PAL::HysteresisState); > >+ void requestSpace(const ClientOrigin&, uint64_t taskSize, CompletionHandler<void(StorageQuotaManager::Decision)>&&); >+ void increasePotentialSpaceUsed(const ClientOrigin&, uint64_t taskSize); >+ void decreasePotentialSpaceUsed(const ClientOrigin&, uint64_t taskSize); >+ void setSpaceUsed(const ClientOrigin&, uint64_t spaceUsed); >+ void clearSpaceUsed(const ClientOrigin&); >+ > private: >- IDBServer(IDBBackingStoreTemporaryFileHandler&, WTF::Function<void(bool)>&&); >- IDBServer(const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler&, WTF::Function<void(bool)>&&); >+ IDBServer(PAL::SessionID, IDBBackingStoreTemporaryFileHandler&, QuotaManagerGetter&&, WTF::Function<void(bool)>&&); >+ IDBServer(PAL::SessionID, const String& databaseDirectoryPath, IDBBackingStoreTemporaryFileHandler&, QuotaManagerGetter&&, WTF::Function<void(bool)>&&); > > UniqueIDBDatabase& getOrCreateUniqueIDBDatabase(const IDBDatabaseIdentifier&); > >@@ -127,6 +138,45 @@ private: > void performCloseAndDeleteDatabasesForOrigins(const Vector<SecurityOriginData>&, uint64_t callbackID); > void didPerformCloseAndDeleteDatabases(uint64_t callbackID); > >+ class QuotaUser final : public StorageQuotaUser { >+ WTF_MAKE_FAST_ALLOCATED; >+ public: >+ QuotaUser(IDBServer&, StorageQuotaManager*, ClientOrigin&&); >+ ~QuotaUser(); >+ >+ StorageQuotaManager* manager() { return m_manager.get(); } >+ >+ void setSpaceUsed(uint64_t spaceUsed) { m_spaceUsed = spaceUsed; } >+ void clearSpaceUsed(); >+ >+ void increasePotentialSpaceUsed(uint64_t increase) { m_estimatedSpaceIncrease += increase; } >+ void decreasePotentialSpaceUsed(uint64_t decrease) >+ { >+ ASSERT(m_estimatedSpaceIncrease >= decrease); >+ m_estimatedSpaceIncrease -= decrease; >+ } >+ >+ void initializeSpaceUsed(uint64_t spaceUsed); >+ >+ private: >+ uint64_t spaceUsed() const final { return m_spaceUsed + m_estimatedSpaceIncrease; } >+ void whenInitialized(CompletionHandler<void()>&&) final; >+ >+ IDBServer& m_server; >+ WeakPtr<StorageQuotaManager> m_manager; >+ ClientOrigin m_origin; >+ bool m_isInitialized { false }; >+ uint64_t m_spaceUsed { 0 }; >+ uint64_t m_estimatedSpaceIncrease { 0 }; >+ CompletionHandler<void()> m_initializationCallback; >+ }; >+ >+ QuotaUser& quotaUser(const ClientOrigin&); >+ void startComputingSpaceUsedForOrigin(const ClientOrigin&); >+ void computeSpaceUsedForOrigin(const ClientOrigin&); >+ void finishComputingSpaceUsedForOrigin(const ClientOrigin&, uint64_t spaceUsed); >+ >+ PAL::SessionID m_sessionID; > HashMap<uint64_t, RefPtr<IDBConnectionToClient>> m_connectionMap; > HashMap<IDBDatabaseIdentifier, std::unique_ptr<UniqueIDBDatabase>> m_uniqueIDBDatabaseMap; > HashSet<UniqueIDBDatabase*> m_uniqueIDBDatabasesInClose; >@@ -143,6 +193,9 @@ private: > > WTF::Function<void(bool)> m_isClosingDatabaseCallback; > PAL::HysteresisActivity m_isClosingDatabaseHysteresis; >+ >+ HashMap<ClientOrigin, std::unique_ptr<QuotaUser>> m_quotaUsers; >+ QuotaManagerGetter m_quotaManagerGetter; > }; > > } // namespace IDBServer >diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.cpp b/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.cpp >index 953cace22a0b28796b2800c80d23b94507cec06a..f6d1658bcdd6d52353468616c6e307f6202851ce 100644 >--- a/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.cpp >+++ b/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.cpp >@@ -592,6 +592,12 @@ void MemoryIDBBackingStore::deleteBackingStore() > // The in-memory IDB backing store doesn't need to do any cleanup when it is deleted. > } > >+uint64_t MemoryIDBBackingStore::databasesSizeForOrigin() const >+{ >+ // FIXME: Implement this. >+ return 0; >+} >+ > } // namespace IDBServer > } // namespace WebCore > >diff --git a/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.h b/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.h >index 7806730ddef32c9c9cd5ea590bd0fbc6318a1cad..64e6cc7de0ca6767ec18d514bcaaf4bd8a562c7f 100644 >--- a/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.h >+++ b/Source/WebCore/Modules/indexeddb/server/MemoryIDBBackingStore.h >@@ -80,6 +80,7 @@ public: > bool isEphemeral() final { return true; } > > void setQuota(uint64_t quota) final { UNUSED_PARAM(quota); }; >+ uint64_t databasesSizeForOrigin() const final; > > void removeObjectStoreForVersionChangeAbort(MemoryObjectStore&); > void restoreObjectStoreForVersionChangeAbort(Ref<MemoryObjectStore>&&); >diff --git a/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp >index 7415ec818a54a822fb9e380b19d4882903480aea..5989cb3e6e344cdebb4d5ee8d2fc342743ff40a7 100644 >--- a/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp >+++ b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.cpp >@@ -850,6 +850,21 @@ uint64_t SQLiteIDBBackingStore::quotaForOrigin() const > return std::min(diskFreeSpaceSize / 2, m_quota); > } > >+uint64_t SQLiteIDBBackingStore::databasesSizeForFolder(const String& folder) >+{ >+ uint64_t diskUsage = 0; >+ for (auto& directory : FileSystem::listDirectory(folder, "*")) { >+ for (auto& file : FileSystem::listDirectory(directory, "*.sqlite3"_s)) >+ diskUsage += SQLiteFileSystem::getDatabaseFileSize(file); >+ } >+ return diskUsage; >+} >+ >+uint64_t SQLiteIDBBackingStore::databasesSizeForOrigin() const >+{ >+ return databasesSizeForFolder(m_absoluteDatabaseDirectory); >+} >+ > uint64_t SQLiteIDBBackingStore::maximumSize() const > { > ASSERT(!isMainThread()); >@@ -859,11 +874,7 @@ uint64_t SQLiteIDBBackingStore::maximumSize() const > uint64_t databaseFileSize = SQLiteFileSystem::getDatabaseFileSize(fullDatabasePath()); > uint64_t quota = quotaForOrigin(); > >- uint64_t diskUsage = 0; >- for (auto& directory : FileSystem::listDirectory(m_absoluteDatabaseDirectory, "*")) { >- for (auto& file : FileSystem::listDirectory(directory, "*.sqlite3"_s)) >- diskUsage += SQLiteFileSystem::getDatabaseFileSize(file); >- } >+ uint64_t diskUsage = databasesSizeForOrigin(); > ASSERT(diskUsage >= databaseFileSize); > > if (quota < diskUsage) >diff --git a/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h >index 14004b309910ab5a20e6396f391ec3b48f04a1ef..4f974ba80173915d0f607b0d2ce1ded00ef803d1 100644 >--- a/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h >+++ b/Source/WebCore/Modules/indexeddb/server/SQLiteIDBBackingStore.h >@@ -82,6 +82,7 @@ public: > void deleteBackingStore() final; > > void setQuota(uint64_t quota) final { m_quota = quota; } >+ uint64_t databasesSizeForOrigin() const final; > > bool supportsSimultaneousTransactions() final { return false; } > bool isEphemeral() final { return false; } >@@ -95,6 +96,7 @@ public: > IDBError getBlobRecordsForObjectStoreRecord(int64_t objectStoreRecord, Vector<String>& blobURLs, PAL::SessionID&, Vector<String>& blobFilePaths); > > static String databaseNameFromEncodedFilename(const String&); >+ static uint64_t databasesSizeForFolder(const String& folder); > > private: > String filenameForDatabaseName() const; >diff --git a/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp >index 65a092b4e2744ac7dbc833639d4e6794a9d7f84d..0e55ab8f1dff9ab32d5070991414c2a08f692bc4 100644 >--- a/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp >+++ b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.cpp >@@ -41,6 +41,7 @@ > #include "IDBValue.h" > #include "Logging.h" > #include "SerializedScriptValue.h" >+#include "StorageQuotaManager.h" > #include "UniqueIDBDatabaseConnection.h" > #include <JavaScriptCore/AuxiliaryBarrierInlines.h> > #include <JavaScriptCore/HeapInlines.h> >@@ -54,8 +55,59 @@ namespace WebCore { > using namespace JSC; > namespace IDBServer { > >+static const uint64_t defaultWriteOperationCost = 4; >+ >+static inline uint64_t estimateSize(const IDBKeyData& keyData) >+{ >+ uint64_t size = 4; >+ switch (keyData.type()) { >+ case IndexedDB::KeyType::String: >+ size += keyData.string().sizeInBytes(); >+ break; >+ case IndexedDB::KeyType::Binary: { >+ size += keyData.binary().size(); >+ break; >+ } >+ case IndexedDB::KeyType::Array: >+ for (auto& data : keyData.array()) >+ size += estimateSize(data); >+ break; >+ default: >+ break; >+ } >+ return size; >+} >+ >+static inline uint64_t estimateSize(const IDBValue& value) >+{ >+ uint64_t size = 4; >+ size += value.data().size(); >+ for (auto& url : value.blobURLs()) >+ size += url.sizeInBytes(); >+ for (auto& path : value.blobFilePaths()) >+ size += path.sizeInBytes(); >+ return size; >+} >+ >+static inline uint64_t estimateSize(const IDBIndexInfo& info) >+{ >+ uint64_t size = 4; >+ size += info.name().sizeInBytes(); >+ return size; >+} >+ >+static inline uint64_t estimateSize(const IDBObjectStoreInfo& info) >+{ >+ uint64_t size = 4; >+ size += info.name().sizeInBytes(); >+ // FIXME: estimate keyPath. >+ for (auto& indexInfo : info.indexMap().values()) >+ size += estimateSize(indexInfo); >+ return size; >+} >+ > UniqueIDBDatabase::UniqueIDBDatabase(IDBServer& server, const IDBDatabaseIdentifier& identifier) >- : m_server(&server) >+ : m_server(server) > , m_identifier(identifier) > , m_operationAndTransactionTimer(*this, &UniqueIDBDatabase::operationAndTransactionTimerFired) > { >@@ -118,6 +170,28 @@ bool UniqueIDBDatabase::isVersionChangeInProgress() > return m_versionChangeDatabaseConnection; > } > >+static inline String quotaErrorMessageName(const char* taskName) >+{ >+ return makeString("Failed to ", taskName, " in database because not enough space for domain"); >+} >+ >+void UniqueIDBDatabase::requestSpace(uint64_t taskSize, const char* taskName, CompletionHandler<void(Optional<IDBError>&&)>&& callback) >+{ >+ m_server->requestSpace(m_identifier.origin(), taskSize, [weakThis = makeWeakPtr(this), taskName, callback = WTFMove(callback)](auto decision) mutable { >+ if (!weakThis) { >+ callback(IDBError { UnknownError }); >+ return; >+ } >+ switch (decision) { >+ case StorageQuotaManager::Decision::Deny: >+ callback(IDBError { QuotaExceededError, quotaErrorMessageName(taskName) }); >+ return; >+ case StorageQuotaManager::Decision::Grant: >+ callback({ }); >+ }; >+ }); >+} >+ > void UniqueIDBDatabase::performCurrentOpenOperation() > { > LOG(IndexedDB, "(main) UniqueIDBDatabase::performCurrentOpenOperation (%p)", this); >@@ -128,9 +202,24 @@ void UniqueIDBDatabase::performCurrentOpenOperation() > if (!m_databaseInfo) { > if (!m_isOpeningBackingStore) { > m_isOpeningBackingStore = true; >- postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::openBackingStore, m_identifier)); >+ // We do not know whether this is an existing or a new database. >+ // We set a small cost so that it is not possible to open an infinite number of database. >+ m_server->requestSpace(m_identifier.origin(), defaultWriteOperationCost, [this, weakThis = makeWeakPtr(this)](auto decision) mutable { >+ if (!weakThis) >+ return; >+ >+ switch (decision) { >+ case StorageQuotaManager::Decision::Deny: { >+ auto result = IDBResultData::error(m_currentOpenDBRequest->requestData().requestIdentifier(), IDBError { QuotaExceededError, quotaErrorMessageName("openDatabase") }); >+ m_currentOpenDBRequest->connection().didOpenDatabase(result); >+ m_currentOpenDBRequest = nullptr; >+ break; >+ } >+ case StorageQuotaManager::Decision::Grant: >+ this->postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::openBackingStore, m_identifier)); >+ }; >+ }); > } >- > return; > } > >@@ -427,7 +516,7 @@ static uint64_t generateUniqueCallbackIdentifier() > return ++currentID; > } > >-uint64_t UniqueIDBDatabase::storeCallbackOrFireError(ErrorCallback&& callback) >+uint64_t UniqueIDBDatabase::storeCallbackOrFireError(ErrorCallback&& callback, uint64_t taskSize) > { > if (m_hardClosedForUserDelete) { > callback(IDBError::userDeleteError()); >@@ -437,11 +526,17 @@ uint64_t UniqueIDBDatabase::storeCallbackOrFireError(ErrorCallback&& callback) > uint64_t identifier = generateUniqueCallbackIdentifier(); > ASSERT(!m_errorCallbacks.contains(identifier)); > m_errorCallbacks.add(identifier, WTFMove(callback)); >+ >+ if (taskSize) { >+ m_server->increasePotentialSpaceUsed(m_identifier.origin(), taskSize); >+ m_pendingSpaceIncreasingTasks.add(identifier, taskSize); >+ } >+ > m_callbackQueue.append(identifier); > return identifier; > } > >-uint64_t UniqueIDBDatabase::storeCallbackOrFireError(KeyDataCallback&& callback) >+uint64_t UniqueIDBDatabase::storeCallbackOrFireError(KeyDataCallback&& callback, uint64_t taskSize) > { > if (m_hardClosedForUserDelete) { > callback(IDBError::userDeleteError(), { }); >@@ -451,6 +546,12 @@ uint64_t UniqueIDBDatabase::storeCallbackOrFireError(KeyDataCallback&& callback) > uint64_t identifier = generateUniqueCallbackIdentifier(); > ASSERT(!m_keyDataCallbacks.contains(identifier)); > m_keyDataCallbacks.add(identifier, WTFMove(callback)); >+ >+ if (taskSize) { >+ m_server->increasePotentialSpaceUsed(m_identifier.origin(), taskSize); >+ m_pendingSpaceIncreasingTasks.add(identifier, taskSize); >+ } >+ > m_callbackQueue.append(identifier); > return identifier; > } >@@ -668,7 +769,19 @@ void UniqueIDBDatabase::createObjectStore(UniqueIDBDatabaseTransaction& transact > ASSERT(isMainThread()); > LOG(IndexedDB, "(main) UniqueIDBDatabase::createObjectStore"); > >- uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback)); >+ auto taskSize = defaultWriteOperationCost + estimateSize(info); >+ requestSpace(taskSize, "createObjectStore", [this, taskSize, transaction = makeRef(transaction), info, callback = WTFMove(callback)](auto error) mutable { >+ if (error) { >+ callback(WTFMove(error.value())); >+ return; >+ } >+ createObjectStoreAfterQuotaCheck(taskSize, transaction.get(), info, WTFMove(callback)); >+ }); >+} >+ >+void UniqueIDBDatabase::createObjectStoreAfterQuotaCheck(uint64_t taskSize, UniqueIDBDatabaseTransaction& transaction, const IDBObjectStoreInfo& info, ErrorCallback callback) >+{ >+ uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback), taskSize); > if (!callbackID) > return; > >@@ -744,7 +857,19 @@ void UniqueIDBDatabase::renameObjectStore(UniqueIDBDatabaseTransaction& transact > ASSERT(isMainThread()); > LOG(IndexedDB, "(main) UniqueIDBDatabase::renameObjectStore"); > >- uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback)); >+ auto taskSize = defaultWriteOperationCost + newName.sizeInBytes(); >+ requestSpace(taskSize, "renameObjectStore", [this, taskSize, transaction = makeRef(transaction), objectStoreIdentifier, newName, callback = WTFMove(callback)](auto error) mutable { >+ if (error) { >+ callback(WTFMove(error.value())); >+ return; >+ } >+ renameObjectStoreAfterQuotaCheck(taskSize, transaction.get(), objectStoreIdentifier, newName, WTFMove(callback)); >+ }); >+} >+ >+void UniqueIDBDatabase::renameObjectStoreAfterQuotaCheck(uint64_t taskSize, UniqueIDBDatabaseTransaction& transaction, uint64_t objectStoreIdentifier, const String& newName, ErrorCallback callback) >+{ >+ uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback), taskSize); > if (!callbackID) > return; > >@@ -816,7 +941,19 @@ void UniqueIDBDatabase::createIndex(UniqueIDBDatabaseTransaction& transaction, c > ASSERT(isMainThread()); > LOG(IndexedDB, "(main) UniqueIDBDatabase::createIndex"); > >- uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback)); >+ auto taskSize = defaultWriteOperationCost + estimateSize(info); >+ requestSpace(taskSize, "createIndex", [this, taskSize, transaction = makeRef(transaction), info, callback = WTFMove(callback)](auto error) mutable { >+ if (error) { >+ callback(WTFMove(error.value())); >+ return; >+ } >+ createIndexAfterQuotaCheck(taskSize, transaction.get(), info, WTFMove(callback)); >+ }); >+} >+ >+void UniqueIDBDatabase::createIndexAfterQuotaCheck(uint64_t taskSize, UniqueIDBDatabaseTransaction& transaction, const IDBIndexInfo& info, ErrorCallback callback) >+{ >+ uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback), taskSize); > if (!callbackID) > return; > postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performCreateIndex, callbackID, transaction.info().identifier(), info)); >@@ -903,7 +1040,19 @@ void UniqueIDBDatabase::renameIndex(UniqueIDBDatabaseTransaction& transaction, u > ASSERT(isMainThread()); > LOG(IndexedDB, "(main) UniqueIDBDatabase::renameIndex"); > >- uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback)); >+ auto taskSize = defaultWriteOperationCost + newName.sizeInBytes(); >+ requestSpace(taskSize, "renameIndex", [this, taskSize, transaction = makeRef(transaction), objectStoreIdentifier, indexIdentifier, newName, callback = WTFMove(callback)](auto error) mutable { >+ if (error) { >+ callback(WTFMove(error.value())); >+ return; >+ } >+ renameIndexAfterQuotaCheck(taskSize, transaction.get(), objectStoreIdentifier, indexIdentifier, newName, WTFMove(callback)); >+ }); >+} >+ >+void UniqueIDBDatabase::renameIndexAfterQuotaCheck(uint64_t taskSize, UniqueIDBDatabaseTransaction& transaction, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName, ErrorCallback callback) >+{ >+ uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback), taskSize); > if (!callbackID) > return; > >@@ -957,7 +1106,19 @@ void UniqueIDBDatabase::putOrAdd(const IDBRequestData& requestData, const IDBKey > ASSERT(isMainThread()); > LOG(IndexedDB, "(main) UniqueIDBDatabase::putOrAdd"); > >- uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback)); >+ auto taskSize = defaultWriteOperationCost + estimateSize(keyData) + estimateSize(value); >+ requestSpace(taskSize, "putOrAdd", [this, taskSize, requestData, keyData, value, callback = WTFMove(callback), overwriteMode](auto error) mutable { >+ if (error) { >+ callback(WTFMove(error.value()), { }); >+ return; >+ } >+ putOrAddAfterQuotaCheck(taskSize, requestData, keyData, value, overwriteMode, WTFMove(callback)); >+ }); >+} >+ >+void UniqueIDBDatabase::putOrAddAfterQuotaCheck(uint64_t taskSize, const IDBRequestData& requestData, const IDBKeyData& keyData, const IDBValue& value, IndexedDB::ObjectStoreOverwriteMode overwriteMode, KeyDataCallback callback) >+{ >+ uint64_t callbackID = storeCallbackOrFireError(WTFMove(callback), taskSize); > if (!callbackID) > return; > postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performPutOrAdd, callbackID, requestData.transactionIdentifier(), requestData.objectStoreIdentifier(), keyData, value, overwriteMode)); >@@ -1768,6 +1929,9 @@ void UniqueIDBDatabase::postDatabaseTask(CrossThreadTask&& task) > > void UniqueIDBDatabase::postDatabaseTaskReply(CrossThreadTask&& task) > { >+ // FIXME: We might want to compute total size only for modification operations. >+ if (m_backingStore) >+ m_databasesSizeForOrigin = m_backingStore->databasesSizeForOrigin(); > m_databaseReplyQueue.append(WTFMove(task)); > m_server->postDatabaseTaskReply(createCrossThreadTask(*this, &UniqueIDBDatabase::executeNextDatabaseTaskReply)); > } >@@ -1832,6 +1996,9 @@ void UniqueIDBDatabase::immediateCloseForUserDelete() > > ASSERT(isMainThread()); > >+ m_pendingSpaceIncreasingTasks.clear(); >+ m_server->clearSpaceUsed(m_identifier.origin()); >+ > // Error out all transactions > for (auto& identifier : copyToVector(m_inProgressTransactions.keys())) > m_inProgressTransactions.get(identifier)->abortWithoutCallback(); >@@ -1902,8 +2069,21 @@ void UniqueIDBDatabase::immediateCloseForUserDelete() > postDatabaseTask(createCrossThreadTask(*this, &UniqueIDBDatabase::performUnconditionalDeleteBackingStore)); > } > >+void UniqueIDBDatabase::updateSpaceUsedIfNeeded(uint64_t callbackIdentifier) >+{ >+ auto iterator = m_pendingSpaceIncreasingTasks.find(callbackIdentifier); >+ if (iterator == m_pendingSpaceIncreasingTasks.end()) >+ return; >+ >+ m_server->decreasePotentialSpaceUsed(m_identifier.origin(), iterator->value); >+ m_server->setSpaceUsed(m_identifier.origin(), m_databasesSizeForOrigin); >+ m_pendingSpaceIncreasingTasks.remove(iterator); >+} >+ > void UniqueIDBDatabase::performErrorCallback(uint64_t callbackIdentifier, const IDBError& error) > { >+ updateSpaceUsedIfNeeded(callbackIdentifier); >+ > auto callback = m_errorCallbacks.take(callbackIdentifier); > ASSERT(callback || m_hardClosedForUserDelete); > if (callback) { >@@ -1915,6 +2095,8 @@ void UniqueIDBDatabase::performErrorCallback(uint64_t callbackIdentifier, const > > void UniqueIDBDatabase::performKeyDataCallback(uint64_t callbackIdentifier, const IDBError& error, const IDBKeyData& resultKey) > { >+ updateSpaceUsedIfNeeded(callbackIdentifier); >+ > auto callback = m_keyDataCallbacks.take(callbackIdentifier); > ASSERT(callback || m_hardClosedForUserDelete); > if (callback) { >@@ -1959,6 +2141,8 @@ void UniqueIDBDatabase::performCountCallback(uint64_t callbackIdentifier, const > > void UniqueIDBDatabase::forgetErrorCallback(uint64_t callbackIdentifier) > { >+ updateSpaceUsedIfNeeded(callbackIdentifier); >+ > ASSERT(m_errorCallbacks.contains(callbackIdentifier)); > ASSERT(m_callbackQueue.last() == callbackIdentifier); > m_callbackQueue.removeLast(); >diff --git a/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.h b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.h >index fc094eef6b394f0d2e97fa169bd21dcdf2355286..683ae83d84c4179e42cebfb1010c6fcc7fca3a51 100644 >--- a/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.h >+++ b/Source/WebCore/Modules/indexeddb/server/UniqueIDBDatabase.h >@@ -54,6 +54,7 @@ class IDBError; > class IDBGetAllResult; > class IDBRequestData; > class IDBTransactionInfo; >+class StorageQuotaManager; > > enum class IDBGetRecordDataType; > >@@ -82,7 +83,7 @@ public: > void openDatabaseConnection(IDBConnectionToClient&, const IDBRequestData&); > > const IDBDatabaseInfo& info() const; >- IDBServer& server() { return *m_server; } >+ IDBServer& server() { return m_server.get(); } > const IDBDatabaseIdentifier& identifier() const { return m_identifier; } > > void createObjectStore(UniqueIDBDatabaseTransaction&, const IDBObjectStoreInfo&, ErrorCallback); >@@ -119,7 +120,10 @@ public: > > bool hardClosedForUserDelete() const { return m_hardClosedForUserDelete; } > >+ uint64_t spaceUsed() const; >+ > void setQuota(uint64_t); >+ > private: > enum class CloseState { Start, Done }; > >@@ -143,6 +147,12 @@ private: > > void scheduleShutdownForClose(); > >+ void createObjectStoreAfterQuotaCheck(uint64_t taskSize, UniqueIDBDatabaseTransaction&, const IDBObjectStoreInfo&, ErrorCallback); >+ void renameObjectStoreAfterQuotaCheck(uint64_t taskSize, UniqueIDBDatabaseTransaction&, uint64_t objectStoreIdentifier, const String& newName, ErrorCallback); >+ void createIndexAfterQuotaCheck(uint64_t taskSize, UniqueIDBDatabaseTransaction&, const IDBIndexInfo&, ErrorCallback); >+ void renameIndexAfterQuotaCheck(uint64_t taskSize, UniqueIDBDatabaseTransaction&, uint64_t objectStoreIdentifier, uint64_t indexIdentifier, const String& newName, ErrorCallback); >+ void putOrAddAfterQuotaCheck(uint64_t taskSize, const IDBRequestData&, const IDBKeyData&, const IDBValue&, IndexedDB::ObjectStoreOverwriteMode, KeyDataCallback); >+ > // Database thread operations > void deleteBackingStore(const IDBDatabaseIdentifier&); > void openBackingStore(const IDBDatabaseIdentifier&); >@@ -193,8 +203,8 @@ private: > void didPerformUnconditionalDeleteBackingStore(); > void didShutdownForClose(); > >- uint64_t storeCallbackOrFireError(ErrorCallback&&); >- uint64_t storeCallbackOrFireError(KeyDataCallback&&); >+ uint64_t storeCallbackOrFireError(ErrorCallback&&, uint64_t taskSize = 0); >+ uint64_t storeCallbackOrFireError(KeyDataCallback&&, uint64_t taskSize = 0); > uint64_t storeCallbackOrFireError(GetAllResultsCallback&&); > uint64_t storeCallbackOrFireError(GetResultCallback&&); > uint64_t storeCallbackOrFireError(CountCallback&&); >@@ -229,9 +239,12 @@ private: > > void notifyServerAboutClose(CloseState); > >- RefPtr<IDBServer> m_server; >+ void requestSpace(uint64_t taskSize, const char* errorMessage, CompletionHandler<void(Optional<IDBError>&&)>&&); >+ void updateSpaceUsedIfNeeded(uint64_t callbackIdentifier); >+ >+ Ref<IDBServer> m_server; > IDBDatabaseIdentifier m_identifier; >- >+ > ListHashSet<RefPtr<ServerOpenDBRequest>> m_pendingOpenDBRequests; > RefPtr<ServerOpenDBRequest> m_currentOpenDBRequest; > >@@ -279,6 +292,9 @@ private: > std::unique_ptr<UniqueIDBDatabase> m_owningPointerForClose; > > HashSet<IDBResourceIdentifier> m_cursorPrefetches; >+ >+ HashMap<uint64_t, uint64_t> m_pendingSpaceIncreasingTasks; >+ uint64_t m_databasesSizeForOrigin { 0 }; > }; > > } // namespace IDBServer >diff --git a/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.cpp b/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.cpp >index dd9f5ad29317c5cdc758832ac6a370bdf95dd898..ce1e778ef3a1f6d017f386a71546d62dee0cfab2 100644 >--- a/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.cpp >+++ b/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.cpp >@@ -44,30 +44,48 @@ > > namespace WebCore { > >-Ref<InProcessIDBServer> InProcessIDBServer::create() >+Ref<InProcessIDBServer> InProcessIDBServer::create(PAL::SessionID sessionID) > { >- Ref<InProcessIDBServer> server = adoptRef(*new InProcessIDBServer); >+ Ref<InProcessIDBServer> server = adoptRef(*new InProcessIDBServer(sessionID)); > server->m_server->registerConnection(server->connectionToClient()); > return server; > } > >-Ref<InProcessIDBServer> InProcessIDBServer::create(const String& databaseDirectoryPath) >+Ref<InProcessIDBServer> InProcessIDBServer::create(PAL::SessionID sessionID, const String& databaseDirectoryPath) > { >- Ref<InProcessIDBServer> server = adoptRef(*new InProcessIDBServer(databaseDirectoryPath)); >+ Ref<InProcessIDBServer> server = adoptRef(*new InProcessIDBServer(sessionID, databaseDirectoryPath)); > server->m_server->registerConnection(server->connectionToClient()); > return server; > } > >-InProcessIDBServer::InProcessIDBServer() >- : m_server(IDBServer::IDBServer::create(*this)) >+StorageQuotaManager* InProcessIDBServer::quotaManager(const ClientOrigin& origin) >+{ >+ return m_quotaManagers.ensure(origin, [] { >+ // FIXME: This quota value and callback implementations are temporary. >+ // Processing done by InProcessIDBServer should be moved to NetworkProcess. >+ return std::make_unique<StorageQuotaManager>(StorageQuotaManager::defaultQuota(), [](uint64_t quota, uint64_t, uint64_t, auto callback) { >+ callback(quota); >+ }); >+ }).iterator->value.get(); >+} >+ >+static inline IDBServer::IDBServer::QuotaManagerGetter storageQuotaManagerGetter(InProcessIDBServer& server) >+{ >+ return [&server](PAL::SessionID, const auto& origin) { >+ return server.quotaManager(origin); >+ }; >+} >+ >+InProcessIDBServer::InProcessIDBServer(PAL::SessionID sessionID) >+ : m_server(IDBServer::IDBServer::create(sessionID, *this, storageQuotaManagerGetter(*this))) > { > relaxAdoptionRequirement(); > m_connectionToServer = IDBClient::IDBConnectionToServer::create(*this); > m_connectionToClient = IDBServer::IDBConnectionToClient::create(*this); > } > >-InProcessIDBServer::InProcessIDBServer(const String& databaseDirectoryPath) >- : m_server(IDBServer::IDBServer::create(databaseDirectoryPath, *this)) >+InProcessIDBServer::InProcessIDBServer(PAL::SessionID sessionID, const String& databaseDirectoryPath) >+ : m_server(IDBServer::IDBServer::create(sessionID, databaseDirectoryPath, *this, storageQuotaManagerGetter(*this))) > { > relaxAdoptionRequirement(); > m_connectionToServer = IDBClient::IDBConnectionToServer::create(*this); >diff --git a/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.h b/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.h >index c7944d1b36f7359d3cb617069c30121885534973..452c7f0514473f3f1162a2cd5b47b3b5f5a0d49c 100644 >--- a/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.h >+++ b/Source/WebCore/Modules/indexeddb/shared/InProcessIDBServer.h >@@ -30,11 +30,18 @@ > #include "IDBConnectionToClient.h" > #include "IDBConnectionToServer.h" > #include "IDBServer.h" >+#include "StorageQuotaManager.h" > #include <wtf/RefCounted.h> > #include <wtf/RefPtr.h> > >+namespace PAL { >+class SessionID; >+} >+ > namespace WebCore { > >+struct ClientOrigin; >+ > namespace IDBClient { > class IDBConnectionToServer; > } >@@ -45,8 +52,8 @@ class IDBServer; > > class InProcessIDBServer final : public IDBClient::IDBConnectionToServerDelegate, public IDBServer::IDBConnectionToClientDelegate, public RefCounted<InProcessIDBServer>, public IDBServer::IDBBackingStoreTemporaryFileHandler { > public: >- WEBCORE_EXPORT static Ref<InProcessIDBServer> create(); >- WEBCORE_EXPORT static Ref<InProcessIDBServer> create(const String& databaseDirectoryPath); >+ WEBCORE_EXPORT static Ref<InProcessIDBServer> create(PAL::SessionID); >+ WEBCORE_EXPORT static Ref<InProcessIDBServer> create(PAL::SessionID, const String& databaseDirectoryPath); > > WEBCORE_EXPORT IDBClient::IDBConnectionToServer& connectionToServer() const; > IDBServer::IDBConnectionToClient& connectionToClient() const; >@@ -114,13 +121,17 @@ public: > > void accessToTemporaryFileComplete(const String& path) override; > >+ StorageQuotaManager* quotaManager(const ClientOrigin&); >+ > private: >- InProcessIDBServer(); >- InProcessIDBServer(const String& databaseDirectoryPath); >+ explicit InProcessIDBServer(PAL::SessionID); >+ InProcessIDBServer(PAL::SessionID, const String& databaseDirectoryPath); > > Ref<IDBServer::IDBServer> m_server; > RefPtr<IDBClient::IDBConnectionToServer> m_connectionToServer; > RefPtr<IDBServer::IDBConnectionToClient> m_connectionToClient; >+ >+ HashMap<ClientOrigin, std::unique_ptr<StorageQuotaManager>> m_quotaManagers; > }; > > } // namespace WebCore >diff --git a/Source/WebCore/loader/EmptyClients.cpp b/Source/WebCore/loader/EmptyClients.cpp >index d56e3379bc4b70b4dc231d13d64a723051387e3a..e53b4047e1dd58a2ff3036b250894a2e6ad17a12 100644 >--- a/Source/WebCore/loader/EmptyClients.cpp >+++ b/Source/WebCore/loader/EmptyClients.cpp >@@ -118,9 +118,9 @@ class EmptyContextMenuClient final : public ContextMenuClient { > > class EmptyDatabaseProvider final : public DatabaseProvider { > #if ENABLE(INDEXED_DATABASE) >- IDBClient::IDBConnectionToServer& idbConnectionToServerForSession(const PAL::SessionID&) final >+ IDBClient::IDBConnectionToServer& idbConnectionToServerForSession(const PAL::SessionID& sessionID) final > { >- static auto& sharedConnection = InProcessIDBServer::create().leakRef(); >+ static auto& sharedConnection = InProcessIDBServer::create(sessionID).leakRef(); > return sharedConnection.connectionToServer(); > } > #endif >diff --git a/Source/WebCore/storage/StorageQuotaManager.cpp b/Source/WebCore/storage/StorageQuotaManager.cpp >index b595da8d97604c1669265313a14242e4b5c713d8..e30a40a89fd4b60676978cce4b6017f5c61e8805 100644 >--- a/Source/WebCore/storage/StorageQuotaManager.cpp >+++ b/Source/WebCore/storage/StorageQuotaManager.cpp >@@ -44,9 +44,23 @@ uint64_t StorageQuotaManager::spaceUsage() const > return usage; > } > >+void StorageQuotaManager::addUser(StorageQuotaUser& user) >+{ >+ ASSERT(!m_pendingInitializationUsers.contains(&user)); >+ ASSERT(!m_users.contains(&user)); >+ m_pendingInitializationUsers.add(&user); >+ user.whenInitialized([this, &user, weakThis = makeWeakPtr(this)]() { >+ if (!weakThis) >+ return; >+ m_pendingInitializationUsers.remove(&user); >+ m_users.add(&user); >+ processPendingRequests({ }); >+ }); >+} >+ > void StorageQuotaManager::requestSpace(uint64_t spaceIncrease, RequestCallback&& callback) > { >- if (!m_pendingRequests.isEmpty()) { >+ if (!m_pendingRequests.isEmpty() || !m_pendingInitializationUsers.isEmpty()) { > m_pendingRequests.append({ spaceIncrease, WTFMove(callback) }); > return; > } >diff --git a/Source/WebCore/storage/StorageQuotaManager.h b/Source/WebCore/storage/StorageQuotaManager.h >index 2a844373baf954dda2d89918170e21a1e8cc0da6..dc8141b024e4c5414242aea2cfc03ab89b6ab526 100644 >--- a/Source/WebCore/storage/StorageQuotaManager.h >+++ b/Source/WebCore/storage/StorageQuotaManager.h >@@ -46,15 +46,13 @@ public: > } > WEBCORE_EXPORT ~StorageQuotaManager(); > >- void addUser(StorageQuotaUser& user) >- { >- ASSERT(!m_users.contains(&user)); >- m_users.add(&user); >- } >+ static constexpr uint64_t defaultQuota() { return 500 * MB; } > >+ WEBCORE_EXPORT void addUser(StorageQuotaUser&); > void removeUser(StorageQuotaUser& user) > { >- ASSERT(m_users.contains(&user)); >+ ASSERT(m_users.contains(&user) || m_pendingInitializationUsers.contains(&user)); >+ m_pendingInitializationUsers.remove(&user); > m_users.remove(&user); > } > >@@ -69,6 +67,7 @@ private: > > uint64_t m_quota { 0 }; > SpaceIncreaseRequester m_spaceIncreaseRequester; >+ HashSet<const StorageQuotaUser*> m_pendingInitializationUsers; > HashSet<const StorageQuotaUser*> m_users; > > struct PendingRequest { >diff --git a/Source/WebCore/storage/StorageQuotaUser.h b/Source/WebCore/storage/StorageQuotaUser.h >index 3dddfab9dcd741e08b996db8629e90c1d61fda03..b4230c5e12d094d699e1e29979164af5b7c838e0 100644 >--- a/Source/WebCore/storage/StorageQuotaUser.h >+++ b/Source/WebCore/storage/StorageQuotaUser.h >@@ -25,6 +25,8 @@ > > #pragma once > >+#include <wtf/CompletionHandler.h> >+ > namespace WebCore { > > class StorageQuotaUser { >@@ -32,6 +34,7 @@ public: > virtual ~StorageQuotaUser() = default; > > virtual uint64_t spaceUsed() const = 0; >+ virtual void whenInitialized(CompletionHandler<void()>&& callback) { callback(); } > }; > > } // namespace WebCore >diff --git a/Source/WebKit/NetworkProcess/NetworkProcess.cpp b/Source/WebKit/NetworkProcess/NetworkProcess.cpp >index c75b3bc04a1d1f96aaf3c860fe66a1e20c462b87..7b21eca0ec583be1619826b2de3c075eb1fc7f7e 100644 >--- a/Source/WebKit/NetworkProcess/NetworkProcess.cpp >+++ b/Source/WebKit/NetworkProcess/NetworkProcess.cpp >@@ -2022,24 +2022,31 @@ void NetworkProcess::didSyncAllCookies() > } > > #if ENABLE(INDEXED_DATABASE) >-IDBServer::IDBServer& NetworkProcess::idbServer(PAL::SessionID sessionID) >+Ref<IDBServer::IDBServer> NetworkProcess::createIDBServer(PAL::SessionID sessionID) > { >- auto addResult = m_idbServers.add(sessionID, nullptr); >- if (!addResult.isNewEntry) { >- ASSERT(addResult.iterator->value); >- return *addResult.iterator->value; >- } >- > auto path = m_idbDatabasePaths.get(sessionID); > // There should already be a registered path for this PAL::SessionID. > // If there's not, then where did this PAL::SessionID come from? > ASSERT(!path.isEmpty()); >- >- addResult.iterator->value = IDBServer::IDBServer::create(path, *this, [this](bool isHoldingLockedFiles) { >+ >+ auto server = IDBServer::IDBServer::create(sessionID, path, *this, [this, weakThis = makeWeakPtr(this)](PAL::SessionID sessionID, const auto& origin) -> StorageQuotaManager* { >+ if (!weakThis) >+ return nullptr; >+ return &storageQuotaManager(sessionID, origin); >+ }, [this, weakThis = makeWeakPtr(this)](bool isHoldingLockedFiles) { >+ if (!weakThis) >+ return; > notifyHoldingLockedFiles(isHoldingLockedFiles); > }); >- addResult.iterator->value->setPerOriginQuota(m_idbPerOriginQuota); >- return *addResult.iterator->value; >+ server->setPerOriginQuota(m_idbPerOriginQuota); >+ return server; >+} >+ >+IDBServer::IDBServer& NetworkProcess::idbServer(PAL::SessionID sessionID) >+{ >+ return *m_idbServers.ensure(sessionID, [this, sessionID] { >+ return this->createIDBServer(sessionID); >+ }).iterator->value; > } > > void NetworkProcess::ensurePathExists(const String& path) >diff --git a/Source/WebKit/NetworkProcess/NetworkProcess.h b/Source/WebKit/NetworkProcess/NetworkProcess.h >index 4b5fcc2489161fa110a0bb6b55f85c4a5c73443c..451cc60f436ac101706f4cc36775c4974f259631 100644 >--- a/Source/WebKit/NetworkProcess/NetworkProcess.h >+++ b/Source/WebKit/NetworkProcess/NetworkProcess.h >@@ -429,6 +429,7 @@ private: > #if ENABLE(INDEXED_DATABASE) > void addIndexedDatabaseSession(PAL::SessionID, String&, SandboxExtension::Handle&); > HashSet<WebCore::SecurityOriginData> indexedDatabaseOrigins(const String& path); >+ Ref<WebCore::IDBServer::IDBServer> createIDBServer(PAL::SessionID); > #endif > > #if ENABLE(SERVICE_WORKER) >@@ -523,7 +524,7 @@ private: > #endif > > struct StorageQuotaManagers { >- uint64_t defaultQuota { 0 }; >+ uint64_t defaultQuota { WebCore::StorageQuotaManager::defaultQuota() }; > HashMap<WebCore::ClientOrigin, std::unique_ptr<WebCore::StorageQuotaManager>> managersPerOrigin; > }; > HashMap<PAL::SessionID, StorageQuotaManagers> m_storageQuotaManagers; >diff --git a/Source/WebKit/WebProcess/Databases/WebDatabaseProvider.cpp b/Source/WebKit/WebProcess/Databases/WebDatabaseProvider.cpp >index b0798e99a8e753669c3ad3049683b59d36c323d2..1c9de65e442982ecffe7c1979360944a2893d6e2 100644 >--- a/Source/WebKit/WebProcess/Databases/WebDatabaseProvider.cpp >+++ b/Source/WebKit/WebProcess/Databases/WebDatabaseProvider.cpp >@@ -72,7 +72,7 @@ WebCore::IDBClient::IDBConnectionToServer& WebDatabaseProvider::idbConnectionToS > if (sessionID.isEphemeral()) { > auto result = m_idbEphemeralConnectionMap.add(sessionID.sessionID(), nullptr); > if (result.isNewEntry) >- result.iterator->value = WebCore::InProcessIDBServer::create(); >+ result.iterator->value = WebCore::InProcessIDBServer::create(sessionID); > > return result.iterator->value->connectionToServer(); > } >diff --git a/Source/WebKitLegacy/Storage/WebDatabaseProvider.cpp b/Source/WebKitLegacy/Storage/WebDatabaseProvider.cpp >index fb7166173e735a04980ca6159a449d22019ed8bb..5ee5a7b29bef45399dfd78b3123c0e0b2fc9aca1 100644 >--- a/Source/WebKitLegacy/Storage/WebDatabaseProvider.cpp >+++ b/Source/WebKitLegacy/Storage/WebDatabaseProvider.cpp >@@ -48,9 +48,9 @@ WebCore::IDBClient::IDBConnectionToServer& WebDatabaseProvider::idbConnectionToS > auto result = m_idbServerMap.add(sessionID.sessionID(), nullptr); > if (result.isNewEntry) { > if (sessionID.isEphemeral()) >- result.iterator->value = WebCore::InProcessIDBServer::create(); >+ result.iterator->value = WebCore::InProcessIDBServer::create(sessionID); > else >- result.iterator->value = WebCore::InProcessIDBServer::create(indexedDatabaseDirectoryPath()); >+ result.iterator->value = WebCore::InProcessIDBServer::create(sessionID, indexedDatabaseDirectoryPath()); > } > > result.iterator->value->idbServer().setPerOriginQuota(m_idbPerOriginQuota); >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index 28c202c529d94209ddf5d563ac9eb58473fe5c52..7bfcf94d6ee0094ba78e94e2373a33df962ecad6 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,20 @@ >+2019-03-11 Youenn Fablet <youenn@apple.com> >+ >+ Check IDB quota usage through QuotaManager >+ https://bugs.webkit.org/show_bug.cgi?id=195302 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * http/tests/IndexedDB/resources/shared.js: Added. >+ * http/tests/IndexedDB/resources/storage-limit.js: Added. >+ * http/tests/IndexedDB/storage-limit.https-expected.txt: Added. >+ * http/tests/IndexedDB/storage-limit.https.html: Added. >+ * http/wpt/cache-storage/cache-quota.any.js: >+ * platform/mac-wk1/TestExpectations: >+ * platform/mac-wk1/storage/indexeddb/storage-limit-expected.txt: Added. >+ * storage/indexeddb/resources/storage-limit.js: >+ * storage/indexeddb/storage-limit-expected.txt: >+ > 2019-03-11 Youenn Fablet <youenn@apple.com> > > Allow storage quota increase by default in WTR >diff --git a/LayoutTests/http/tests/IndexedDB/resources/shared.js b/LayoutTests/http/tests/IndexedDB/resources/shared.js >new file mode 100644 >index 0000000000000000000000000000000000000000..fc9c1e893022ec28290ae498b1e95470220dbf15 >--- /dev/null >+++ b/LayoutTests/http/tests/IndexedDB/resources/shared.js >@@ -0,0 +1,227 @@ >+var jsTestIsAsync = true; >+if (self.importScripts && !self.postMessage) { >+ // Shared worker. Make postMessage send to the newest client, which in >+ // our tests is the only client. >+ >+ // Store messages for sending until we have somewhere to send them. >+ self.postMessage = function(message) >+ { >+ if (typeof self.pendingMessages === "undefined") >+ self.pendingMessages = []; >+ self.pendingMessages.push(message); >+ }; >+ self.onconnect = function(event) >+ { >+ self.postMessage = function(message) >+ { >+ event.ports[0].postMessage(message); >+ }; >+ // Offload any stored messages now that someone has connected to us. >+ if (typeof self.pendingMessages === "undefined") >+ return; >+ while (self.pendingMessages.length) >+ event.ports[0].postMessage(self.pendingMessages.shift()); >+ }; >+} >+ >+function removeVendorPrefixes() >+{ >+ IDBCursor = self.IDBCursor || self.webkitIDBCursor; >+ IDBDatabase = self.IDBDatabase || self.webkitIDBDatabase; >+ IDBFactory = self.IDBFactory || self.webkitIDBFactory; >+ IDBIndex = self.IDBIndex || self.webkitIDBIndex; >+ IDBKeyRange = self.IDBKeyRange || self.webkitIDBKeyRange; >+ IDBObjectStore = self.IDBObjectStore || self.webkitIDBObjectStore; >+ IDBRequest = self.IDBRequest || self.webkitIDBRequest; >+ IDBTransaction = self.IDBTransaction || self.webkitIDBTransaction; >+ >+ indexedDB = evalAndLog("indexedDB = self.indexedDB || self.webkitIndexedDB || self.mozIndexedDB || self.msIndexedDB || self.OIndexedDB;"); >+ debug(""); >+} >+ >+function unexpectedSuccessCallback() >+{ >+ testFailed("Success function called unexpectedly."); >+ finishJSTest(); >+} >+ >+function unexpectedErrorCallback(event) >+{ >+ testFailed("Error function called unexpectedly: (" + event.target.error.name + ") " + event.target.error.message); >+ finishJSTest(); >+} >+ >+function unexpectedAbortCallback(e) >+{ >+ testFailed("Abort function called unexpectedly! Message: [" + e.target.error.message + "]"); >+ finishJSTest(); >+} >+ >+function unexpectedCompleteCallback() >+{ >+ testFailed("oncomplete function called unexpectedly!"); >+ finishJSTest(); >+} >+ >+function unexpectedBlockedCallback(e) >+{ >+ testFailed("onblocked called unexpectedly. oldVersion = " + e.oldVersion + ", newVersion = " + e.newVersion); >+ finishJSTest(); >+} >+ >+function unexpectedUpgradeNeededCallback() >+{ >+ testFailed("onupgradeneeded called unexpectedly"); >+ finishJSTest(); >+} >+ >+function unexpectedVersionChangeCallback(e) >+{ >+ testFailed("onversionchange called unexpectedly. oldVersion = " + e.oldVersion + ". newVersion = " + e.newVersion); >+ finishJSTest(); >+} >+ >+function evalAndExpectException(cmd, exceptionCode, exceptionName, _quiet) >+{ >+ if (!_quiet) >+ debug("Expecting exception from " + cmd); >+ try { >+ eval(cmd); >+ testFailed("No exception thrown! Should have been " + exceptionCode); >+ } catch (e) { >+ code = e.code; >+ if (!_quiet) >+ testPassed("Exception was thrown."); >+ shouldBe("code", exceptionCode, _quiet); >+ if (exceptionName) { >+ ename = e.name; >+ shouldBe("ename", exceptionName, _quiet); >+ } >+ if (!_quiet) >+ debug("Exception message: " + e.message); >+ } >+} >+ >+function evalAndExpectExceptionClass(cmd, expected) >+{ >+ debug("Expecting " + expected + " exception from " + cmd); >+ try { >+ eval(cmd); >+ testFailed("No exception thrown!" ); >+ } catch (e) { >+ testPassed("Exception was thrown."); >+ if (eval("e instanceof " + expected)) >+ testPassed(cmd + " threw " + e); >+ else >+ testFailed("Expected " + expected + " but saw " + e); >+ } >+} >+ >+function evalAndLogCallback(cmd) { >+ function callback() { >+ evalAndLog(cmd); >+ } >+ return callback; >+} >+ >+// If this function is deleted, a standalone layout test exercising its >+// functionality should be added. >+function deleteAllObjectStores(db) >+{ >+ while (db.objectStoreNames.length) >+ db.deleteObjectStore(db.objectStoreNames.item(0)); >+ debug("Deleted all object stores."); >+} >+ >+function setDBNameFromPath(suffix) { >+ var name = self.location.pathname.substring(1 + self.location.pathname.lastIndexOf("/")); >+ if (suffix) >+ name += suffix; >+ eval('dbname = "' + name + '"'); >+ return name; >+} >+ >+function preamble(evt) >+{ >+ if (evt) >+ event = evt; >+ debug(""); >+ debug(preamble.caller.name + "():"); >+} >+ >+// For Workers >+if (!self.DOMException) { >+ self.DOMException = { >+ INDEX_SIZE_ERR: 1, >+ DOMSTRING_SIZE_ERR: 2, >+ HIERARCHY_REQUEST_ERR: 3, >+ WRONG_DOCUMENT_ERR: 4, >+ INVALID_CHARACTER_ERR: 5, >+ NO_DATA_ALLOWED_ERR: 6, >+ NO_MODIFICATION_ALLOWED_ERR: 7, >+ NOT_FOUND_ERR: 8, >+ NOT_SUPPORTED_ERR: 9, >+ INUSE_ATTRIBUTE_ERR: 10, >+ INVALID_STATE_ERR: 11, >+ SYNTAX_ERR: 12, >+ INVALID_MODIFICATION_ERR: 13, >+ NAMESPACE_ERR: 14, >+ INVALID_ACCESS_ERR: 15, >+ VALIDATION_ERR: 16, >+ TYPE_MISMATCH_ERR: 17, >+ SECURITY_ERR: 18, >+ NETWORK_ERR: 19, >+ ABORT_ERR: 20, >+ URL_MISMATCH_ERR: 21, >+ QUOTA_EXCEEDED_ERR: 22, >+ TIMEOUT_ERR: 23, >+ INVALID_NODE_TYPE_ERR: 24, >+ DATA_CLONE_ERR: 25 >+ }; >+} >+ >+function indexedDBTest(upgradeCallback, optionalOpenCallback, optionalParameters) { >+ removeVendorPrefixes(); >+ if (optionalParameters && 'suffix' in optionalParameters) { >+ setDBNameFromPath(optionalParameters['suffix']); >+ } else { >+ setDBNameFromPath(); >+ } >+ var deleteRequest = evalAndLog("indexedDB.deleteDatabase(dbname)"); >+ deleteRequest.onerror = unexpectedErrorCallback; >+ deleteRequest.onblocked = unexpectedBlockedCallback; >+ deleteRequest.onsuccess = function() { >+ self.openRequest = null; >+ if (optionalParameters && 'version' in optionalParameters) >+ openRequest = evalAndLog("indexedDB.open(dbname, " + optionalParameters['version'] + ")"); >+ else >+ openRequest = evalAndLog("indexedDB.open(dbname)"); >+ shouldBe("openRequest.readyState", "'pending'", true/*quiet*/); >+ openRequest.onerror = unexpectedErrorCallback; >+ openRequest.onupgradeneeded = upgradeCallback; >+ openRequest.onblocked = unexpectedBlockedCallback; >+ if (optionalOpenCallback) >+ openRequest.onsuccess = optionalOpenCallback; >+ delete self.openRequest; >+ if (optionalParameters && 'runAfterOpen' in optionalParameters) >+ (optionalParameters['runAfterOpen'])(); >+ }; >+} >+ >+function waitForRequests(requests, callback) { >+ var count = requests.length; >+ >+ if (!count) { >+ callback(requests); >+ return; >+ } >+ >+ requests.forEach(function(req) { >+ req.onsuccess = function() { >+ --count; >+ if (!count) >+ callback(requests); >+ }; >+ req.onerror = unexpectedErrorCallback; >+ }); >+} >diff --git a/LayoutTests/http/tests/IndexedDB/resources/storage-limit.js b/LayoutTests/http/tests/IndexedDB/resources/storage-limit.js >new file mode 100644 >index 0000000000000000000000000000000000000000..51c15b8494ede61323c1de63b5d0f16c1663eb1c >--- /dev/null >+++ b/LayoutTests/http/tests/IndexedDB/resources/storage-limit.js >@@ -0,0 +1,39 @@ >+if (window.testRunner) >+ testRunner.setAllowStorageQuotaIncrease(false); >+ >+description("This test makes sure that storage of indexedDB does not grow unboundedly."); >+ >+window.caches.open("test").then(cache => { >+ return cache.put(new Request("/test"), new Response(new Uint8Array(204800))); >+}).then(() => { >+ indexedDBTest(prepareDatabase, onOpenSuccess, {'suffix': '-1'}); >+}).catch(e => { >+ testFailed("Cache API store operation failed: " + e); >+ finishJSTest(); >+}); >+ >+function prepareDatabase(event) >+{ >+ evalAndLog("db = event.target.result"); >+ evalAndLog("store = db.createObjectStore('store')"); >+} >+ >+// Quota for test is 400ko, but IDB is eating some of it when initializing files. >+// Let's make sure that 200ko is fine but 200ko after 200ko is not fine. >+async function onOpenSuccess(event) >+{ >+ evalAndLog("db = event.target.result"); >+ evalAndLog("store = db.transaction('store', 'readwrite').objectStore('store')"); >+ evalAndLog("request = store.add(new Uint8Array(204800), 'key')"); >+ request.onerror = function(event) { >+ shouldBeTrue("'error' in request"); >+ shouldBe("request.error.code", "DOMException.QUOTA_EXCEEDED_ERR"); >+ shouldBeEqualToString("request.error.name", "QuotaExceededError"); >+ finishJSTest(); >+ } >+ >+ request.onsuccess = function(event) { >+ testFailed("Add operation should fail because storage limit is reached, but succeeded."); >+ finishJSTest(); >+ } >+} >diff --git a/LayoutTests/http/tests/IndexedDB/storage-limit.https-expected.txt b/LayoutTests/http/tests/IndexedDB/storage-limit.https-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..996ab197f9bba6a561b60bdb95201f7ebe8545a5 >--- /dev/null >+++ b/LayoutTests/http/tests/IndexedDB/storage-limit.https-expected.txt >@@ -0,0 +1,21 @@ >+This test makes sure that storage of indexedDB does not grow unboundedly. >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+ >+indexedDB = self.indexedDB || self.webkitIndexedDB || self.mozIndexedDB || self.msIndexedDB || self.OIndexedDB; >+ >+indexedDB.deleteDatabase(dbname) >+indexedDB.open(dbname) >+db = event.target.result >+store = db.createObjectStore('store') >+db = event.target.result >+store = db.transaction('store', 'readwrite').objectStore('store') >+request = store.add(new Uint8Array(204800), 'key') >+PASS 'error' in request is true >+PASS request.error.code is DOMException.QUOTA_EXCEEDED_ERR >+PASS request.error.name is "QuotaExceededError" >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >diff --git a/LayoutTests/http/tests/IndexedDB/storage-limit.https.html b/LayoutTests/http/tests/IndexedDB/storage-limit.https.html >new file mode 100644 >index 0000000000000000000000000000000000000000..6b4df80f5205fcb8793636a2c76e7a46ab4d2e51 >--- /dev/null >+++ b/LayoutTests/http/tests/IndexedDB/storage-limit.https.html >@@ -0,0 +1,9 @@ >+<html> >+<head> >+<script src="/js-test-resources/js-test.js"></script> >+<script src="resources/shared.js"></script> >+</head> >+<body> >+<script src="resources/storage-limit.js"></script> >+</body> >+</html> >diff --git a/LayoutTests/http/wpt/cache-storage/cache-quota.any.js b/LayoutTests/http/wpt/cache-storage/cache-quota.any.js >index afc74cc4a44bf72c3945680831bb803f0a984fd9..959e293628fe10a28700e045bf1655a6b0d86eed 100644 >--- a/LayoutTests/http/wpt/cache-storage/cache-quota.any.js >+++ b/LayoutTests/http/wpt/cache-storage/cache-quota.any.js >@@ -4,6 +4,9 @@ > var test_url = 'https://example.com/foo'; > var test_body = 'Hello world!'; > >+if (window.testRunner) >+ testRunner.setAllowStorageQuotaIncrease(false); >+ > if (window.testRunner) > testRunner.setAllowStorageQuotaIncrease(false); > >diff --git a/LayoutTests/platform/mac-wk1/TestExpectations b/LayoutTests/platform/mac-wk1/TestExpectations >index d51e20c707a8da89ead58bec1987cd6f8667f9c4..5ecac48067121c1c6dfe3848efd28f6a981c4235 100644 >--- a/LayoutTests/platform/mac-wk1/TestExpectations >+++ b/LayoutTests/platform/mac-wk1/TestExpectations >@@ -162,6 +162,10 @@ imported/w3c/web-platform-tests/server-timing/service_worker_idl.html [ Skip ] > imported/w3c/web-platform-tests/fetch/api/request/destination [ Skip ] > imported/w3c/web-platform-tests/fetch/cross-origin-resource-policy [ Skip ] > >+# Quota check missing in WK1 >+http/tests/IndexedDB/storage-limit.https.html [ Skip ] >+storage/indexeddb/storage-limit.html [ Skip ] >+ > # Skip WebRTC for now in WK1 > imported/w3c/web-platform-tests/webrtc [ Skip ] > webrtc [ Skip ] >diff --git a/LayoutTests/platform/mac-wk1/storage/indexeddb/storage-limit-expected.txt b/LayoutTests/platform/mac-wk1/storage/indexeddb/storage-limit-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..9a303c60373491f70691e5a944626246b0587e90 >--- /dev/null >+++ b/LayoutTests/platform/mac-wk1/storage/indexeddb/storage-limit-expected.txt >@@ -0,0 +1,24 @@ >+This test makes sure that storage of indexedDB does not grow unboundedly. >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+ >+indexedDB = self.indexedDB || self.webkitIndexedDB || self.mozIndexedDB || self.msIndexedDB || self.OIndexedDB; >+ >+indexedDB.deleteDatabase(dbname) >+indexedDB.open(dbname) >+ >+prepareDatabase(): >+db = event.target.result >+store = db.createObjectStore('store') >+ >+onOpenSuccess(): >+db = event.target.result >+store = db.transaction('store', 'readwrite').objectStore('store') >+request = store.add(new Uint8Array(409601), 0) >+FAIL Add operation should fail because storage limit is reached, but succeeded. >+PASS successfullyParsed is true >+Some tests failed. >+ >+TEST COMPLETE >+ >diff --git a/LayoutTests/storage/indexeddb/resources/storage-limit.js b/LayoutTests/storage/indexeddb/resources/storage-limit.js >index 0d57b6c693f7d073bda9c3cc12905b0c5867c224..81be6d2096281a5bfb787029501db0e135daf0dc 100644 >--- a/LayoutTests/storage/indexeddb/resources/storage-limit.js >+++ b/LayoutTests/storage/indexeddb/resources/storage-limit.js >@@ -3,11 +3,11 @@ if (this.importScripts) { > importScripts('shared.js'); > } > >-var quota = 1024 * 1024; // 1 MB >-description("This test makes sure that storage of indexedDB does not grow unboundedly."); >- > if (window.testRunner) >- testRunner.setIDBPerOriginQuota(quota); >+ testRunner.setAllowStorageQuotaIncrease(false); >+ >+var quota = 400 * 1024; // default quota for testing. >+description("This test makes sure that storage of indexedDB does not grow unboundedly."); > > indexedDBTest(prepareDatabase, onOpenSuccess); > >@@ -35,4 +35,4 @@ function onOpenSuccess(event) > testFailed("Add operation should fail because storage limit is reached, but succeeded."); > finishJSTest(); > } >-} >\ No newline at end of file >+} >diff --git a/LayoutTests/storage/indexeddb/storage-limit-expected.txt b/LayoutTests/storage/indexeddb/storage-limit-expected.txt >index c314a44af6d700063c4ae0fbde47538b0aecb10d..a4cf6a95e73642a39f3fd0e22f3b1ab09ff749f7 100644 >--- a/LayoutTests/storage/indexeddb/storage-limit-expected.txt >+++ b/LayoutTests/storage/indexeddb/storage-limit-expected.txt >@@ -15,7 +15,7 @@ store = db.createObjectStore('store') > onOpenSuccess(): > db = event.target.result > store = db.transaction('store', 'readwrite').objectStore('store') >-request = store.add(new Uint8Array(1048577), 0) >+request = store.add(new Uint8Array(409601), 0) > PASS 'error' in request is true > PASS request.error.code is DOMException.QUOTA_EXCEEDED_ERR > PASS request.error.name is "QuotaExceededError"
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 195302
:
363573
|
363988
|
363990
|
363991
|
363993
|
364005
|
364201
|
364211
|
364217
|
364283
|
364308
|
364310
|
364345
|
364368
|
364395
|
364418
|
364551