NetworkProcess Cache and Caches should be cleared when the last related WebProcess...
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 7 Sep 2017 00:03:13 +0000 (00:03 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 7 Sep 2017 00:03:13 +0000 (00:03 +0000)
https://bugs.webkit.org/show_bug.cgi?id=176249

Patch by Youenn Fablet <youenn@apple.com> on 2017-09-06
Reviewed by Alex Christensen.

Source/WebCore:

Test: http/tests/cache-storage/cache-representation.https.html

Each Cache construction/destruction is notified to the CacheStorageConnection
so that the WebKit2 engine can handle memory management accordingly.

Adding an internal API to grab the representation of an engine at any time.

* Modules/cache/Cache.cpp:
(WebCore::Cache::Cache):
(WebCore::Cache::~Cache):
* Modules/cache/CacheStorageConnection.h:
(WebCore::CacheStorageConnection::reference):
(WebCore::CacheStorageConnection::dereference):
* Modules/cache/WorkerCacheStorageConnection.cpp:
(WebCore::WorkerCacheStorageConnection::reference):
(WebCore::WorkerCacheStorageConnection::dereference):
* Modules/cache/WorkerCacheStorageConnection.h:
* testing/Internals.cpp:
(WebCore::Internals::cacheStorageEngineRepresentation):
* testing/Internals.h:
* testing/Internals.idl:

Source/WebKit:

CacheStorageEngineConnection will store the number of Caches references for its related WebProcess.
For a new reference, CacheStorageEngineConnection will notify its engine to lock the related Cache.
When the last reference is gone, CacheStorageEngineConnection will ask the engine to release the lock.

CacheStorageEngine will keep locks as a counter for each Cache object.
When the counter goes to zero, the engine asks the Cache to dispose itself.
Disposal is done by its parent Caches as only Caches knows whether the Cache is persistent or has been removed.

The Caches will remove the Cache from memory.
For removed caches, all information will be lost.
For caches that are not removed, they will keep their identifiers but all in-memory record information will be discarded.
If there is a need to reopen the Cache, it will need to read its information from the filesystem.
If the Caches has no longer any active Cache object, it will ask the engine to release itself.

WebProcess crashing is handled correctly as the CacheStorageEngineConnection will release its locks at destruction
time of the connection to the web process.

Adding the possiblity to grab an engine representation for test purposes.

* NetworkProcess/cache/CacheStorageEngine.cpp:
(WebKit::CacheStorage::Engine::readCachesFromDisk):
(WebKit::CacheStorage::Engine::removeCaches):
(WebKit::CacheStorage::Engine::lock):
(WebKit::CacheStorage::Engine::unlock):
* NetworkProcess/cache/CacheStorageEngine.h:
* NetworkProcess/cache/CacheStorageEngineCache.cpp:
(WebKit::CacheStorage::Cache::Cache):
(WebKit::CacheStorage::Cache::dispose):
(WebKit::CacheStorage::Cache::clearMemoryRepresentation):
* NetworkProcess/cache/CacheStorageEngineCache.h:
(WebKit::CacheStorage::Cache::isActive const):
* NetworkProcess/cache/CacheStorageEngineCaches.cpp:
(WebKit::CacheStorage::Caches::Caches):
(WebKit::CacheStorage::Caches::open):
(WebKit::CacheStorage::Caches::dispose):
(WebKit::CacheStorage::Caches::readCachesFromDisk):
(WebKit::CacheStorage::Caches::clearMemoryRepresentation):
* NetworkProcess/cache/CacheStorageEngineCaches.h:
(WebKit::CacheStorage::Caches::create):
* NetworkProcess/cache/CacheStorageEngineConnection.cpp:
(WebKit::CacheStorageEngineConnection::~CacheStorageEngineConnection):
(WebKit::CacheStorageEngineConnection::reference):
(WebKit::CacheStorageEngineConnection::dereference):
* NetworkProcess/cache/CacheStorageEngineConnection.h:
* NetworkProcess/cache/CacheStorageEngineConnection.messages.in:
* WebProcess/Cache/WebCacheStorageConnection.cpp:
(WebKit::WebCacheStorageConnection::reference):
(WebKit::WebCacheStorageConnection::dereference):
* WebProcess/Cache/WebCacheStorageConnection.h:

LayoutTests:

* http/tests/cache-storage/cache-representation.https-expected.txt: Added.
* http/tests/cache-storage/cache-representation.https.html: Added.
* http/tests/cache-storage/resources/cache-persistency-iframe.html:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@221710 268f45cc-cd09-0410-ab3c-d52691b4dbfc

26 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/cache-storage/cache-origins.https.html
LayoutTests/http/tests/cache-storage/cache-representation.https-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cache-storage/cache-representation.https.html [new file with mode: 0644]
LayoutTests/http/tests/cache-storage/resources/cache-persistency-iframe.html
Source/WebCore/ChangeLog
Source/WebCore/Modules/cache/CacheStorageConnection.h
Source/WebCore/Modules/cache/DOMCache.cpp
Source/WebCore/Modules/cache/WorkerCacheStorageConnection.cpp
Source/WebCore/Modules/cache/WorkerCacheStorageConnection.h
Source/WebCore/testing/Internals.cpp
Source/WebCore/testing/Internals.h
Source/WebCore/testing/Internals.idl
Source/WebKit/ChangeLog
Source/WebKit/NetworkProcess/cache/CacheStorageEngine.cpp
Source/WebKit/NetworkProcess/cache/CacheStorageEngine.h
Source/WebKit/NetworkProcess/cache/CacheStorageEngineCache.cpp
Source/WebKit/NetworkProcess/cache/CacheStorageEngineCache.h
Source/WebKit/NetworkProcess/cache/CacheStorageEngineCaches.cpp
Source/WebKit/NetworkProcess/cache/CacheStorageEngineCaches.h
Source/WebKit/NetworkProcess/cache/CacheStorageEngineConnection.cpp
Source/WebKit/NetworkProcess/cache/CacheStorageEngineConnection.h
Source/WebKit/NetworkProcess/cache/CacheStorageEngineConnection.messages.in
Source/WebKit/WebProcess/Cache/WebCacheStorageConnection.cpp
Source/WebKit/WebProcess/Cache/WebCacheStorageConnection.h
Source/WebKit/WebProcess/Cache/WebCacheStorageConnection.messages.in

index abd8065..f8e2452 100644 (file)
@@ -1,3 +1,14 @@
+2017-09-06  Youenn Fablet  <youenn@apple.com>
+
+        NetworkProcess Cache and Caches should be cleared when the last related WebProcess Cache or CacheStorage is destroyed
+        https://bugs.webkit.org/show_bug.cgi?id=176249
+
+        Reviewed by Alex Christensen.
+
+        * http/tests/cache-storage/cache-representation.https-expected.txt: Added.
+        * http/tests/cache-storage/cache-representation.https.html: Added.
+        * http/tests/cache-storage/resources/cache-persistency-iframe.html:
+
 2017-09-06  Matt Lewis  <jlewis3@apple.com>
 
         Marked media/video-main-content-allow-then-scroll.html as flaky on macOS and failing on iOS.
index 012c54a..eb57ad7 100644 (file)
@@ -4,7 +4,6 @@
 <title>Cache Storage: testing persistency of different origins</title>
 <script src="/resources/testharness.js"></script>
 <script src="/resources/testharnessreport.js"></script>
-<script src="/resources/gc.js"></script>
 </head>
 <body>
     <script>
diff --git a/LayoutTests/http/tests/cache-storage/cache-representation.https-expected.txt b/LayoutTests/http/tests/cache-storage/cache-representation.https-expected.txt
new file mode 100644 (file)
index 0000000..53d3287
--- /dev/null
@@ -0,0 +1,12 @@
+
+
+PASS Cleaning existing caches 
+PASS Create a cache storage and look at the representation 
+PASS Look at the representation a second time after removing the iframe 
+PASS Remove a cache and look at the representation a second time 
+PASS Look at the representation a second time after reloading the iframe 
+PASS A cache is created 
+PASS A cache is persisting 
+PASS A cache is going in remove bucket 
+PASS A removed cache gets collected 
+
diff --git a/LayoutTests/http/tests/cache-storage/cache-representation.https.html b/LayoutTests/http/tests/cache-storage/cache-representation.https.html
new file mode 100644 (file)
index 0000000..3a44f62
--- /dev/null
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Cache Storage: testing persistency</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/js-test-resources/gc.js"></script>
+</head>
+<body>
+    <div id="check"></div>
+    <script>
+    if (window.testRunner)
+        testRunner.setPrivateBrowsingEnabled(true);
+
+    function checkCaches(hasPersistent, hasRemoved, name, value) {
+        test(() => {
+            var results = JSON.parse(value);
+            var caches = results[0].caches;
+            assert_equals(!!caches["persistent"].length, hasPersistent, "persistent");
+            assert_equals(!!caches["removed"].length, hasRemoved, "removed");
+        }, name);
+    }
+
+    promise_test(test => {
+        return self.caches.keys().then(keys => {
+            var pending = [];
+            for (key of keys)
+                pending.push(self.caches.delete(keys[0]));
+            return Promise.all(pending);
+        });
+    }, "Cleaning existing caches");
+
+    promise_test(test => {
+        if (!window.internals)
+            return Promise.reject("Test requires internals");
+
+        return new Promise((resolve, reject) => {
+            window.addEventListener("message", test.step_func((event) => {
+                return internals.cacheStorageEngineRepresentation().then(value => {
+                    checkCaches(true, false, "A cache is created", value);
+                    resolve();
+                });
+            }));
+            check.innerHTML = "<iframe src='resources/cache-persistency-iframe.html'></iframe>";
+        })
+    }, "Create a cache storage and look at the representation");
+
+    promise_test(test => {
+        if (!window.internals)
+            return Promise.reject("Test requires internals");
+
+        return new Promise((resolve, reject) => {
+            window.addEventListener("message", test.step_func((event) => {
+                return internals.cacheStorageEngineRepresentation().then(value => {
+                    checkCaches(true, false, "A cache is persisting", value);
+                    resolve();
+                });
+            }));
+            check.innerHTML = "<iframe src='resources/cache-persistency-iframe.html#check'></iframe>";
+        })
+    }, "Look at the representation a second time after removing the iframe");
+
+    promise_test(test => {
+        if (!window.internals)
+            return Promise.reject("Test requires internals");
+
+        return new Promise((resolve, reject) => {
+            window.addEventListener("message", test.step_func((event) => {
+                return internals.cacheStorageEngineRepresentation().then(value => {
+                    checkCaches(false, true, "A cache is going in remove bucket", value);
+                    resolve();
+                });
+            }));
+            check.innerHTML = "<iframe src='resources/cache-persistency-iframe.html#remove'></iframe>";
+        })
+    }, "Remove a cache and look at the representation a second time");
+
+    promise_test(test => {
+        if (!window.internals)
+            return Promise.reject("Test requires internals");
+
+        return new Promise((resolve, reject) => {
+            window.addEventListener("message", test.step_func((event) => {
+                gc();
+                setTimeout(() => {
+                    return internals.cacheStorageEngineRepresentation().then(value => {
+                        checkCaches(false, false, "A removed cache gets collected", value);
+                        resolve();
+                    });
+                }, 0);
+            }));
+            check.innerHTML = "<iframe src='resources/cache-persistency-iframe.html#check'></iframe>";
+        });
+    }, "Look at the representation a second time after reloading the iframe");
+
+    </script>
+</body>
+</html>
+
index ae2f13c..d0bbadf 100644 (file)
@@ -2,6 +2,7 @@
 <html>
 <body>
     <script>
+var cache;
 function doTest()
 {
     if (window.location.hash === "#check") {
@@ -11,6 +12,16 @@ function doTest()
         return;
     }
 
+    if (window.location.hash === "#remove") {
+        self.caches.open("testCacheName").then(c => {
+            cache = c
+            self.caches.delete("testCacheName").then(() => {
+                window.parent.postMessage("removed", "*");
+            });
+        });
+        return;
+    }
+
     var cacheName = "testCacheName";
     if (window.location.hash.indexOf("#name=") === 0)
         cacheName = window.location.hash.substring(6);
index dfe0093..b4df7fa 100644 (file)
@@ -1,3 +1,32 @@
+2017-09-06  Youenn Fablet  <youenn@apple.com>
+
+        NetworkProcess Cache and Caches should be cleared when the last related WebProcess Cache or CacheStorage is destroyed
+        https://bugs.webkit.org/show_bug.cgi?id=176249
+
+        Reviewed by Alex Christensen.
+
+        Test: http/tests/cache-storage/cache-representation.https.html
+
+        Each Cache construction/destruction is notified to the CacheStorageConnection
+        so that the WebKit2 engine can handle memory management accordingly.
+
+        Adding an internal API to grab the representation of an engine at any time.
+
+        * Modules/cache/Cache.cpp:
+        (WebCore::Cache::Cache):
+        (WebCore::Cache::~Cache):
+        * Modules/cache/CacheStorageConnection.h:
+        (WebCore::CacheStorageConnection::reference):
+        (WebCore::CacheStorageConnection::dereference):
+        * Modules/cache/WorkerCacheStorageConnection.cpp:
+        (WebCore::WorkerCacheStorageConnection::reference):
+        (WebCore::WorkerCacheStorageConnection::dereference):
+        * Modules/cache/WorkerCacheStorageConnection.h:
+        * testing/Internals.cpp:
+        (WebCore::Internals::cacheStorageEngineRepresentation):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+
 2017-09-05  Matt Rajca  <mrajca@apple.com>
 
         Support new autoplay quirk for arbitrary user gestures.
index f0451a3..466d578 100644 (file)
@@ -45,8 +45,12 @@ public:
     void batchDeleteOperation(uint64_t cacheIdentifier, const ResourceRequest&, CacheQueryOptions&&, DOMCacheEngine::RecordIdentifiersCallback&&);
     void batchPutOperation(uint64_t cacheIdentifier, Vector<DOMCacheEngine::Record>&&, DOMCacheEngine::RecordIdentifiersCallback&&);
 
+    virtual void reference(uint64_t /* cacheIdentifier */) { }
+    virtual void dereference(uint64_t /* cacheIdentifier */) { }
+
     // Used only for testing purposes.
     virtual void clearMemoryRepresentation(const String& /* origin */, DOMCacheEngine::CompletionCallback&& callback) { callback(DOMCacheEngine::Error::NotImplemented); }
+    virtual void engineRepresentation(WTF::Function<void(const String&)>&& callback) { callback(String { }); }
 
 protected:
     CacheStorageConnection() =  default;
index 85a5c4b..eea03a9 100644 (file)
@@ -47,10 +47,12 @@ DOMCache::DOMCache(ScriptExecutionContext& context, String&& name, uint64_t iden
     , m_connection(WTFMove(connection))
 {
     suspendIfNeeded();
+    m_connection->reference(m_identifier);
 }
 
 DOMCache::~DOMCache()
 {
+    m_connection->dereference(m_identifier);
 }
 
 void DOMCache::match(RequestInfo&& info, CacheQueryOptions&& options, Ref<DeferredPromise>&& promise)
index af955de..fb4d59b 100644 (file)
@@ -163,6 +163,26 @@ void WorkerCacheStorageConnection::doRetrieveCaches(uint64_t requestIdentifier,
     });
 }
 
+void WorkerCacheStorageConnection::reference(uint64_t cacheIdentifier)
+{
+    m_proxy.postTaskToLoader([this, protectedThis = makeRef(*this), cacheIdentifier](ScriptExecutionContext&) {
+        ASSERT(isMainThread());
+        ASSERT(m_mainThreadConnection);
+
+        m_mainThreadConnection->reference(cacheIdentifier);
+    });
+}
+
+void WorkerCacheStorageConnection::dereference(uint64_t cacheIdentifier)
+{
+    m_proxy.postTaskToLoader([this, protectedThis = makeRef(*this), cacheIdentifier](ScriptExecutionContext&) {
+        ASSERT(isMainThread());
+        ASSERT(m_mainThreadConnection);
+
+        m_mainThreadConnection->dereference(cacheIdentifier);
+    });
+}
+
 static inline Vector<CrossThreadRecordData> recordsDataFromRecords(const Vector<Record>& records)
 {
     Vector<CrossThreadRecordData> recordsData;
index e34fa7b..bf5ba29 100644 (file)
@@ -47,6 +47,10 @@ private:
     void doRetrieveCaches(uint64_t requestIdentifier, const String& origin, uint64_t updateCounter) final;
 
     void doRetrieveRecords(uint64_t requestIdentifier, uint64_t cacheIdentifier, const URL&) final;
+
+    void reference(uint64_t cacheIdentifier) final;
+    void dereference(uint64_t cacheIdentifier) final;
+
     void doBatchDeleteOperation(uint64_t requestIdentifier, uint64_t cacheIdentifier, const WebCore::ResourceRequest&, WebCore::CacheQueryOptions&&) final;
     void doBatchPutOperation(uint64_t requestIdentifier, uint64_t cacheIdentifier, Vector<DOMCacheEngine::Record>&&) final;
 
index 7b158df..56f9915 100644 (file)
@@ -4146,4 +4146,21 @@ void Internals::clearCacheStorageMemoryRepresentation()
     m_cacheStorageConnection->clearMemoryRepresentation(document->securityOrigin().toString(), [](std::optional<DOMCacheEngine::Error>&&) { });
 }
 
+void Internals::cacheStorageEngineRepresentation(DOMPromiseDeferred<IDLDOMString>&& promise)
+{
+    auto* document = contextDocument();
+    if (!document)
+        return;
+
+    if (!m_cacheStorageConnection) {
+        if (auto* page = contextDocument()->page())
+            m_cacheStorageConnection = page->cacheStorageProvider().createCacheStorageConnection(page->sessionID());
+        if (!m_cacheStorageConnection)
+            return;
+    }
+    m_cacheStorageConnection->engineRepresentation([promise = WTFMove(promise)](const String& result) mutable {
+        promise.resolve(result);
+    });
+}
+
 } // namespace WebCore
