[Cache API] Support cache names persistency
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 30 Aug 2017 22:50:22 +0000 (22:50 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 30 Aug 2017 22:50:22 +0000 (22:50 +0000)
https://bugs.webkit.org/show_bug.cgi?id=175995

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

Source/WebCore:

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

Adding method to clear the memory representation of the cache storage.
Exposing it as internals so that layout tests can be used for testing persistency by combining clearing and private browsing mode.

Introducing ReadDisk and WriteDisk errors that are used by CacheStorage::Engine.

* Modules/cache/CacheStorageConnection.h:
(WebCore::CacheStorageConnection::clearMemoryRepresentation):
* Modules/cache/DOMCache.cpp:
(WebCore::DOMCache::errorToException):
* Modules/cache/DOMCache.h:
* testing/Internals.cpp:
(WebCore::Internals::clearCacheStorageMemoryRepresentation):
* testing/Internals.h:
* testing/Internals.idl:

Source/WebKit:

Adding disk read/write capacities to CacheStorage engine.
This is used to store per-origin cache names in a file.
Making Engine a thread safe refcounted object so that it does read/write in a background thread.

Introducing CacheStorage::Caches as the object managing the list of Cache objects for a given origin.
Caches will be responsible to do all the read/write operations for all of its caches.
It will be responsible for quota limitation as well.

Moving part of the logic from CacheStorage::Engine into CacheStorage::Caches.

CacheStorage::Engine is initialized asynchronously as it first creates a salt which is used
to obfuscate the names of the various files stored on disk.

In the same spirit, CacheStorage::Caches is initialized asynchronously as it needs to read from the disk the list of cache names.
Once read, the names will be stored in memory.
Added the possibility to clear this in-memory representation. This will be useful for testing.
This might also be useful to save memory when there is no more use of CacheStorage by web pages.

Introducing a new cacheStorageSubdirectoryName parameter for WebsiteDataStore so as to segment the different per session CacheStorageEngine
in direct sub folders of the main cacheStorageDirectory folder.

* CMakeLists.txt:
* NetworkProcess/cache/CacheStorageEngine.cpp:
(WebKit::CacheStorage::Engine::open):
(WebKit::CacheStorage::Engine::remove):
(WebKit::CacheStorage::Engine::retrieveCaches):
(WebKit::CacheStorage::Engine::retrieveRecords):
(WebKit::CacheStorage::Engine::initialize):
(WebKit::CacheStorage::Engine::readCachesFromDisk):
(WebKit::CacheStorage::Engine::cache):
(WebKit::CacheStorage::Engine::writeFile): Making use of default parameter to directly return to the main loop.
(WebKit::CacheStorage::Engine::readFile): Ditto.
(WebKit::CacheStorage::Engine::clearMemoryRepresentation):
* NetworkProcess/cache/CacheStorageEngine.h:
(WebKit::CacheStorage::Engine::rootPath const):
(WebKit::CacheStorage::Engine::salt const):
(WebKit::CacheStorage::Engine::nextCacheIdentifier):
* NetworkProcess/cache/CacheStorageEngineCaches.cpp: Added.
(WebKit::CacheStorage::cachesRootPath):
(WebKit::CacheStorage::cachesListFilename):
(WebKit::CacheStorage::Caches::Caches):
(WebKit::CacheStorage::Caches::initialize):
(WebKit::CacheStorage::Caches::find):
(WebKit::CacheStorage::Caches::open):
(WebKit::CacheStorage::Caches::remove):
(WebKit::CacheStorage::encodeCacheNames):
(WebKit::CacheStorage::decodeCachesNames):
(WebKit::CacheStorage::Caches::readCachesFromDisk):
(WebKit::CacheStorage::Caches::writeCachesToDisk):
(WebKit::CacheStorage::Caches::clearMemoryRepresentation):
(WebKit::CacheStorage::Caches::cacheInfos const):
* NetworkProcess/cache/CacheStorageEngineCaches.h: Added.
(WebKit::CacheStorage::Caches::create):
(WebKit::CacheStorage::Caches::isInitialized const):
(WebKit::CacheStorage::Caches::detach):
* NetworkProcess/cache/CacheStorageEngineConnection.cpp:
(WebKit::CacheStorageEngineConnection::clearMemoryRepresentation):
* NetworkProcess/cache/CacheStorageEngineConnection.h:
* NetworkProcess/cache/CacheStorageEngineConnection.messages.in:
* NetworkProcess/cache/NetworkCacheData.cpp:
(WebKit::NetworkCache::makeSalt):
* NetworkProcess/cache/NetworkCacheData.h:
* UIProcess/WebsiteData/WebsiteDataStore.h:
* WebKit.xcodeproj/project.pbxproj:
* WebProcess/Cache/WebCacheStorageConnection.cpp:
(WebKit::WebCacheStorageConnection::clearMemoryRepresentation):
* WebProcess/Cache/WebCacheStorageConnection.h:

LayoutTests:

Putting these tests in http/tests folder so that they run in a different origin and so a different Caches than other tests.
Testing private browsing caching and clearing the memory representation would probably affect other cache+prviate browsing tests that would run in parallel.

* http/tests/cache-storage/cache-persistency.https-expected.txt: Added.
* http/tests/cache-storage/cache-persistency.https.html: Added.
* http/tests/cache-storage/resources/cache-persistency-iframe.html: Added.
* platform/ios-wk1/TestExpectations: Skipping new test on WK1.
* platform/mac-wk1/TestExpectations: Ditto.

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

28 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/cache-storage/cache-persistency.https-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/cache-storage/cache-persistency.https.html [new file with mode: 0644]
LayoutTests/http/tests/cache-storage/resources/cache-persistency-iframe.html [new file with mode: 0644]
LayoutTests/platform/ios-wk1/TestExpectations
LayoutTests/platform/mac-wk1/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/Modules/cache/CacheStorageConnection.h
Source/WebCore/Modules/cache/DOMCache.cpp
Source/WebCore/Modules/cache/DOMCache.h
Source/WebCore/testing/Internals.cpp
Source/WebCore/testing/Internals.h
Source/WebCore/testing/Internals.idl
Source/WebKit/CMakeLists.txt
Source/WebKit/ChangeLog
Source/WebKit/NetworkProcess/cache/CacheStorageEngine.cpp
Source/WebKit/NetworkProcess/cache/CacheStorageEngine.h
Source/WebKit/NetworkProcess/cache/CacheStorageEngineCaches.cpp [new file with mode: 0644]
Source/WebKit/NetworkProcess/cache/CacheStorageEngineCaches.h [new file with mode: 0644]
Source/WebKit/NetworkProcess/cache/CacheStorageEngineConnection.cpp
Source/WebKit/NetworkProcess/cache/CacheStorageEngineConnection.h
Source/WebKit/NetworkProcess/cache/CacheStorageEngineConnection.messages.in
Source/WebKit/NetworkProcess/cache/NetworkCacheData.cpp
Source/WebKit/NetworkProcess/cache/NetworkCacheData.h
Source/WebKit/UIProcess/WebsiteData/WebsiteDataStore.h
Source/WebKit/WebKit.xcodeproj/project.pbxproj
Source/WebKit/WebProcess/Cache/WebCacheStorageConnection.cpp
Source/WebKit/WebProcess/Cache/WebCacheStorageConnection.h

index 9d77fac..6d7bfa3 100644 (file)
@@ -1,3 +1,19 @@
+2017-08-30  Youenn Fablet  <youenn@apple.com>
+
+        [Cache API] Support cache names persistency
+        https://bugs.webkit.org/show_bug.cgi?id=175995
+
+        Reviewed by Alex Christensen.
+
+        Putting these tests in http/tests folder so that they run in a different origin and so a different Caches than other tests.
+        Testing private browsing caching and clearing the memory representation would probably affect other cache+prviate browsing tests that would run in parallel.
+
+        * http/tests/cache-storage/cache-persistency.https-expected.txt: Added.
+        * http/tests/cache-storage/cache-persistency.https.html: Added.
+        * http/tests/cache-storage/resources/cache-persistency-iframe.html: Added.
+        * platform/ios-wk1/TestExpectations: Skipping new test on WK1.
+        * platform/mac-wk1/TestExpectations: Ditto.
+
 2017-08-30  Per Arne Vollan  <pvollan@apple.com>
 
         Update expectations for CSS regions tests after r220870.
diff --git a/LayoutTests/http/tests/cache-storage/cache-persistency.https-expected.txt b/LayoutTests/http/tests/cache-storage/cache-persistency.https-expected.txt
new file mode 100644 (file)
index 0000000..72cd20a
--- /dev/null
@@ -0,0 +1,10 @@
+
+
+PASS Cleaning existing caches 
+PASS Clear memory representation and disable disk persistency 
+PASS Cleaning added caches from previous test 1 
+PASS Clear memory representation but keep disk persistency 
+PASS Cleaning added caches from previous test 2 
+PASS Adding/Removing caches and clearing in memory representation at various times 
+PASS Cleaning added caches 
+
diff --git a/LayoutTests/http/tests/cache-storage/cache-persistency.https.html b/LayoutTests/http/tests/cache-storage/cache-persistency.https.html
new file mode 100644 (file)
index 0000000..66ccee1
--- /dev/null
@@ -0,0 +1,129 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Cache Storage: testing persistency</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+    <div id="check"></div>
+    <script>
+    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 || !window.testRunner)
+            return Promise.reject("Test requires internals");
+
+        testRunner.setPrivateBrowsingEnabled(true);
+
+        return new Promise((resolve, reject) => {
+            window.addEventListener("message", test.step_func((event) => {
+                if (event.data === "ready") {
+                    internals.clearCacheStorageMemoryRepresentation();
+                    check.innerHTML = "<iframe src='resources/cache-persistency-iframe.html#check'></iframe>";
+                    return;
+                }
+                assert_true(event.data === false, "No cache object should be found");
+                testRunner.setPrivateBrowsingEnabled(false);
+                resolve();
+            }));
+            check.innerHTML = "<iframe src='resources/cache-persistency-iframe.html'></iframe>";
+        })
+    }, "Clear memory representation and disable disk persistency");
+
+    promise_test(test => {
+        if (window.internals)
+            internals.clearCacheStorageMemoryRepresentation();
+        if (window.testRunner)
+            testRunner.setPrivateBrowsingEnabled(false);
+
+        return self.caches.keys().then(keys => {
+            var pending = [];
+            for (key of keys)
+                pending.push(self.caches.delete(keys[0]));
+            return Promise.all(pending);
+        });
+    }, "Cleaning added caches from previous test 1");
+
+    promise_test(test => {
+        if (!window.internals)
+            return Promise.reject("Test requires internals");
+
+        return new Promise((resolve, reject) => {
+            window.addEventListener("message", test.step_func((event) => {
+                if (event.data === "ready") {
+                    internals.clearCacheStorageMemoryRepresentation();
+                    check.innerHTML = "<iframe src='resources/cache-persistency-iframe.html#check'></iframe>";
+                    return;
+                }
+                assert_true(event.data === true, "A cache object should be found");
+                resolve();
+            }));
+            check.innerHTML = "<iframe src='resources/cache-persistency-iframe.html'></iframe>";
+        })
+    }, "Clear memory representation but keep disk persistency");
+
+    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 added caches from previous test 2");
+
+    promise_test(test => {
+        if (!window.internals)
+            return Promise.reject("Test requires internals");
+
+        return Promise.all([self.caches.open("test2"), self.caches.open("test1")]).then(() => {
+            return self.caches.keys()
+        }).then(keys => {
+            assert_array_equals(keys, ["test2", "test1"]);
+        }).then(() => {
+            internals.clearCacheStorageMemoryRepresentation();
+            return self.caches.keys();
+        }).then(keys => {
+            assert_array_equals(keys, ["test2", "test1"]);
+            return self.caches.delete("test2");
+        }).then(() => {
+            internals.clearCacheStorageMemoryRepresentation();
+            return self.caches.keys();
+        }).then(keys => {
+            assert_array_equals(keys, ["test1"]);
+        }).then(() => {
+            return self.caches.open("test2");
+        }).then(() => {
+            internals.clearCacheStorageMemoryRepresentation();
+            return self.caches.keys();
+        }).then(keys => {
+            assert_array_equals(keys, ["test1", "test2"]);
+        }).then(() => {
+            return Promise.all([self.caches.delete("test2"), self.caches.delete("test1")]);
+        }).then(() => {
+            internals.clearCacheStorageMemoryRepresentation();
+            return self.caches.keys();
+        }).then(keys => {
+            assert_array_equals(keys, []);
+        });
+    }, "Adding/Removing caches and clearing in memory representation at various times");
+
+    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 added caches");
+    </script>
+</body>
+</html>
+
diff --git a/LayoutTests/http/tests/cache-storage/resources/cache-persistency-iframe.html b/LayoutTests/http/tests/cache-storage/resources/cache-persistency-iframe.html
new file mode 100644 (file)
index 0000000..979c98f
--- /dev/null
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<body>
+    <script>
+function doTest()
+{
+    if (window.location.hash === "#check") {
+        self.caches.keys().then(keys => {
+            window.parent.postMessage(keys.length === 1 && keys[0] === "testCacheName", "*");
+        });
+        return;
+    }
+
+    self.caches.open("testCacheName").then(() => {
+        window.parent.postMessage("ready", "*");
+    });
+}
+doTest();
+    </script>
+</body>
+</html>
+
index 4181049..e58cafa 100644 (file)
@@ -10,6 +10,7 @@ editing/input/focus-change-with-marked-text.html [ Pass ]
 # No service worker implementation for WK1
 imported/w3c/web-platform-tests/service-workers [ Skip ]
 http/wpt/cache-storage [ Skip ]
+http/tests/cache-storage [ Skip ]
 
 # Skip WebRTC for now in WK1
 imported/w3c/web-platform-tests/webrtc [ Skip ]
index 5ff8acb..fc39787 100644 (file)
@@ -94,6 +94,7 @@ http/tests/ssl/media-stream
 # No service worker implementation for WK1
 imported/w3c/web-platform-tests/service-workers [ Skip ]
 http/wpt/cache-storage [ Skip ]
+http/tests/cache-storage [ Skip ]
 
 # Skip WebRTC for now in WK1
 imported/w3c/web-platform-tests/webrtc [ Skip ]
index 7c8ddfd..4f01cf1 100644 (file)
@@ -1,3 +1,27 @@
+2017-08-30  Youenn Fablet  <youenn@apple.com>
+
+        [Cache API] Support cache names persistency
+        https://bugs.webkit.org/show_bug.cgi?id=175995
+
+        Reviewed by Alex Christensen.
+
+        Test: http/tests/cache-storage/cache-persistency.https.html
+
+        Adding method to clear the memory representation of the cache storage.
+        Exposing it as internals so that layout tests can be used for testing persistency by combining clearing and private browsing mode.
+
+        Introducing ReadDisk and WriteDisk errors that are used by CacheStorage::Engine.
+
+        * Modules/cache/CacheStorageConnection.h:
+        (WebCore::CacheStorageConnection::clearMemoryRepresentation):
+        * Modules/cache/DOMCache.cpp:
+        (WebCore::DOMCache::errorToException):
+        * Modules/cache/DOMCache.h:
+        * testing/Internals.cpp:
+        (WebCore::Internals::clearCacheStorageMemoryRepresentation):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+
 2017-08-30  Matt Lewis  <jlewis3@apple.com>
 
         Unreviewed, rolling out r221384.