index 2b81460..dad1f4a 100644 (file)
@@ -600,6 +600,7 @@ public:
     String audioSessionCategory() const;
 
     void clearCacheStorageMemoryRepresentation();
+    void cacheStorageEngineRepresentation(DOMPromiseDeferred<IDLDOMString>&&);
 
 private:
     explicit Internals(Document&);
index e87a74c..3bf02b7 100644 (file)
@@ -545,6 +545,7 @@ enum EventThrottlingBehavior {
     [Conditional=MEDIA_STREAM] void simulateMediaStreamTrackCaptureSourceFailure(MediaStreamTrack track);
 
     void clearCacheStorageMemoryRepresentation();
+    Promise<DOMString> cacheStorageEngineRepresentation();
 
     DOMString audioSessionCategory();
 };
index a82d74c..262f2d0 100644 (file)
@@ -1,3 +1,60 @@
+2017-09-06  Youenn Fablet  <youenn@apple.com>
+
+        NetworkProcess Cache and Caches should be cleared when the last related WebProcess Cache or CacheStorage is destroyed
+        https://bugs.webkit.org/show_bug.cgi?id=176249
+
+        Reviewed by Alex Christensen.
+
+        CacheStorageEngineConnection will store the number of Caches references for its related WebProcess.
+        For a new reference, CacheStorageEngineConnection will notify its engine to lock the related Cache.
+        When the last reference is gone, CacheStorageEngineConnection will ask the engine to release the lock.
+
+        CacheStorageEngine will keep locks as a counter for each Cache object.
+        When the counter goes to zero, the engine asks the Cache to dispose itself.
+        Disposal is done by its parent Caches as only Caches knows whether the Cache is persistent or has been removed.
+
+        The Caches will remove the Cache from memory.
+        For removed caches, all information will be lost.
+        For caches that are not removed, they will keep their identifiers but all in-memory record information will be discarded.
+        If there is a need to reopen the Cache, it will need to read its information from the filesystem.
+        If the Caches has no longer any active Cache object, it will ask the engine to release itself.
+
+        WebProcess crashing is handled correctly as the CacheStorageEngineConnection will release its locks at destruction
+        time of the connection to the web process.
+
+        Adding the possiblity to grab an engine representation for test purposes.
+
+        * NetworkProcess/cache/CacheStorageEngine.cpp:
+        (WebKit::CacheStorage::Engine::readCachesFromDisk):
+        (WebKit::CacheStorage::Engine::removeCaches):
+        (WebKit::CacheStorage::Engine::lock):
+        (WebKit::CacheStorage::Engine::unlock):
+        * NetworkProcess/cache/CacheStorageEngine.h:
+        * NetworkProcess/cache/CacheStorageEngineCache.cpp:
+        (WebKit::CacheStorage::Cache::Cache):
+        (WebKit::CacheStorage::Cache::dispose):
+        (WebKit::CacheStorage::Cache::clearMemoryRepresentation):
+        * NetworkProcess/cache/CacheStorageEngineCache.h:
+        (WebKit::CacheStorage::Cache::isActive const):
+        * NetworkProcess/cache/CacheStorageEngineCaches.cpp:
+        (WebKit::CacheStorage::Caches::Caches):
+        (WebKit::CacheStorage::Caches::open):
+        (WebKit::CacheStorage::Caches::dispose):
+        (WebKit::CacheStorage::Caches::readCachesFromDisk):
+        (WebKit::CacheStorage::Caches::clearMemoryRepresentation):
+        * NetworkProcess/cache/CacheStorageEngineCaches.h:
+        (WebKit::CacheStorage::Caches::create):
+        * NetworkProcess/cache/CacheStorageEngineConnection.cpp:
+        (WebKit::CacheStorageEngineConnection::~CacheStorageEngineConnection):
+        (WebKit::CacheStorageEngineConnection::reference):
+        (WebKit::CacheStorageEngineConnection::dereference):
+        * NetworkProcess/cache/CacheStorageEngineConnection.h:
+        * NetworkProcess/cache/CacheStorageEngineConnection.messages.in:
+        * WebProcess/Cache/WebCacheStorageConnection.cpp:
+        (WebKit::WebCacheStorageConnection::reference):
+        (WebKit::WebCacheStorageConnection::dereference):
+        * WebProcess/Cache/WebCacheStorageConnection.h:
+
 2017-09-06  Alex Christensen  <achristensen@webkit.org>
 
         Add WKUIDelegatePrivate equivalent of WKPageUIClient's pinnedStateDidChange