index f2080dc..4c42271 100644 (file)
@@ -45,6 +45,9 @@ public:
     void batchDeleteOperation(uint64_t cacheIdentifier, const ResourceRequest&, CacheQueryOptions&&, DOMCache::RecordIdentifiersCallback&&);
     void batchPutOperation(uint64_t cacheIdentifier, Vector<DOMCache::Record>&&, DOMCache::RecordIdentifiersCallback&&);
 
+    // Used only for testing purposes.
+    virtual void clearMemoryRepresentation(const String& /* origin */, DOMCache::CompletionCallback&& callback) { callback(DOMCache::Error::NotImplemented); }
+
 protected:
     CacheStorageConnection() =  default;
 
index 33da808..33f274d 100644 (file)
@@ -40,6 +40,10 @@ Exception errorToException(Error error)
     switch (error) {
     case Error::NotImplemented:
         return Exception { NotSupportedError, ASCIILiteral("Not implemented") };
+    case Error::ReadDisk:
+        return Exception { TypeError, ASCIILiteral("Failed reading data from the file system") };
+    case Error::WriteDisk:
+        return Exception { TypeError, ASCIILiteral("Failed writing data to the file system") };
     default:
         return Exception { TypeError, ASCIILiteral("Internal error") };
     }
index c2123a9..eebe667 100644 (file)
@@ -40,6 +40,8 @@ namespace DOMCache {
 
 enum class Error {
     NotImplemented,
+    ReadDisk,
+    WriteDisk,
     Internal
 };
 
@@ -94,6 +96,8 @@ template<> struct EnumTraits<WebCore::DOMCache::Error> {
     using values = EnumValues<
         WebCore::DOMCache::Error,
         WebCore::DOMCache::Error::NotImplemented,
+        WebCore::DOMCache::Error::ReadDisk,
+        WebCore::DOMCache::Error::WriteDisk,
         WebCore::DOMCache::Error::Internal
     >;
 };
index 84275df..2f9af02 100644 (file)
@@ -39,6 +39,8 @@
 #include "CSSMediaRule.h"
 #include "CSSStyleRule.h"
 #include "CSSSupportsRule.h"
+#include "CacheStorageConnection.h"
+#include "CacheStorageProvider.h"
 #include "CachedImage.h"
 #include "CachedResourceLoader.h"
 #include "Chrome.h"
@@ -4136,4 +4138,19 @@ String Internals::audioSessionCategory() const
     return emptyString();
 }
 
+void Internals::clearCacheStorageMemoryRepresentation()
+{
+    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->clearMemoryRepresentation(document->securityOrigin().toString(), [](std::optional<DOMCache::Error>&&) { });
+}
+
 } // namespace WebCore
index d27827d..2b81460 100644 (file)
@@ -43,6 +43,7 @@
 namespace WebCore {
 
 class AudioContext;
+class CacheStorageConnection;
 class DOMRect;
 class DOMRectList;
 class DOMURL;
@@ -598,6 +599,8 @@ public:
 
     String audioSessionCategory() const;
 
+    void clearCacheStorageMemoryRepresentation();
+
 private:
     explicit Internals(Document&);
     Document* contextDocument() const;
@@ -618,6 +621,7 @@ private:
 #endif
 
     std::unique_ptr<InspectorStubFrontend> m_inspectorFrontend;
+    RefPtr<CacheStorageConnection> m_cacheStorageConnection;
 };
 
 } // namespace WebCore
index 4271ae2..e87a74c 100644 (file)
@@ -544,5 +544,7 @@ enum EventThrottlingBehavior {
     [Conditional=MEDIA_STREAM] void removeMediaStreamTrack(MediaStream stream, MediaStreamTrack track);
     [Conditional=MEDIA_STREAM] void simulateMediaStreamTrackCaptureSourceFailure(MediaStreamTrack track);
 
+    void clearCacheStorageMemoryRepresentation();
+
     DOMString audioSessionCategory();
 };