index 701107a..3f039a3 100644 (file)
@@ -32,6 +32,7 @@
 #include <pal/SessionID.h>
 #include <wtf/MainThread.h>
 #include <wtf/NeverDestroyed.h>
+#include <wtf/text/StringBuilder.h>
 #include <wtf/text/StringHash.h>
 
 using namespace WebCore::DOMCacheEngine;
@@ -190,7 +191,7 @@ void Engine::readCachesFromDisk(const String& origin, CachesCallback&& callback)
 {
     initialize([this, origin, callback = WTFMove(callback)](std::optional<Error>&& error) mutable {
         auto& caches = m_caches.ensure(origin, [&origin, this] {
-            return Caches::create(*this, origin);
+            return Caches::create(*this, String { origin });
         }).iterator->value;
 
         if (caches->isInitialized()) {
@@ -289,6 +290,12 @@ void Engine::removeFile(const String& filename)
     });
 }
 
+void Engine::removeCaches(const String& origin)
+{
+    ASSERT(m_caches.contains(origin));
+    m_caches.remove(origin);
+}
+
 void Engine::clearMemoryRepresentation(const String& origin)
 {
     readCachesFromDisk(origin, [](CachesOrError&& result) {
@@ -298,6 +305,54 @@ void Engine::clearMemoryRepresentation(const String& origin)
     });
 }
 
+void Engine::lock(uint64_t cacheIdentifier)
+{
+    auto& counter = m_cacheLocks.ensure(cacheIdentifier, []() {
+        return 0;
+    }).iterator->value;
+
+    ++counter;
+}
+
+void Engine::unlock(uint64_t cacheIdentifier)
+{
+    auto lockCount = m_cacheLocks.find(cacheIdentifier);
+    ASSERT(lockCount != m_cacheLocks.end());
+    if (lockCount == m_cacheLocks.end())
+        return;
+
+    ASSERT(lockCount->value);
+    if (--lockCount->value)
+        return;
+
+    readCache(cacheIdentifier, [this](CacheOrError&& result) mutable {
+        if (!result.hasValue())
+            return;
+
+        result.value().get().dispose();
+    });
+}
+
+String Engine::representation()
+{
+    bool isFirst = true;
+    StringBuilder builder;
+    builder.append("[");
+    for (auto& keyValue : m_caches) {
+        if (!isFirst)
+            builder.append(",");
+        isFirst = false;
+
+        builder.append("\n{ \"origin\" : \"");
+        builder.append(keyValue.key);
+        builder.append("\", \"caches\" : ");
+        keyValue.value->appendRepresentation(builder);
+        builder.append("}");
+    }
+    builder.append("\n]");
+    return builder.toString();
+}
+
 } // namespace CacheStorage
 
 } // namespace WebKit