index cd418b9..24d6367 100644 (file)
@@ -117,6 +117,7 @@ set(WebKit2_SOURCES
     NetworkProcess/PingLoad.cpp
 
     NetworkProcess/cache/CacheStorageEngine.cpp
+    NetworkProcess/cache/CacheStorageEngineCaches.cpp
     NetworkProcess/cache/CacheStorageEngineConnection.cpp
     NetworkProcess/cache/NetworkCache.cpp
     NetworkProcess/cache/NetworkCacheBlobStorage.cpp
index 5879fe8..de4cab8 100644 (file)
@@ -1,3 +1,78 @@
+2017-08-30  Youenn Fablet  <youenn@apple.com>
+
+        [Cache API] Support cache names persistency
+        https://bugs.webkit.org/show_bug.cgi?id=175995
+
+        Reviewed by Alex Christensen.
+
+        Adding disk read/write capacities to CacheStorage engine.
+        This is used to store per-origin cache names in a file.
+        Making Engine a thread safe refcounted object so that it does read/write in a background thread.
+
+        Introducing CacheStorage::Caches as the object managing the list of Cache objects for a given origin.
+        Caches will be responsible to do all the read/write operations for all of its caches.
+        It will be responsible for quota limitation as well.
+
+        Moving part of the logic from CacheStorage::Engine into CacheStorage::Caches.
+
+        CacheStorage::Engine is initialized asynchronously as it first creates a salt which is used
+        to obfuscate the names of the various files stored on disk.
+
+        In the same spirit, CacheStorage::Caches is initialized asynchronously as it needs to read from the disk the list of cache names.
+        Once read, the names will be stored in memory.
+        Added the possibility to clear this in-memory representation. This will be useful for testing.
+        This might also be useful to save memory when there is no more use of CacheStorage by web pages.
+
+        Introducing a new cacheStorageSubdirectoryName parameter for WebsiteDataStore so as to segment the different per session CacheStorageEngine
+        in direct sub folders of the main cacheStorageDirectory folder.
+
+        * CMakeLists.txt:
+        * NetworkProcess/cache/CacheStorageEngine.cpp:
+        (WebKit::CacheStorage::Engine::open):
+        (WebKit::CacheStorage::Engine::remove):
+        (WebKit::CacheStorage::Engine::retrieveCaches):
+        (WebKit::CacheStorage::Engine::retrieveRecords):
+        (WebKit::CacheStorage::Engine::initialize):
+        (WebKit::CacheStorage::Engine::readCachesFromDisk):
+        (WebKit::CacheStorage::Engine::cache):
+        (WebKit::CacheStorage::Engine::writeFile): Making use of default parameter to directly return to the main loop.
+        (WebKit::CacheStorage::Engine::readFile): Ditto.
+        (WebKit::CacheStorage::Engine::clearMemoryRepresentation):
+        * NetworkProcess/cache/CacheStorageEngine.h:
+        (WebKit::CacheStorage::Engine::rootPath const):
+        (WebKit::CacheStorage::Engine::salt const):
+        (WebKit::CacheStorage::Engine::nextCacheIdentifier):
+        * NetworkProcess/cache/CacheStorageEngineCaches.cpp: Added.
+        (WebKit::CacheStorage::cachesRootPath):
+        (WebKit::CacheStorage::cachesListFilename):
+        (WebKit::CacheStorage::Caches::Caches):
+        (WebKit::CacheStorage::Caches::initialize):
+        (WebKit::CacheStorage::Caches::find):
+        (WebKit::CacheStorage::Caches::open):
+        (WebKit::CacheStorage::Caches::remove):
+        (WebKit::CacheStorage::encodeCacheNames):
+        (WebKit::CacheStorage::decodeCachesNames):
+        (WebKit::CacheStorage::Caches::readCachesFromDisk):
+        (WebKit::CacheStorage::Caches::writeCachesToDisk):
+        (WebKit::CacheStorage::Caches::clearMemoryRepresentation):
+        (WebKit::CacheStorage::Caches::cacheInfos const):
+        * NetworkProcess/cache/CacheStorageEngineCaches.h: Added.
+        (WebKit::CacheStorage::Caches::create):
+        (WebKit::CacheStorage::Caches::isInitialized const):
+        (WebKit::CacheStorage::Caches::detach):
+        * NetworkProcess/cache/CacheStorageEngineConnection.cpp:
+        (WebKit::CacheStorageEngineConnection::clearMemoryRepresentation):
+        * NetworkProcess/cache/CacheStorageEngineConnection.h:
+        * NetworkProcess/cache/CacheStorageEngineConnection.messages.in:
+        * NetworkProcess/cache/NetworkCacheData.cpp:
+        (WebKit::NetworkCache::makeSalt):
+        * NetworkProcess/cache/NetworkCacheData.h:
+        * UIProcess/WebsiteData/WebsiteDataStore.h:
+        * WebKit.xcodeproj/project.pbxproj:
+        * WebProcess/Cache/WebCacheStorageConnection.cpp:
+        (WebKit::WebCacheStorageConnection::clearMemoryRepresentation):
+        * WebProcess/Cache/WebCacheStorageConnection.h:
+
 2017-08-30  Brady Eidson  <beidson@apple.com>
 
         Add "Identified" base class to replace a whole bunch of custom identifier generators.
index 86cfcd6..a45f73f 100644 (file)
@@ -84,42 +84,40 @@ void Engine::open(const String& origin, const String& cacheName, CacheIdentifier
             return;
         }
 
-        auto& caches = cachesOrError.value().get();
+        Caches& caches = cachesOrError.value();
 
-        auto position = caches.findMatching([&](const auto& item) { return item.name == cacheName; });
-        if (position == notFound) {
-            uint64_t cacheIdentifier = ++m_nextCacheIdentifier;
-            caches.append(Cache { cacheIdentifier, cacheName, Vector<Record>(), 0 });
-            writeCachesToDisk([cacheIdentifier, callback = WTFMove(callback)](std::optional<Error>&& error) {
-                if (error) {
-                    callback(makeUnexpected(error.value()));
-                    return;
-                }
-                callback(cacheIdentifier);
-            });
-        } else
-            callback(caches[position].identifier);
+        if (auto* cache = caches.find(cacheName)) {
+            callback(cache->identifier);
+            return;
+        }
 
+        caches.open(String { cacheName }, [callback = WTFMove(callback)](const CacheIdentifierOrError& result) mutable {
+            callback(result);
+        });
     });
 }
 
 void Engine::remove(uint64_t cacheIdentifier, CacheIdentifierCallback&& callback)
 {
-    std::optional<Cache> removedCache;
+    Caches* cachesToModify = nullptr;
     for (auto& caches : m_caches.values()) {
-        auto position = caches.findMatching([&](const auto& item) { return item.identifier == cacheIdentifier; });
-        if (position != notFound) {
-            removedCache = WTFMove(caches[position]);
-            caches.remove(position);
+        auto* cacheToRemove = caches->find(cacheIdentifier);
+        if (cacheToRemove) {
+            cachesToModify = caches.ptr();
             break;
         }
     }
-    if (!removedCache) {
-        callback(makeUnexpected(Error::Internal));
+    if (!cachesToModify) {
+        auto position = m_removedCaches.findMatching([&](const auto& item) { return item.identifier == cacheIdentifier; });
+        if (position == notFound) {
+            callback(makeUnexpected(Error::Internal));
+            return;
+        }
+        callback(cacheIdentifier);
         return;
     }
-    m_removedCaches.append(WTFMove(removedCache.value()));
-    writeCachesToDisk([cacheIdentifier, callback = WTFMove(callback)](std::optional<Error>&& error) {
+
+    cachesToModify->remove(cacheIdentifier, [cacheIdentifier, callback = WTFMove(callback)](std::optional<Error>&& error) {
         if (error) {
             callback(makeUnexpected(error.value()));
             return;
@@ -136,14 +134,7 @@ void Engine::retrieveCaches(const String& origin, CacheInfosCallback&& callback)
             return;
         }
 
-        auto& caches = cachesOrError.value().get();
-
-        Vector<CacheInfo> cachesInfo;
-        cachesInfo.reserveInitialCapacity(caches.size());
-        for (auto& cache : caches)
-            cachesInfo.uncheckedAppend(CacheInfo { cache.identifier, cache.name});
-
-        callback(WTFMove(cachesInfo));
+        callback(cachesOrError.value().get().cacheInfos());
     });
 }
 
@@ -159,7 +150,7 @@ void Engine::retrieveRecords(uint64_t cacheIdentifier, RecordsCallback&& callbac
 
         Vector<Record> copy;
         copy.reserveInitialCapacity(records.size());
-        for (auto& record : result.value().get().records)
+        for (auto& record : records)
             copy.uncheckedAppend(record.copy());
 
         callback(WTFMove(copy));
@@ -244,21 +235,62 @@ void Engine::deleteMatchingRecords(uint64_t cacheIdentifier, WebCore::ResourceRe
     });
 }
 
-void Engine::writeCachesToDisk(Function<void(std::optional<Error>&&)>&& callback)
+void Engine::initialize(Function<void(std::optional<Error>&&)>&& callback)
 {
-    // FIXME: Implement writing.
-    callback(std::nullopt);
+    if (m_salt) {
+        callback(std::nullopt);
+        return;
+    }
+
+    if (!shouldPersist()) {
+        m_salt = makeSalt();
+        callback(std::nullopt);
+        return;
+    }
+
+    String saltPath = WebCore::pathByAppendingComponent(m_rootPath, ASCIILiteral("salt"));
+    m_ioQueue->dispatch([protectedThis = makeRef(*this), this, callback = WTFMove(callback), saltPath = WTFMove(saltPath)] () mutable {
+        WebCore::makeAllDirectories(m_rootPath);
+        RunLoop::main().dispatch([protectedThis = WTFMove(protectedThis), this, salt = readOrMakeSalt(saltPath), callback = WTFMove(callback)]() mutable {
+            if (!salt) {
+                callback(Error::WriteDisk);
+                return;
+            }
+            m_salt = WTFMove(salt);
+            callback(std::nullopt);
+        });
+    });
 }
 
 void Engine::readCachesFromDisk(const String& origin, CachesCallback&& callback)
 {
-    // FIXME: Implement reading.
-
-    auto& caches = m_caches.ensure(origin, [] {
-        return Vector<Cache>();
+    auto& caches = m_caches.ensure(origin, [&origin, this] {
+        return Caches::create(*this, origin);
     }).iterator->value;
 
-    callback(std::reference_wrapper<Vector<Cache>> { caches });
+    if (caches->isInitialized()) {
+        callback(std::reference_wrapper<Caches> { caches.get() });
+        return;
+    }
+
+    initialize([this, origin, callback = WTFMove(callback)](std::optional<Error>&& error) mutable {
+        if (error) {
+            callback(makeUnexpected(error.value()));
+            return;
+        }
+
+        auto caches = m_caches.get(origin);
+        ASSERT(caches);
+
+        caches->initialize([callback = WTFMove(callback), caches](std::optional<Error>&& error) mutable {
+            if (error) {
+                callback(makeUnexpected(error.value()));
+                return;
+            }
+
+            callback(std::reference_wrapper<Caches> { *caches });
+        });
+    });
 }
 
 void Engine::readCache(uint64_t cacheIdentifier, CacheCallback&& callback)
@@ -288,11 +320,8 @@ Cache* Engine::cache(uint64_t cacheIdentifier)
 {
     Cache* result = nullptr;
     for (auto& caches : m_caches.values()) {
-        auto position = caches.findMatching([&](const auto& item) { return item.identifier == cacheIdentifier; });
-        if (position != notFound) {
-            result = &caches[position];
+        if ((result = caches->find(cacheIdentifier)))
             break;
-        }
     }
     if (!result) {
         auto position = m_removedCaches.findMatching([&](const auto& item) { return item.identifier == cacheIdentifier; });
@@ -322,17 +351,15 @@ void Engine::writeFile(const String& filename, NetworkCache::Data&& data, WebCor
         return;
     }
 
-    m_ioQueue->dispatch([this, protectedThis = makeRef(*this), callback = WTFMove(callback), data = WTFMove(data), filename = filename.isolatedCopy()] () mutable {
+    m_ioQueue->dispatch([callback = WTFMove(callback), data = WTFMove(data), filename = filename.isolatedCopy()] () mutable {
         auto channel = IOChannel::open(filename, IOChannel::Type::Create);
-        channel->write(0, data, m_ioQueue.get(), [callback = WTFMove(callback)](int error) mutable {
-            RunLoop::main().dispatch([callback = WTFMove(callback), error]() mutable {
-                if (error) {
-                    // FIXME: Use specific filesystem error.
-                    callback(Error::Internal);
-                    return;
-                }
-                callback(std::nullopt);
-            });
+        channel->write(0, data, nullptr, [callback = WTFMove(callback)](int error) mutable {
+            ASSERT(RunLoop::isMain());
+            if (error) {
+                callback(Error::WriteDisk);
+                return;
+            }
+            callback(std::nullopt);
         });
     });
 }
@@ -344,7 +371,7 @@ void Engine::readFile(const String& filename, WTF::Function<void(const NetworkCa
         return;
     }
 
-    m_ioQueue->dispatch([this, protectedThis = makeRef(*this), callback = WTFMove(callback), filename = filename.isolatedCopy()]() mutable {
+    m_ioQueue->dispatch([callback = WTFMove(callback), filename = filename.isolatedCopy()]() mutable {
         auto channel = IOChannel::open(filename, IOChannel::Type::Read);
         if (channel->fileDescriptor() < 0) {
             RunLoop::main().dispatch([callback = WTFMove(callback)]() mutable {
@@ -353,10 +380,10 @@ void Engine::readFile(const String& filename, WTF::Function<void(const NetworkCa
             return;
         }
 
-        channel->read(0, std::numeric_limits<size_t>::max(), m_ioQueue.get(), [callback = WTFMove(callback)](const Data& data, int error) mutable {
-            RunLoop::main().dispatch([callback = WTFMove(callback), data, error]() mutable {
-                callback(data, error);
-            });
+        channel->read(0, std::numeric_limits<size_t>::max(), nullptr, [callback = WTFMove(callback)](const Data& data, int error) mutable {
+            // FIXME: We should do the decoding in the background thread.
+            ASSERT(RunLoop::isMain());
+            callback(data, error);
         });
     });
 }
@@ -371,6 +398,15 @@ void Engine::removeFile(const String& filename)
     });
 }
 
+void Engine::clearMemoryRepresentation(const String& origin)
+{
+    readCachesFromDisk(origin, [](CachesOrError&& result) {
+        if (!result.hasValue())
+            return;
+        result.value().get().clearMemoryRepresentation();
+    });
+}
+
 } // namespace CacheStorage
 
 } // namespace WebKit
index b5e2260..93a74c1 100644 (file)
@@ -25,7 +25,7 @@
 
 #pragma once
 
-#include "CacheStorageEngineCache.h"
+#include "CacheStorageEngineCaches.h"
 #include "NetworkCacheData.h"
 #include <wtf/HashMap.h>
 #include <wtf/ThreadSafeRefCounted.h>
@@ -63,13 +63,19 @@ public:
     void readFile(const String& filename, WTF::Function<void(const NetworkCache::Data&, int error)>&&);
     void removeFile(const String& filename);
 
+    const String& rootPath() const { return m_rootPath; }
+    const NetworkCache::Salt& salt() const { return m_salt.value(); }
+    uint64_t nextCacheIdentifier() { return ++m_nextCacheIdentifier; }
+
+    void clearMemoryRepresentation(const String& origin);
+
 private:
     static Engine& defaultEngine();
     explicit Engine(String&& rootPath);
 
-    void writeCachesToDisk(WebCore::DOMCache::CompletionCallback&&);
+    void initialize(Function<void(std::optional<WebCore::DOMCache::Error>&&)>&&);
 
-    using CachesOrError = Expected<std::reference_wrapper<Vector<Cache>>, WebCore::DOMCache::Error>;
+    using CachesOrError = Expected<std::reference_wrapper<Caches>, WebCore::DOMCache::Error>;
     using CachesCallback = WTF::Function<void(CachesOrError&&)>;
     void readCachesFromDisk(const String& origin, CachesCallback&&);
 
@@ -84,11 +90,12 @@ private:
 
     Cache* cache(uint64_t cacheIdentifier);
 
-    HashMap<String, Vector<Cache>> m_caches;
+    HashMap<String, Ref<Caches>> m_caches;
     Vector<Cache> m_removedCaches;
     uint64_t m_nextCacheIdentifier { 0 };
     String m_rootPath;
     RefPtr<WorkQueue> m_ioQueue;
+    std::optional<NetworkCache::Salt> m_salt;
 };
 
 } // namespace CacheStorage
diff --git a/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCaches.cpp b/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCaches.cpp
new file mode 100644 (file)
index 0000000..90d6a10
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2017 Apple Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "CacheStorageEngine.h"
+
+#include "NetworkCacheCoders.h"
+#include <wtf/text/StringBuilder.h>
+
+using namespace WebCore::DOMCache;
+using namespace WebKit::NetworkCache;
+
+namespace WebKit {
+
+namespace CacheStorage {
+
+static inline String cachesRootPath(Engine& engine, const String& origin)
+{
+    Key key(engine.rootPath(), { }, { }, origin, engine.salt());
+    return WebCore::pathByAppendingComponent(engine.rootPath(), key.partitionHashAsString());
+}
+
+static inline String cachesListFilename(const String& cachesRootPath)
+{
+    return WebCore::pathByAppendingComponent(cachesRootPath, ASCIILiteral("cacheslist"));
+}
+
+Caches::Caches(Engine& engine, const String& origin)
+    : m_engine(&engine)
+    , m_rootPath(cachesRootPath(engine, origin))
+{
+}
+
+void Caches::initialize(WebCore::DOMCache::CompletionCallback&& callback)
+{
+    if (m_isInitialized || !m_engine || !m_engine->shouldPersist()) {
+        callback(std::nullopt);
+        return;
+    }
+
+    if (m_storage) {
+        m_pendingInitializationCallbacks.append(WTFMove(callback));
+        return;
+    }
+
+    auto storage = Storage::open(m_rootPath, Storage::Mode::Normal);
+    if (!storage) {
+        callback(Error::WriteDisk);
+        return;
+    }
+    m_storage = storage.releaseNonNull();
+    readCachesFromDisk([this, callback = WTFMove(callback)](Expected<Vector<Cache>, Error>&& result) {
+        if (!result.hasValue()) {
+            callback(result.error());
+
+            auto pendingCallbacks = WTFMove(m_pendingInitializationCallbacks);
+            for (auto& callback : pendingCallbacks)
+                callback(result.error());
+            return;
+        }
+        m_caches = WTFMove(result.value());
+        m_isInitialized = true;
+        callback(std::nullopt);
+
+        auto pendingCallbacks = WTFMove(m_pendingInitializationCallbacks);
+        for (auto& callback : pendingCallbacks)
+            callback(std::nullopt);
+    });
+}
+
+Cache* Caches::find(const String& name)
+{
+    auto position = m_caches.findMatching([&](const auto& item) { return item.name == name; });
+    return (position != notFound) ? &m_caches[position] : nullptr;
+}
+
+Cache* Caches::find(uint64_t identifier)
+{
+    auto position = m_caches.findMatching([&](const auto& item) { return item.identifier == identifier; });
+    if (position != notFound)
+        return &m_caches[position];
+
+    position = m_removedCaches.findMatching([&](const auto& item) { return item.identifier == identifier; });
+    return (position != notFound) ? &m_removedCaches[position] : nullptr;
+}
+
+void Caches::open(String&& name, CacheIdentifierCallback&& callback)
+{
+    ASSERT(m_engine);
+
+    uint64_t cacheIdentifier = m_engine->nextCacheIdentifier();
+    m_caches.append(Cache { cacheIdentifier, WTFMove(name), { }, 0 });
+    writeCachesToDisk([callback = WTFMove(callback), cacheIdentifier](std::optional<Error>&& error) mutable {
+        if (error) {
+            callback(makeUnexpected(error.value()));
+            return;
+        }
+        callback(cacheIdentifier);
+    });
+}
+
+void Caches::remove(uint64_t identifier, CompletionCallback&& callback)
+{
+    ASSERT(m_engine);
+
+    auto position = m_caches.findMatching([&](const auto& item) { return item.identifier == identifier; });
+
+    ASSERT(position != notFound);
+
+    auto cache = WTFMove(m_caches[position]);
+    m_caches.remove(position);
+    m_removedCaches.append(WTFMove(cache));
+
+    writeCachesToDisk(WTFMove(callback));
+}
+
+static inline Data encodeCacheNames(const Vector<Cache>& caches)
+{
+    WTF::Persistence::Encoder encoder;
+
+    uint64_t size = caches.size();
+    encoder << size;
+    for (auto& cache : caches)
+        encoder << cache.name;
+
+    return Data { encoder.buffer(), encoder.bufferSize() };
+}
+
+static inline Expected<Vector<String>, Error> decodeCachesNames(const Data& data, int error)
+{
+    if (error)
+        return makeUnexpected(Error::ReadDisk);
+
+    WTF::Persistence::Decoder decoder(data.data(), data.size());
+    uint64_t count;
+    if (!decoder.decode(count))
+        return makeUnexpected(Error::ReadDisk);
+
+    Vector<String> names;
+    names.reserveInitialCapacity(count);
+    for (size_t index = 0; index < count; ++index) {
+        String name;
+        if (!decoder.decode(name))
+            return makeUnexpected(Error::ReadDisk);
+
+        names.uncheckedAppend(WTFMove(name));
+    }
+    return names;
+}
+
+void Caches::readCachesFromDisk(WTF::Function<void(Expected<Vector<Cache>, Error>&&)>&& callback)
+{
+    ASSERT(m_engine);
+    ASSERT(!m_isInitialized);
+    ASSERT(m_caches.isEmpty());
+
+    if (!m_engine->shouldPersist()) {
+        callback(Vector<Cache> { });
+        return;
+    }
+
+    auto filename = cachesListFilename(m_rootPath);
+    if (!WebCore::fileExists(filename)) {
+        callback(Vector<Cache> { });
+        return;
+    }
+
+    m_engine->readFile(filename, [protectedThis = makeRef(*this), this, callback = WTFMove(callback)](const Data& data, int error) mutable {
+        if (!m_engine) {
+            callback(Vector<Cache> { });
+            return;
+        }
+
+        auto result = decodeCachesNames(data, error);
+        if (!result.hasValue()) {
+            callback(makeUnexpected(result.error()));
+            return;
+        }
+        Vector<Cache> caches;
+        caches.reserveInitialCapacity(result.value().size());
+        for (auto& name : result.value())
+            caches.uncheckedAppend(Cache { m_engine->nextCacheIdentifier(), WTFMove(name), { }, 0 });
+
+        callback(WTFMove(caches));
+    });
+}
+
+void Caches::writeCachesToDisk(CompletionCallback&& callback)
+{
+    if (!m_engine->shouldPersist()) {
+        callback(std::nullopt);
+        return;
+    }
+
+    ASSERT(m_engine);
+
+    if (m_caches.isEmpty()) {
+        m_engine->removeFile(cachesListFilename(m_rootPath));
+        callback(std::nullopt);
+        return;
+    }
+
+    m_engine->writeFile(cachesListFilename(m_rootPath), encodeCacheNames(m_caches), [callback = WTFMove(callback)](std::optional<Error>&& error) mutable {
+        callback(WTFMove(error));
+    });
+}
+
+void Caches::clearMemoryRepresentation()
+{
+    m_caches.clear();
+    m_isInitialized = false;
+    m_storage = nullptr;
+}
+
+Vector<CacheInfo> Caches::cacheInfos() const
+{
+    Vector<CacheInfo> cacheInfos;
+    cacheInfos.reserveInitialCapacity(m_caches.size());
+    for (auto& cache : m_caches)
+        cacheInfos.uncheckedAppend(CacheInfo { cache.identifier, cache.name });
+    return cacheInfos;
+}
+
+} // namespace CacheStorage
+
+} // namespace WebKit
diff --git a/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCaches.h b/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCaches.h
new file mode 100644 (file)
index 0000000..a55c51d
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include "CacheStorageEngineCache.h"
+#include "NetworkCacheStorage.h"
+
+namespace WebKit {
+
+namespace CacheStorage {
+
+class Engine;
+
+class Caches : public RefCounted<Caches> {
+public:
+    static Ref<Caches> create(Engine& engine, const String& origin) { return adoptRef(*new Caches { engine, origin }); }
+
+    void initialize(WebCore::DOMCache::CompletionCallback&&);
+
+    bool isInitialized() const { return m_isInitialized; }
+
+    Cache* find(const String& name);
+    Cache* find(uint64_t identifier);
+
+    void open(String&& name, WebCore::DOMCache::CacheIdentifierCallback&&);
+    void remove(uint64_t identifier, WebCore::DOMCache::CompletionCallback&&);
+
+    Vector<WebCore::DOMCache::CacheInfo> cacheInfos() const;
+
+    void clearMemoryRepresentation();
+    void detach() { m_engine = nullptr; }
+
+private:
+    Caches(Engine&, const String& rootPath);
+
+    void readCachesFromDisk(WTF::Function<void(Expected<Vector<Cache>, WebCore::DOMCache::Error>&&)>&&);
+    void writeCachesToDisk(WebCore::DOMCache::CompletionCallback&&);
+
+    bool m_isInitialized { false };
+    Engine* m_engine { nullptr };
+    String m_rootPath;
+    Vector<Cache> m_caches;
+    Vector<Cache> m_removedCaches;
+    RefPtr<NetworkCache::Storage> m_storage;
+    Vector<WebCore::DOMCache::CompletionCallback> m_pendingInitializationCallbacks;
+};
+
+} // namespace CacheStorage
+
+} // namespace WebKit
index bf49938..c02cafd 100644 (file)
@@ -63,6 +63,11 @@ 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::records(PAL::SessionID sessionID, uint64_t requestIdentifier, uint64_t cacheIdentifier)
 {
     Engine::from(sessionID).retrieveRecords(cacheIdentifier, [protectedThis = makeRef(*this), this, sessionID, requestIdentifier](RecordsOrError&& result) {
index fb3a664..082d1e4 100644 (file)
@@ -54,6 +54,8 @@ private:
     void remove(PAL::SessionID, uint64_t removeRequestIdentifier, uint64_t cacheIdentifier);
     void caches(PAL::SessionID, uint64_t retrieveCachesIdentifier, const String& origin);
 
+    void clearMemoryRepresentation(PAL::SessionID, const String& origin);
+
     void records(PAL::SessionID, uint64_t requestIdentifier, uint64_t cacheIdentifier);
     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::DOMCache::Record>&&);
index 5922eed..4eac748 100644 (file)
@@ -25,6 +25,8 @@ messages -> CacheStorageEngineConnection {
     Remove(PAL::SessionID sessionID, uint64_t requestIdentifier, uint64_t cacheIdentifier);
     Caches(PAL::SessionID sessionID, uint64_t requestIdentifier, String origin);
 
+    ClearMemoryRepresentation(PAL::SessionID sessionID, String origin);
+
     Records(PAL::SessionID sessionID, uint64_t requestIdentifier, uint64_t cacheIdentifier);
     DeleteMatchingRecords(PAL::SessionID sessionID, uint64_t requestIdentifier, uint64_t cacheIdentifier, WebCore::ResourceRequest request, struct WebCore::CacheQueryOptions options);
     PutRecords(PAL::SessionID sessionID, uint64_t requestIdentifier, uint64_t cacheIdentifier, Vector<WebCore::DOMCache::Record> record);
index 31ae48f..746dd6e 100644 (file)
@@ -128,7 +128,7 @@ bool bytesEqual(const Data& a, const Data& b)
     return !memcmp(a.data(), b.data(), a.size());
 }
 
-static Salt makeSalt()
+Salt makeSalt()
 {
     Salt salt;
     static_assert(salt.size() == 8, "Salt size");
index 600b5e4..c2c99d5 100644 (file)
@@ -105,6 +105,7 @@ Data mapFile(const char* path);
 
 using Salt = std::array<uint8_t, 8>;
 
+Salt makeSalt();
 std::optional<Salt> readOrMakeSalt(const String& path);
 SHA1::Digest computeSHA1(const Data&, const Salt&);
 
index 3af1167..1cdb643 100644 (file)
@@ -69,6 +69,7 @@ class WebsiteDataStore : public RefCounted<WebsiteDataStore>, public WebProcessL
 public:
     struct Configuration {
         String cacheStorageDirectory;
+        String cacheStorageSubdirectoryName;
         String networkCacheDirectory;
         String applicationCacheDirectory;
         String applicationCacheFlatFileSubdirectoryName;
index 82bbeb9..831db73 100644 (file)
                413075B21DE85F580039EC69 /* LibWebRTCSocketFactory.h in Headers */ = {isa = PBXBuildFile; fileRef = 413075A61DE85EE70039EC69 /* LibWebRTCSocketFactory.h */; };
                413075B31DE85F580039EC69 /* LibWebRTCProvider.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 413075A71DE85EE70039EC69 /* LibWebRTCProvider.cpp */; };
                413075B41DE85F580039EC69 /* LibWebRTCProvider.h in Headers */ = {isa = PBXBuildFile; fileRef = 413075A81DE85EE70039EC69 /* LibWebRTCProvider.h */; };
+               4135FBD11F4FB8090074C47B /* CacheStorageEngineCaches.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4135FBCF1F4FB7F20074C47B /* CacheStorageEngineCaches.cpp */; };
                41897ECF1F415D620016FA42 /* WebCacheStorageConnection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41897ECE1F415D5C0016FA42 /* WebCacheStorageConnection.cpp */; };
                41897ED01F415D650016FA42 /* WebCacheStorageProvider.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 41897ECC1F415D5C0016FA42 /* WebCacheStorageProvider.cpp */; };
                41897ED11F415D680016FA42 /* WebCacheStorageConnection.h in Headers */ = {isa = PBXBuildFile; fileRef = 41897ECD1F415D5C0016FA42 /* WebCacheStorageConnection.h */; };
                413075A61DE85EE70039EC69 /* LibWebRTCSocketFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LibWebRTCSocketFactory.h; path = Network/webrtc/LibWebRTCSocketFactory.h; sourceTree = "<group>"; };
                413075A71DE85EE70039EC69 /* LibWebRTCProvider.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; name = LibWebRTCProvider.cpp; path = Network/webrtc/LibWebRTCProvider.cpp; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };
                413075A81DE85EE70039EC69 /* LibWebRTCProvider.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = LibWebRTCProvider.h; path = Network/webrtc/LibWebRTCProvider.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
+               4135FBCF1F4FB7F20074C47B /* CacheStorageEngineCaches.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CacheStorageEngineCaches.cpp; sourceTree = "<group>"; };
+               4135FBD01F4FB7F20074C47B /* CacheStorageEngineCaches.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CacheStorageEngineCaches.h; sourceTree = "<group>"; };
                41897ECB1F415D5C0016FA42 /* WebCacheStorageConnection.messages.in */ = {isa = PBXFileReference; lastKnownFileType = text; path = WebCacheStorageConnection.messages.in; sourceTree = "<group>"; };
                41897ECC1F415D5C0016FA42 /* WebCacheStorageProvider.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = WebCacheStorageProvider.cpp; sourceTree = "<group>"; };
                41897ECD1F415D5C0016FA42 /* WebCacheStorageConnection.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WebCacheStorageConnection.h; sourceTree = "<group>"; };
                                41897ED61F415D860016FA42 /* CacheStorageEngine.cpp */,
                                41897ED21F415D850016FA42 /* CacheStorageEngine.h */,
                                41FABD281F4DDFDC006A6C97 /* CacheStorageEngineCache.h */,
+                               4135FBCF1F4FB7F20074C47B /* CacheStorageEngineCaches.cpp */,
+                               4135FBD01F4FB7F20074C47B /* CacheStorageEngineCaches.h */,
                                41897ED31F415D850016FA42 /* CacheStorageEngineConnection.cpp */,
                                41897ED41F415D850016FA42 /* CacheStorageEngineConnection.h */,
                                41897ED51F415D850016FA42 /* CacheStorageEngineConnection.messages.in */,
                                E170876B16D6CA6900F99226 /* BlobRegistryProxy.cpp in Sources */,
                                BCF18638167D071E00A1A85A /* CacheModel.cpp in Sources */,
                                41897ED71F415D8A0016FA42 /* CacheStorageEngine.cpp in Sources */,
+                               4135FBD11F4FB8090074C47B /* CacheStorageEngineCaches.cpp in Sources */,
                                41897ED91F415D8A0016FA42 /* CacheStorageEngineConnection.cpp in Sources */,
                                517CF0E3163A486C00C2950F /* CacheStorageEngineConnectionMessageReceiver.cpp in Sources */,
                                1AA2E51E12E4C05E00BC4966 /* CGUtilities.cpp in Sources */,
index c3d1302..bfd0ba3 100644 (file)
@@ -86,6 +86,12 @@ 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)
+{
+    connection().send(Messages::CacheStorageEngineConnection::ClearMemoryRepresentation(m_sessionID, origin), 0);
+    callback(std::nullopt);
+}
+
 void WebCacheStorageConnection::openCompleted(uint64_t requestIdentifier, const CacheIdentifierOrError& result)
 {
     CacheStorageConnection::openCompleted(requestIdentifier, result);
index 25affcf..f59e2cf 100644 (file)
@@ -60,6 +60,8 @@ 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::DOMCache::Record>&&) final;
 
+    void clearMemoryRepresentation(const String& origin, WebCore::DOMCache::CompletionCallback&&) final;
+
     void openCompleted(uint64_t requestIdentifier, const WebCore::DOMCache::CacheIdentifierOrError&);
     void removeCompleted(uint64_t requestIdentifier, const WebCore::DOMCache::CacheIdentifierOrError&);
     void updateCaches(uint64_t requestIdentifier, WebCore::DOMCache::CacheInfosOrError&&);