index 2af8815..ea62016 100644 (file)
@@ -43,6 +43,9 @@ namespace WebKit {
 
 namespace CacheStorage {
 
+using CacheIdentifier = uint64_t;
+using LockCount = uint64_t;
+
 class Engine : public ThreadSafeRefCounted<Engine> {
 public:
     ~Engine();
@@ -61,6 +64,9 @@ public:
     void putRecords(uint64_t cacheIdentifier, Vector<WebCore::DOMCacheEngine::Record>&&, WebCore::DOMCacheEngine::RecordIdentifiersCallback&&);
     void deleteMatchingRecords(uint64_t cacheIdentifier, WebCore::ResourceRequest&&, WebCore::CacheQueryOptions&&, WebCore::DOMCacheEngine::RecordIdentifiersCallback&&);
 
+    void lock(uint64_t cacheIdentifier);
+    void unlock(uint64_t cacheIdentifier);
+
     void writeFile(const String& filename, NetworkCache::Data&&, WebCore::DOMCacheEngine::CompletionCallback&&);
     void readFile(const String& filename, WTF::Function<void(const NetworkCache::Data&, int error)>&&);
     void removeFile(const String& filename);
@@ -69,7 +75,10 @@ public:
     const NetworkCache::Salt& salt() const { return m_salt.value(); }
     uint64_t nextCacheIdentifier() { return ++m_nextCacheIdentifier; }
 
+    void removeCaches(const String& origin);
+
     void clearMemoryRepresentation(const String& origin);
+    String representation();
 
 private:
     static Engine& defaultEngine();
@@ -92,6 +101,7 @@ private:
     String m_rootPath;
     RefPtr<WorkQueue> m_ioQueue;
     std::optional<NetworkCache::Salt> m_salt;
+    HashMap<CacheIdentifier, LockCount> m_cacheLocks;
 };
 
 } // namespace CacheStorage
index ed35b79..61a04c0 100644 (file)
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "CacheStorageEngine.h"
 
+#include "CacheStorageEngineCaches.h"
 #include "NetworkCacheIOChannel.h"
 #include "NetworkCacheKey.h"
 #include "NetworkProcess.h"
@@ -73,6 +74,18 @@ Cache::Cache(Caches& caches, uint64_t identifier, State state, String&& name)
 {
 }
 
+void Cache::dispose()
+{
+    m_caches.dispose(*this);
+}
+
+void Cache::clearMemoryRepresentation()
+{
+    m_records = { };
+    m_nextRecordIdentifier = 0;
+    m_state = State::Uninitialized;
+}
 void Cache::open(CompletionCallback&& callback)
 {
     if (m_state == State::Open) {
index 87e30ec..a6445e1 100644 (file)
@@ -44,6 +44,7 @@ public:
 
     uint64_t identifier() const { return m_identifier; }
     const String& name() const { return m_name; }
+    bool isActive() const { return m_state != State::Uninitialized; }
 
     Vector<WebCore::DOMCacheEngine::Record> retrieveRecords(const WebCore::URL&) const;
     WebCore::DOMCacheEngine::CacheInfo info() const { return { m_identifier, m_name }; }
@@ -51,6 +52,9 @@ public:
     void put(Vector<WebCore::DOMCacheEngine::Record>&&, WebCore::DOMCacheEngine::RecordIdentifiersCallback&&);
     void remove(WebCore::ResourceRequest&&, WebCore::CacheQueryOptions&&, WebCore::DOMCacheEngine::RecordIdentifiersCallback&&);
 
+    void dispose();
+    void clearMemoryRepresentation();
 private:
     Vector<WebCore::DOMCacheEngine::Record>* recordsFromURL(const WebCore::URL&);
     const Vector<WebCore::DOMCacheEngine::Record>* recordsFromURL(const WebCore::URL&) const;
index 1909618..2c1bfa5 100644 (file)
@@ -50,9 +50,10 @@ static inline String cachesListFilename(const String& cachesRootPath)
     return WebCore::pathByAppendingComponent(cachesRootPath, ASCIILiteral("cacheslist"));
 }
 
-Caches::Caches(Engine& engine, const String& origin)
+Caches::Caches(Engine& engine, String&& origin)
     : m_engine(&engine)
-    , m_rootPath(cachesRootPath(engine, origin))
+    , m_origin(WTFMove(origin))
+    , m_rootPath(cachesRootPath(engine, m_origin))
 {
 }
 
@@ -163,6 +164,20 @@ void Caches::remove(uint64_t identifier, CacheIdentifierCallback&& callback)
     });
 }
 
+void Caches::dispose(Cache& cache)
+{
+    auto position = m_removedCaches.findMatching([&](const auto& item) { return item.identifier() == cache.identifier(); });
+    if (position != notFound) {
+        m_removedCaches.remove(position);
+        return;
+    }
+    ASSERT(m_caches.findMatching([&](const auto& item) { return item.identifier() == cache.identifier(); }) != notFound);
+    cache.clearMemoryRepresentation();
+
+    if (m_caches.findMatching([](const auto& item) { return item.isActive(); }) == notFound)
+        clearMemoryRepresentation();
+}
+
 static inline Data encodeCacheNames(const Vector<Cache>& caches)
 {
     WTF::Persistence::Encoder encoder;
@@ -260,6 +275,8 @@ void Caches::clearMemoryRepresentation()
     m_caches.clear();
     m_isInitialized = false;
     m_storage = nullptr;
+    if (m_engine)
+        m_engine->removeCaches(m_origin);
 }
 
 bool Caches::isDirty(uint64_t updateCounter) const
@@ -279,6 +296,34 @@ CacheInfos Caches::cacheInfos(uint64_t updateCounter) const
     return { WTFMove(cacheInfos), m_updateCounter };
 }
 
+void Caches::appendRepresentation(StringBuilder& builder) const
+{
+    builder.append("{ \"persistent\": [");
+
+    bool isFirst = true;
+    for (auto& cache : m_caches) {
+        if (!isFirst)
+            builder.append(", ");
+        isFirst = false;
+        builder.append("\"");
+        builder.append(cache.name());
+        builder.append("\"");
+    }
+
+    builder.append("], \"removed\": [");
+
+    isFirst = true;
+    for (auto& cache : m_removedCaches) {
+        if (!isFirst)
+            builder.append(", ");
+        isFirst = false;
+        builder.append("\"");
+        builder.append(cache.name());
+        builder.append("\"");
+    }
+    builder.append("]}\n");
+}
+
 } // namespace CacheStorage
 
 } // namespace WebKit
index 1abfe77..f313fd1 100644 (file)
@@ -36,12 +36,13 @@ class Engine;
 
 class Caches : public RefCounted<Caches> {
 public:
-    static Ref<Caches> create(Engine& engine, const String& origin) { return adoptRef(*new Caches { engine, origin }); }
+    static Ref<Caches> create(Engine& engine, String&& origin) { return adoptRef(*new Caches { engine, WTFMove(origin) }); }
 
     void initialize(WebCore::DOMCacheEngine::CompletionCallback&&);
     void open(const String& name, WebCore::DOMCacheEngine::CacheIdentifierCallback&&);
     void remove(uint64_t identifier, WebCore::DOMCacheEngine::CacheIdentifierCallback&&);
     void clearMemoryRepresentation();
+    void dispose(Cache&);
 
     void detach();
 
@@ -49,9 +50,10 @@ public:
     WebCore::DOMCacheEngine::CacheInfos cacheInfos(uint64_t updateCounter) const;
 
     Cache* find(uint64_t identifier);
+    void appendRepresentation(StringBuilder&) const;
 
 private:
-    Caches(Engine&, const String& rootPath);
+    Caches(Engine&, String&& origin);
 
     void readCachesFromDisk(WTF::Function<void(Expected<Vector<Cache>, WebCore::DOMCacheEngine::Error>&&)>&&);
     void writeCachesToDisk(WebCore::DOMCacheEngine::CompletionCallback&&);
@@ -67,6 +69,7 @@ private:
     bool m_isInitialized { false };
     Engine* m_engine { nullptr };
     uint64_t m_updateCounter { 0 };
+    String m_origin;
     String m_rootPath;
     Vector<Cache> m_caches;
     Vector<Cache> m_removedCaches;
index cea2ebc..dfd63e6 100644 (file)
@@ -42,6 +42,17 @@ CacheStorageEngineConnection::CacheStorageEngineConnection(NetworkConnectionToWe
 {
 }
 
+CacheStorageEngineConnection::~CacheStorageEngineConnection()
+{
+    for (auto& keyValue : m_cachesLocks) {
+        auto& sessionID = keyValue.key;
+        for (auto& references : keyValue.value) {
+            ASSERT(references.value);
+            Engine::from(sessionID).unlock(references.key);
+        }
+    }
+}
+
 void CacheStorageEngineConnection::open(PAL::SessionID sessionID, uint64_t requestIdentifier, const String& origin, const String& cacheName)
 {
     Engine::from(sessionID).open(origin, cacheName, [protectedThis = makeRef(*this), this, sessionID, requestIdentifier](const CacheIdentifierOrError& result) {
@@ -63,11 +74,6 @@ void CacheStorageEngineConnection::caches(PAL::SessionID sessionID, uint64_t req
     });
 }
 
-void CacheStorageEngineConnection::clearMemoryRepresentation(PAL::SessionID sessionID, const String& origin)
-{
-    Engine::from(sessionID).clearMemoryRepresentation(origin);
-}
-
 void CacheStorageEngineConnection::retrieveRecords(PAL::SessionID sessionID, uint64_t requestIdentifier, uint64_t cacheIdentifier, WebCore::URL&& url)
 {
     Engine::from(sessionID).retrieveRecords(cacheIdentifier, WTFMove(url), [protectedThis = makeRef(*this), this, sessionID, requestIdentifier](RecordsOrError&& result) {
@@ -89,4 +95,43 @@ void CacheStorageEngineConnection::putRecords(PAL::SessionID sessionID, uint64_t
     });
 }
 
+void CacheStorageEngineConnection::reference(PAL::SessionID sessionID, uint64_t cacheIdentifier)
+{
+    auto& references = m_cachesLocks.ensure(sessionID, []() {
+        return HashMap<CacheIdentifier, LockCount> { };
+    }).iterator->value;
+    auto& counter = references.ensure(cacheIdentifier, [this]() {
+        return 0;
+    }).iterator->value;
+    if (!counter++)
+        Engine::from(sessionID).lock(cacheIdentifier);
+}
+
+void CacheStorageEngineConnection::dereference(PAL::SessionID sessionID, uint64_t cacheIdentifier)
+{
+    ASSERT(m_cachesLocks.contains(sessionID));
+    auto& references = m_cachesLocks.ensure(sessionID, []() {
+        return HashMap<CacheIdentifier, LockCount> { };
+    }).iterator->value;
+
+    auto referenceResult = references.find(cacheIdentifier);
+    ASSERT(referenceResult != references.end());
+    if (referenceResult == references.end())
+        return;
+
+    ASSERT(referenceResult->value);
+    if (!--referenceResult->value)
+        Engine::from(sessionID).unlock(cacheIdentifier);
+}
+
+void CacheStorageEngineConnection::clearMemoryRepresentation(PAL::SessionID sessionID, const String& origin)
+{
+    Engine::from(sessionID).clearMemoryRepresentation(origin);
+}
+
+void CacheStorageEngineConnection::engineRepresentation(PAL::SessionID sessionID, uint64_t requestIdentifier)
+{
+    m_connection.connection().send(Messages::WebCacheStorageConnection::EngineRepresentationCompleted(requestIdentifier, Engine::from(sessionID).representation()), sessionID.sessionID());
+}
+
 }
index adbf4ac..8f3572e 100644 (file)
@@ -44,7 +44,7 @@ class NetworkConnectionToWebProcess;
 class CacheStorageEngineConnection : public RefCounted<CacheStorageEngineConnection> {
 public:
     static Ref<CacheStorageEngineConnection> create(NetworkConnectionToWebProcess& connection) { return adoptRef(*new CacheStorageEngineConnection(connection)); }
-
+    ~CacheStorageEngineConnection();
     void didReceiveMessage(IPC::Connection&, IPC::Decoder&);
 
 private:
@@ -54,13 +54,18 @@ private:
     void remove(PAL::SessionID, uint64_t removeRequestIdentifier, uint64_t cacheIdentifier);
     void caches(PAL::SessionID, uint64_t retrieveCachesIdentifier, const String& origin, uint64_t updateCounter);
 
-    void clearMemoryRepresentation(PAL::SessionID, const String& origin);
-
     void retrieveRecords(PAL::SessionID, uint64_t requestIdentifier, uint64_t cacheIdentifier, WebCore::URL&&);
     void deleteMatchingRecords(PAL::SessionID, uint64_t requestIdentifier, uint64_t cacheIdentifier, WebCore::ResourceRequest&&, WebCore::CacheQueryOptions&&);
     void putRecords(PAL::SessionID, uint64_t requestIdentifier, uint64_t cacheIdentifier, Vector<WebCore::DOMCacheEngine::Record>&&);
 
+    void reference(PAL::SessionID, uint64_t cacheIdentifier);
+    void dereference(PAL::SessionID, uint64_t cacheIdentifier);
+
+    void clearMemoryRepresentation(PAL::SessionID, const String& origin);
+    void engineRepresentation(PAL::SessionID, uint64_t requestIdentifier);
+
     NetworkConnectionToWebProcess& m_connection;
+    HashMap<PAL::SessionID, HashMap<CacheStorage::CacheIdentifier, CacheStorage::LockCount>> m_cachesLocks;
 };
 
 }
index 4f61b67..82536d7 100644 (file)
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 messages -> CacheStorageEngineConnection {
+    Reference(PAL::SessionID sessionID, uint64_t cacheIdentifier);
+    Dereference(PAL::SessionID sessionID, uint64_t cacheIdentifier);
+
     Open(PAL::SessionID sessionID, uint64_t requestIdentifier, String origin, String cacheName);
     Remove(PAL::SessionID sessionID, uint64_t requestIdentifier, uint64_t cacheIdentifier);
     Caches(PAL::SessionID sessionID, uint64_t requestIdentifier, String origin, uint64_t updateCounter);
 
     ClearMemoryRepresentation(PAL::SessionID sessionID, String origin);
+    EngineRepresentation(PAL::SessionID sessionID, uint64_t requestIdentifier);
 
     RetrieveRecords(PAL::SessionID sessionID, uint64_t requestIdentifier, uint64_t cacheIdentifier, WebCore::URL url);
     DeleteMatchingRecords(PAL::SessionID sessionID, uint64_t requestIdentifier, uint64_t cacheIdentifier, WebCore::ResourceRequest request, struct WebCore::CacheQueryOptions options);
index 818b9d0..b442328 100644 (file)
@@ -86,10 +86,14 @@ void WebCacheStorageConnection::doBatchPutOperation(uint64_t requestIdentifier,
     connection().send(Messages::CacheStorageEngineConnection::PutRecords(m_sessionID, requestIdentifier, cacheIdentifier, records), 0);
 }
 
-void WebCacheStorageConnection::clearMemoryRepresentation(const String& origin, CompletionCallback&& callback)
+void WebCacheStorageConnection::reference(uint64_t cacheIdentifier)
 {
-    connection().send(Messages::CacheStorageEngineConnection::ClearMemoryRepresentation(m_sessionID, origin), 0);
-    callback(std::nullopt);
+    connection().send(Messages::CacheStorageEngineConnection::Reference(m_sessionID, cacheIdentifier), 0);
+}
+
+void WebCacheStorageConnection::dereference(uint64_t cacheIdentifier)
+{
+    connection().send(Messages::CacheStorageEngineConnection::Dereference(m_sessionID, cacheIdentifier), 0);
 }
 
 void WebCacheStorageConnection::openCompleted(uint64_t requestIdentifier, const CacheIdentifierOrError& result)
@@ -122,4 +126,23 @@ void WebCacheStorageConnection::putRecordsCompleted(uint64_t requestIdentifier,
     CacheStorageConnection::putRecordsCompleted(requestIdentifier, WTFMove(result));
 }
 
+void WebCacheStorageConnection::clearMemoryRepresentation(const String& origin, CompletionCallback&& callback)
+{
+    connection().send(Messages::CacheStorageEngineConnection::ClearMemoryRepresentation(m_sessionID, origin), 0);
+    callback(std::nullopt);
+}
+
+void WebCacheStorageConnection::engineRepresentation(WTF::Function<void(const String&)>&& callback)
+{
+    uint64_t requestIdentifier = ++m_engineRepresentationNextIdentifier;
+    m_engineRepresentationCallbacks.set(requestIdentifier, WTFMove(callback));
+    connection().send(Messages::CacheStorageEngineConnection::EngineRepresentation(m_sessionID, requestIdentifier), 0);
+}
+
+void WebCacheStorageConnection::engineRepresentationCompleted(uint64_t requestIdentifier, const String& result)
+{
+    if (auto callback = m_engineRepresentationCallbacks.take(requestIdentifier))
+        callback(result);
+}
+
 }
index 9f2aed2..8b5ac85 100644 (file)
@@ -60,7 +60,11 @@ private:
     void doBatchDeleteOperation(uint64_t requestIdentifier, uint64_t cacheIdentifier, const WebCore::ResourceRequest&, WebCore::CacheQueryOptions&&) final;
     void doBatchPutOperation(uint64_t requestIdentifier, uint64_t cacheIdentifier, Vector<WebCore::DOMCacheEngine::Record>&&) final;
 
+    void reference(uint64_t cacheIdentifier) final;
+    void dereference(uint64_t cacheIdentifier) final;
+
     void clearMemoryRepresentation(const String& origin, WebCore::DOMCacheEngine::CompletionCallback&&) final;
+    void engineRepresentation(WTF::Function<void(const String&)>&&) final;
 
     void openCompleted(uint64_t requestIdentifier, const WebCore::DOMCacheEngine::CacheIdentifierOrError&);
     void removeCompleted(uint64_t requestIdentifier, const WebCore::DOMCacheEngine::CacheIdentifierOrError&);
@@ -70,8 +74,12 @@ private:
     void deleteRecordsCompleted(uint64_t requestIdentifier, WebCore::DOMCacheEngine::RecordIdentifiersOrError&&);
     void putRecordsCompleted(uint64_t requestIdentifier, WebCore::DOMCacheEngine::RecordIdentifiersOrError&&);
 
+    void engineRepresentationCompleted(uint64_t requestIdentifier, const String& representation);
+
     WebCacheStorageProvider& m_provider;
     PAL::SessionID m_sessionID;
+    uint64_t m_engineRepresentationNextIdentifier { 0 };
+    HashMap<uint64_t, WTF::Function<void(const String&)>> m_engineRepresentationCallbacks;
 };
 
 }
index 5e67ec9..97329a6 100644 (file)
@@ -28,4 +28,6 @@ messages -> WebCacheStorageConnection {
     UpdateRecords(uint64_t requestIdentifier, WebCore::DOMCacheEngine::RecordsOrError result);
     DeleteRecordsCompleted(uint64_t requestIdentifier, WebCore::DOMCacheEngine::RecordIdentifiersOrError result);
     PutRecordsCompleted(uint64_t requestIdentifier, WebCore::DOMCacheEngine::RecordIdentifiersOrError result);
+
+    EngineRepresentationCompleted(uint64_t requestIdentifier, String representation);
 }