Title: [222073] trunk
Revision
222073
Author
[email protected]
Date
2017-09-14 20:56:49 -0700 (Thu, 14 Sep 2017)

Log Message

Add Cache API support of records persistency
https://bugs.webkit.org/show_bug.cgi?id=176845

Patch by Youenn Fablet <[email protected]> on 2017-09-14
Reviewed by Alex Christensen.

Source/WebKit:

Introducing asynchronous opening of Cache objects.
When opening, Cache objects will read the list of records from the disk.
They keep in memory the necessary information to make any match or remove request without reading further information from the disk.
Cache objects thus keep during the opening time a list of callbacks to call when being opened.
Persistent storage is implemented through NetworkCache::Storage.

A Cache object is added a unique name so that records from a Cache cannot be mixed with records from another Cache.
This unique name is stored persistenly in addition to the Cache name.
This unique name is used as the key type given to the storage.

Request, response and responsebody data are no longer kept by the Cache object.
For regular sessions, this information is kept on the disk through NetworkCache::Storage.
Insertion time is also stored persistently so as to keep the ordering of the records consistent.
For private browsing sessions, this information is kept in a map owned by the Caches object.
RecordData is renamed to RecordInformation as it now only contains the information used to handle the different Cache actions.

For every record retrieval action, a ReadRecordTaskCounter is used.
Its purpose is to allow retrieving the record data on the disk in parallel,
and then send back the information when everything is read.
In case some records cannot be correctly read, they are removed automatically at that point.
The current implementation loads all information in memory including response bodies.
Further improvements might consider streaming the response bodies progressively.

For every stored record, an AsynchronousPutTaskCounter is used.
Its purpose is also to wait for all put actions to be completed before executing the completion callback.
In some cases, a record needs to be overriden.
In that case, as per the specification, only the data related to the response is to be modified.
This requires reading the record request data on disk before actually writing the whole record again.
This is implemented in updateRecordToDisk.

The Cache object is delegating all record writing/reading tasks to its Caches object.
The Caches object is responsible to handle persistent/private sessions.

* NetworkProcess/cache/CacheStorageEngine.cpp:
(WebKit::CacheStorage::Engine::retrieveRecords): Making sure Cache object is opened.
(WebKit::CacheStorage::Engine::readCache): Ditto.
(WebKit::CacheStorage::Engine::unlock): Disposing the cache without going through initializing it.
* NetworkProcess/cache/CacheStorageEngineCache.cpp:
(WebKit::CacheStorage::computeKeyURL):
(WebKit::CacheStorage::queryCache):
(WebKit::CacheStorage::updateVaryInformation):
(WebKit::CacheStorage::Cache::toRecordInformation):
(WebKit::CacheStorage::Cache::Cache):
(WebKit::CacheStorage::Cache::open):
(WebKit::CacheStorage::Cache::finishOpening):
(WebKit::CacheStorage::ReadRecordTaskCounter::create):
(WebKit::CacheStorage::ReadRecordTaskCounter::~ReadRecordTaskCounter):
(WebKit::CacheStorage::ReadRecordTaskCounter::appendRecord):
(WebKit::CacheStorage::ReadRecordTaskCounter::ReadRecordTaskCounter):
(WebKit::CacheStorage::Cache::retrieveRecord):
(WebKit::CacheStorage::Cache::retrieveRecords):
(WebKit::CacheStorage::Cache::addRecord):
(WebKit::CacheStorage::Cache::recordsFromURL):
(WebKit::CacheStorage::Cache::recordsFromURL const):
(WebKit::CacheStorage::AsynchronousPutTaskCounter::create):
(WebKit::CacheStorage::AsynchronousPutTaskCounter::~AsynchronousPutTaskCounter):
(WebKit::CacheStorage::AsynchronousPutTaskCounter::onCompletion):
(WebKit::CacheStorage::AsynchronousPutTaskCounter::setError):
(WebKit::CacheStorage::Cache::put):
(WebKit::CacheStorage::Cache::remove):
(WebKit::CacheStorage::Cache::removeFromRecordList):
(WebKit::CacheStorage::Cache::writeRecordToDisk):
(WebKit::CacheStorage::Cache::updateRecordToDisk):
(WebKit::CacheStorage::Cache::readRecordFromDisk):
(WebKit::CacheStorage::Cache::removeRecordFromDisk):
(WebKit::CacheStorage::Cache::encode):
(WebKit::CacheStorage::decodeRecordHeader):
(WebKit::CacheStorage::Cache::decode):
(WebKit::CacheStorage::Cache::keys const):
* NetworkProcess/cache/CacheStorageEngineCache.h:
(WebKit::CacheStorage::Cache::isOpened const):
(WebKit::CacheStorage::Cache::uniqueName const):
* NetworkProcess/cache/CacheStorageEngineCaches.cpp:
(WebKit::CacheStorage::Caches::initialize):
(WebKit::CacheStorage::Caches::open):
(WebKit::CacheStorage::Caches::remove):
(WebKit::CacheStorage::Caches::dispose):
(WebKit::CacheStorage::encodeCacheNames):
(WebKit::CacheStorage::decodeCachesNames):
(WebKit::CacheStorage::Caches::readCachesFromDisk):
(WebKit::CacheStorage::Caches::readRecordsList):
(WebKit::CacheStorage::Caches::writeRecord):
(WebKit::CacheStorage::Caches::readRecord):
(WebKit::CacheStorage::Caches::removeRecord):
(WebKit::CacheStorage::Caches::clearMemoryRepresentation):
(WebKit::CacheStorage::Caches::salt const):
* NetworkProcess/cache/CacheStorageEngineCaches.h:
(WebKit::CacheStorage::Caches::shouldPersist const):
* NetworkProcess/cache/NetworkCacheStorage.cpp:
(WebKit::NetworkCache::Storage::store):
* NetworkProcess/cache/NetworkCacheStorage.h:
(WebKit::NetworkCache::Storage::writeWithoutWaiting):

LayoutTests:

* http/tests/cache-storage/cache-persistency.https.html:
* http/tests/cache-storage/cache-records-persistency.https-expected.txt: Added.
* http/tests/cache-storage/cache-records-persistency.https.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (222072 => 222073)


--- trunk/LayoutTests/ChangeLog	2017-09-15 03:15:59 UTC (rev 222072)
+++ trunk/LayoutTests/ChangeLog	2017-09-15 03:56:49 UTC (rev 222073)
@@ -1,3 +1,14 @@
+2017-09-14  Youenn Fablet  <[email protected]>
+
+        Add Cache API support of records persistency
+        https://bugs.webkit.org/show_bug.cgi?id=176845
+
+        Reviewed by Alex Christensen.
+
+        * http/tests/cache-storage/cache-persistency.https.html:
+        * http/tests/cache-storage/cache-records-persistency.https-expected.txt: Added.
+        * http/tests/cache-storage/cache-records-persistency.https.html: Added.
+
 2017-09-14  Commit Queue  <[email protected]>
 
         Unreviewed, rolling out r221932 and r221933.

Modified: trunk/LayoutTests/http/tests/cache-storage/cache-persistency.https.html (222072 => 222073)


--- trunk/LayoutTests/http/tests/cache-storage/cache-persistency.https.html	2017-09-15 03:15:59 UTC (rev 222072)
+++ trunk/LayoutTests/http/tests/cache-storage/cache-persistency.https.html	2017-09-15 03:56:49 UTC (rev 222073)
@@ -42,11 +42,11 @@
     promise_test(test => {
         if (window.testRunner)
             testRunner.setPrivateBrowsingEnabled(false);
-        var clearPromise;
-        if (window.internals)
-            clearPromise =internals.clearCacheStorageMemoryRepresentation();
 
-        return Promise.resolve(clearPromise).then(() => {
+        return Promise.resolve().then(() => {
+            if (window.internals)
+                return internals.clearCacheStorageMemoryRepresentation();
+        }).then(() => {
             return self.caches.keys();
         }).then(keys => {
             var pending = [];

Added: trunk/LayoutTests/http/tests/cache-storage/cache-records-persistency.https-expected.txt (0 => 222073)


--- trunk/LayoutTests/http/tests/cache-storage/cache-records-persistency.https-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/cache-storage/cache-records-persistency.https-expected.txt	2017-09-15 03:56:49 UTC (rev 222073)
@@ -0,0 +1,9 @@
+
+PASS Cleaning existing caches 
+PASS Cache.put called twice with matching Requests - keys should remain the same - testing persistency 
+PASS Cleaning created caches 
+PASS Checking persistency of cache keys 
+PASS Checking persistency of response 
+PASS Checking persistency of response body 
+PASS Testing overriding of an existing cached record that is not in memory 
+

Added: trunk/LayoutTests/http/tests/cache-storage/cache-records-persistency.https.html (0 => 222073)


--- trunk/LayoutTests/http/tests/cache-storage/cache-records-persistency.https.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/cache-storage/cache-records-persistency.https.html	2017-09-15 03:56:49 UTC (rev 222073)
@@ -0,0 +1,133 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Cache Storage: testing Cache persistency</title>
+<script src=""
+<script src=""
+</head>
+<body>
+    <script>
+function assert_header_equals(actual, expected, description) {
+    assert_class_string(actual, "Headers", description);
+    var header;
+    var actual_headers = [];
+    var expected_headers = [];
+    for (header of actual)
+        actual_headers.push(header[0] + ": " + header[1]);
+    for (header of expected)
+        expected_headers.push(header[0] + ": " + header[1]);
+    assert_array_equals(actual_headers, expected_headers,
+                        description + " Headers differ.");
+}
+
+function assert_response_equals(actual, expected, description) {
+    assert_class_string(actual, "Response", description);
+    ["type", "url", "status", "ok", "statusText"].forEach(function(attribute) {
+        assert_equals(actual[attribute], expected[attribute],
+                      description + " Attributes differ: " + attribute + ".");
+    });
+    assert_header_equals(actual.headers, expected.headers, description);
+}
+
+function assert_request_equals(actual, expected, description) {
+    assert_class_string(actual, "Request", description);
+    ["url"].forEach(function(attribute) {
+        assert_equals(actual[attribute], expected[attribute],
+                      description + " Attributes differ: " + attribute + ".");
+    });
+    assert_header_equals(actual.headers, expected.headers, description);
+}
+
+function assert_request_array_equals(actual, expected, description) {
+    assert_true(Array.isArray(actual), description);
+    assert_equals(actual.length, expected.length, description);
+    actual.forEach(function(value, index) {
+        assert_request_equals(value, expected[index],
+                              description + " : object[" + index + "]");
+    });
+}
+
+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");
+
+var test_url = 'https://example.com/foo';
+var test_body = 'Hello world!';
+
+promise_test(() => {
+    var cache;
+    var cache_keys;
+    var first_response = new Response("Old body", { statusText: 'Old status' });
+    var alternate_response = new Response("New body", { statusText: 'New status' });
+    return self.caches.open("test-cache-records-persistency").then(c => {
+        cache = c;
+    }).then(() => {
+        return cache.put(test_url, first_response);
+    }).then(() => {
+        return cache.keys();
+    }).then(keys => {
+        cache_keys = keys;
+        if (window.internals)
+            return internals.clearCacheStorageMemoryRepresentation();
+    }).then(() => {
+        return self.caches.open("test-cache-records-persistency");
+    }).then(c => {
+        cache = c;
+        return cache.keys().then((keys) => {
+            test(() => {
+                assert_request_array_equals(keys, cache_keys);
+            }, "Checking persistency of cache keys");
+            return cache.match(test_url);
+        }).then((result) => {
+            test(() => {
+                assert_response_equals(result, first_response, "Cache.put should store the response persistently");
+            }, "Checking persistency of response");
+            return result.text();
+        }).then((body) => {
+            test(() => {
+                assert_equals(body, "Old body", "Cache put should store the response body persistently.");
+            }, "Checking persistency of response body");
+        });
+    }).then(() => {
+        return cache.put(test_url, alternate_response);
+    }).then(() => {
+        return cache.keys();
+    }).then(keys => {
+        cache_keys = keys;
+        if (window.internals)
+            return internals.clearCacheStorageMemoryRepresentation();
+    }).then(() => {
+        return self.caches.open("test-cache-records-persistency");
+    }).then(c => {
+        cache = c;
+        return cache.keys();
+    }).then((keys) => {
+        assert_request_array_equals(keys, cache_keys);
+    }).then(() => {
+        promise_test(() => {
+            return cache.match(test_url).then((result) => {
+                assert_response_equals(result, alternate_response, 'Cache.put should replace existing ' + 'response with new response.');
+                return result.text();
+            }).then((body) => {
+                assert_equals(body, "New body", 'Cache put should store new response body.');
+            });
+        }, 'Testing overriding of an existing cached record that is not in memory');
+    });
+}, 'Cache.put called twice with matching Requests - keys should remain the same - testing 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 created caches");
+    </script>
+</body>
+</html>

Modified: trunk/Source/WebKit/ChangeLog (222072 => 222073)


--- trunk/Source/WebKit/ChangeLog	2017-09-15 03:15:59 UTC (rev 222072)
+++ trunk/Source/WebKit/ChangeLog	2017-09-15 03:56:49 UTC (rev 222073)
@@ -1,3 +1,103 @@
+2017-09-14  Youenn Fablet  <[email protected]>
+
+        Add Cache API support of records persistency
+        https://bugs.webkit.org/show_bug.cgi?id=176845
+
+        Reviewed by Alex Christensen.
+
+        Introducing asynchronous opening of Cache objects.
+        When opening, Cache objects will read the list of records from the disk.
+        They keep in memory the necessary information to make any match or remove request without reading further information from the disk.
+        Cache objects thus keep during the opening time a list of callbacks to call when being opened.
+        Persistent storage is implemented through NetworkCache::Storage.
+
+        A Cache object is added a unique name so that records from a Cache cannot be mixed with records from another Cache.
+        This unique name is stored persistenly in addition to the Cache name.
+        This unique name is used as the key type given to the storage.
+
+        Request, response and responsebody data are no longer kept by the Cache object.
+        For regular sessions, this information is kept on the disk through NetworkCache::Storage.
+        Insertion time is also stored persistently so as to keep the ordering of the records consistent.
+        For private browsing sessions, this information is kept in a map owned by the Caches object.
+        RecordData is renamed to RecordInformation as it now only contains the information used to handle the different Cache actions.
+
+        For every record retrieval action, a ReadRecordTaskCounter is used.
+        Its purpose is to allow retrieving the record data on the disk in parallel,
+        and then send back the information when everything is read.
+        In case some records cannot be correctly read, they are removed automatically at that point.
+        The current implementation loads all information in memory including response bodies.
+        Further improvements might consider streaming the response bodies progressively.
+
+        For every stored record, an AsynchronousPutTaskCounter is used.
+        Its purpose is also to wait for all put actions to be completed before executing the completion callback.
+        In some cases, a record needs to be overriden.
+        In that case, as per the specification, only the data related to the response is to be modified.
+        This requires reading the record request data on disk before actually writing the whole record again.
+        This is implemented in updateRecordToDisk.
+
+        The Cache object is delegating all record writing/reading tasks to its Caches object.
+        The Caches object is responsible to handle persistent/private sessions.
+
+        * NetworkProcess/cache/CacheStorageEngine.cpp:
+        (WebKit::CacheStorage::Engine::retrieveRecords): Making sure Cache object is opened.
+        (WebKit::CacheStorage::Engine::readCache): Ditto.
+        (WebKit::CacheStorage::Engine::unlock): Disposing the cache without going through initializing it.
+        * NetworkProcess/cache/CacheStorageEngineCache.cpp:
+        (WebKit::CacheStorage::computeKeyURL):
+        (WebKit::CacheStorage::queryCache):
+        (WebKit::CacheStorage::updateVaryInformation):
+        (WebKit::CacheStorage::Cache::toRecordInformation):
+        (WebKit::CacheStorage::Cache::Cache):
+        (WebKit::CacheStorage::Cache::open):
+        (WebKit::CacheStorage::Cache::finishOpening):
+        (WebKit::CacheStorage::ReadRecordTaskCounter::create):
+        (WebKit::CacheStorage::ReadRecordTaskCounter::~ReadRecordTaskCounter):
+        (WebKit::CacheStorage::ReadRecordTaskCounter::appendRecord):
+        (WebKit::CacheStorage::ReadRecordTaskCounter::ReadRecordTaskCounter):
+        (WebKit::CacheStorage::Cache::retrieveRecord):
+        (WebKit::CacheStorage::Cache::retrieveRecords):
+        (WebKit::CacheStorage::Cache::addRecord):
+        (WebKit::CacheStorage::Cache::recordsFromURL):
+        (WebKit::CacheStorage::Cache::recordsFromURL const):
+        (WebKit::CacheStorage::AsynchronousPutTaskCounter::create):
+        (WebKit::CacheStorage::AsynchronousPutTaskCounter::~AsynchronousPutTaskCounter):
+        (WebKit::CacheStorage::AsynchronousPutTaskCounter::onCompletion):
+        (WebKit::CacheStorage::AsynchronousPutTaskCounter::setError):
+        (WebKit::CacheStorage::Cache::put):
+        (WebKit::CacheStorage::Cache::remove):
+        (WebKit::CacheStorage::Cache::removeFromRecordList):
+        (WebKit::CacheStorage::Cache::writeRecordToDisk):
+        (WebKit::CacheStorage::Cache::updateRecordToDisk):
+        (WebKit::CacheStorage::Cache::readRecordFromDisk):
+        (WebKit::CacheStorage::Cache::removeRecordFromDisk):
+        (WebKit::CacheStorage::Cache::encode):
+        (WebKit::CacheStorage::decodeRecordHeader):
+        (WebKit::CacheStorage::Cache::decode):
+        (WebKit::CacheStorage::Cache::keys const):
+        * NetworkProcess/cache/CacheStorageEngineCache.h:
+        (WebKit::CacheStorage::Cache::isOpened const):
+        (WebKit::CacheStorage::Cache::uniqueName const):
+        * NetworkProcess/cache/CacheStorageEngineCaches.cpp:
+        (WebKit::CacheStorage::Caches::initialize):
+        (WebKit::CacheStorage::Caches::open):
+        (WebKit::CacheStorage::Caches::remove):
+        (WebKit::CacheStorage::Caches::dispose):
+        (WebKit::CacheStorage::encodeCacheNames):
+        (WebKit::CacheStorage::decodeCachesNames):
+        (WebKit::CacheStorage::Caches::readCachesFromDisk):
+        (WebKit::CacheStorage::Caches::readRecordsList):
+        (WebKit::CacheStorage::Caches::writeRecord):
+        (WebKit::CacheStorage::Caches::readRecord):
+        (WebKit::CacheStorage::Caches::removeRecord):
+        (WebKit::CacheStorage::Caches::clearMemoryRepresentation):
+        (WebKit::CacheStorage::Caches::salt const):
+        * NetworkProcess/cache/CacheStorageEngineCaches.h:
+        (WebKit::CacheStorage::Caches::shouldPersist const):
+        * NetworkProcess/cache/NetworkCacheStorage.cpp:
+        (WebKit::NetworkCache::Storage::store):
+        * NetworkProcess/cache/NetworkCacheStorage.h:
+        (WebKit::NetworkCache::Storage::writeWithoutWaiting):
+
 2017-09-14  Alex Christensen  <[email protected]>
 
         Add WKUIDelegatePrivate equivalent of WKPageUIClient's drawHeader, drawFooter, headerHeight, and footerHeight

Modified: trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngine.cpp (222072 => 222073)


--- trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngine.cpp	2017-09-15 03:15:59 UTC (rev 222072)
+++ trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngine.cpp	2017-09-15 03:56:49 UTC (rev 222073)
@@ -132,8 +132,7 @@
             callback(makeUnexpected(result.error()));
             return;
         }
-
-        callback(result.value().get().retrieveRecords(url));
+        result.value().get().retrieveRecords(url, WTFMove(callback));
     });
 }
 
@@ -217,12 +216,28 @@
 
 void Engine::readCache(uint64_t cacheIdentifier, CacheCallback&& callback)
 {
-    // FIXME: Implement reading.
     auto* cache = this->cache(cacheIdentifier);
     if (!cache) {
         callback(makeUnexpected(Error::Internal));
         return;
     }
+    if (!cache->isOpened()) {
+        cache->open([this, protectedThis = makeRef(*this), cacheIdentifier, callback = WTFMove(callback)](std::optional<Error>&& error) mutable {
+            if (error) {
+                callback(makeUnexpected(error.value()));
+                return;
+            }
+
+            auto* cache = this->cache(cacheIdentifier);
+            if (!cache) {
+                callback(makeUnexpected(Error::Internal));
+                return;
+            }
+            ASSERT(cache->isOpened());
+            callback(std::reference_wrapper<Cache> { *cache });
+        });
+        return;
+    }
     callback(std::reference_wrapper<Cache> { *cache });
 }
 
@@ -328,12 +343,11 @@
     if (--lockCount->value)
         return;
 
-    readCache(cacheIdentifier, [this](CacheOrError&& result) mutable {
-        if (!result.hasValue())
-            return;
+    auto* cache = this->cache(cacheIdentifier);
+    if (!cache)
+        return;
 
-        result.value().get().dispose();
-    });
+    cache->dispose();
 }
 
 String Engine::representation()

Modified: trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCache.cpp (222072 => 222073)


--- trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCache.cpp	2017-09-15 03:15:59 UTC (rev 222072)
+++ trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCache.cpp	2017-09-15 03:56:49 UTC (rev 222073)
@@ -27,6 +27,7 @@
 #include "CacheStorageEngine.h"
 
 #include "CacheStorageEngineCaches.h"
+#include "NetworkCacheCoders.h"
 #include "NetworkCacheIOChannel.h"
 #include "NetworkCacheKey.h"
 #include "NetworkProcess.h"
@@ -33,9 +34,9 @@
 #include <WebCore/CacheQueryOptions.h>
 #include <WebCore/HTTPParsers.h>
 #include <pal/SessionID.h>
-#include <wtf/CurrentTime.h>
 #include <wtf/MainThread.h>
 #include <wtf/NeverDestroyed.h>
+#include <wtf/UUID.h>
 #include <wtf/persistence/PersistentCoders.h>
 #include <wtf/persistence/PersistentDecoder.h>
 #include <wtf/persistence/PersistentEncoder.h>
@@ -51,8 +52,19 @@
 
 namespace CacheStorage {
 
-static inline Vector<uint64_t> queryCache(const Vector<RecordData>* records, const ResourceRequest& request, const CacheQueryOptions& options)
+static std::optional<std::pair<Record, double>> decodeRecordHeader(const Storage::Record&);
+
+static inline String computeKeyURL(const URL& url)
 {
+    URL keyURL = url;
+    if (keyURL.hasQuery())
+        keyURL.setQuery({ });
+    keyURL.removeFragmentIdentifier();
+    return keyURL;
+}
+
+static inline Vector<uint64_t> queryCache(const Vector<RecordInformation>* records, const ResourceRequest& request, const CacheQueryOptions& options)
+{
     if (!records)
         return { };
 
@@ -67,47 +79,42 @@
     return results;
 }
 
-static inline void updateVaryInformation(RecordData& recordData)
+static inline void updateVaryInformation(RecordInformation& recordInformation, const ResourceRequest& request, const ResourceResponse& response)
 {
-    auto varyValue = recordData.data->response.httpHeaderField(WebCore::HTTPHeaderName::Vary);
+    auto varyValue = response.httpHeaderField(WebCore::HTTPHeaderName::Vary);
     if (varyValue.isNull()) {
-        recordData.hasVaryStar = false;
-        recordData.varyHeaders = { };
+        recordInformation.hasVaryStar = false;
+        recordInformation.varyHeaders = { };
         return;
     }
 
     varyValue.split(',', false, [&](StringView view) {
-        if (!recordData.hasVaryStar && stripLeadingAndTrailingHTTPSpaces(view) == "*")
-            recordData.hasVaryStar = true;
+        if (!recordInformation.hasVaryStar && stripLeadingAndTrailingHTTPSpaces(view) == "*")
+            recordInformation.hasVaryStar = true;
         String headerName = view.toString();
-        recordData.varyHeaders.add(headerName, recordData.data->request.httpHeaderField(headerName));
+        recordInformation.varyHeaders.add(headerName, request.httpHeaderField(headerName));
     });
 
-    if (recordData.hasVaryStar)
-        recordData.varyHeaders = { };
+    if (recordInformation.hasVaryStar)
+        recordInformation.varyHeaders = { };
 }
 
-static inline Record toRecord(const RecordData& record)
+RecordInformation Cache::toRecordInformation(const Record& record)
 {
-    return { record.identifier, record.updateResponseCounter, record.data->requestHeadersGuard, record.data->request, record.data->options, record.data->referrer, record.data->responseHeadersGuard, record.data->response, copyResponseBody(record.data->responseBody) };
-}
+    Key key { ASCIILiteral("record"), m_uniqueName, { }, createCanonicalUUIDString(), m_caches.salt() };
+    RecordInformation recordInformation { WTFMove(key), monotonicallyIncreasingTimeMS(), record.identifier, 0 , record.request.url(), false, { } };
 
-static inline RecordData toRecordData(Record&& record)
-{
-    auto url = ""
-    RecordData::Data data = { record.requestHeadersGuard, WTFMove(record.request), WTFMove(record.options), WTFMove(record.referrer), WTFMove(record.responseHeadersGuard), WTFMove(record.response), WTFMove(record.responseBody) };
-    RecordData recordData = { { }, monotonicallyIncreasingTimeMS(), record.identifier, 0, url, false, { }, WTFMove(data) };
+    updateVaryInformation(recordInformation, record.request, record.response);
 
-    updateVaryInformation(recordData);
-
-    return recordData;
+    return recordInformation;
 }
 
-Cache::Cache(Caches& caches, uint64_t identifier, State state, String&& name)
+Cache::Cache(Caches& caches, uint64_t identifier, State state, String&& name, String&& uniqueName)
     : m_caches(caches)
     , m_state(state)
     , m_identifier(identifier)
     , m_name(WTFMove(name))
+    , m_uniqueName(WTFMove(uniqueName))
 {
 }
 
@@ -122,7 +129,38 @@
     m_nextRecordIdentifier = 0;
     m_state = State::Uninitialized;
 }
- 
+
+static RecordInformation isolatedCopy(const RecordInformation& information)
+{
+    auto result = RecordInformation { information.key, information.insertionTime, information.identifier, information.updateResponseCounter, information.url.isolatedCopy(), information.hasVaryStar, { } };
+    HashMap<String, String> varyHeaders;
+    for (const auto& keyValue : information.varyHeaders)
+        varyHeaders.set(keyValue.key.isolatedCopy(), keyValue.value.isolatedCopy());
+    result.varyHeaders = WTFMove(varyHeaders);
+    return result;
+}
+
+struct TraversalResult {
+    uint64_t cacheIdentifier;
+    HashMap<String, Vector<RecordInformation>> records;
+    Vector<Key> failedRecords;
+};
+
+static TraversalResult isolatedCopy(TraversalResult&& result)
+{
+    HashMap<String, Vector<RecordInformation>> isolatedRecords;
+    for (auto& keyValue : result.records) {
+        auto& recordVector = keyValue.value;
+        for (size_t cptr = 0; cptr < recordVector.size(); cptr++)
+            recordVector[cptr] = isolatedCopy(recordVector[cptr]);
+
+        isolatedRecords.set(keyValue.key.isolatedCopy(), WTFMove(recordVector));
+    }
+
+    // No need to isolate keys since they are isolated through the copy constructor
+    return TraversalResult { result.cacheIdentifier, WTFMove(isolatedRecords), WTFMove(result.failedRecords) };
+}
+
 void Cache::open(CompletionCallback&& callback)
 {
     if (m_state == State::Open) {
@@ -134,18 +172,54 @@
         return;
     }
     m_state = State::Opening;
-    readRecordsList([caches = makeRef(m_caches), identifier = m_identifier, callback = WTFMove(callback)](std::optional<Error>&& error) mutable {
-        auto* cache = caches->find(identifier);
-        if (!cache) {
-            callback(Error::Internal);
+    TraversalResult traversalResult { m_identifier, { }, { } };
+    m_caches.readRecordsList(*this, [caches = makeRef(m_caches), callback = WTFMove(callback), traversalResult = WTFMove(traversalResult)](const auto* storageRecord, const auto&) mutable {
+        if (!storageRecord) {
+            RunLoop::main().dispatch([caches = WTFMove(caches), callback = WTFMove(callback), traversalResult = isolatedCopy(WTFMove(traversalResult)) ]() mutable {
+                for (auto& key : traversalResult.failedRecords)
+                    caches->removeRecord(key);
+
+                auto* cache = caches->find(traversalResult.cacheIdentifier);
+                if (!cache) {
+                    callback(Error::Internal);
+                    return;
+                }
+                cache->m_records = WTFMove(traversalResult.records);
+                cache->finishOpening(WTFMove(callback), std::nullopt);
+            });
             return;
         }
-        cache->finishOpening(WTFMove(callback), WTFMove(error));
+
+        auto decoded = decodeRecordHeader(*storageRecord);
+        if (!decoded) {
+            traversalResult.failedRecords.append(storageRecord->key);
+            return;
+        }
+
+        auto& record = decoded->first;
+        auto insertionTime = decoded->second;
+
+        RecordInformation recordInformation { storageRecord->key, insertionTime, 0, 0, record.request.url(), false, { } };
+        updateVaryInformation(recordInformation, record.request, record.response);
+
+        auto& sameURLRecords = traversalResult.records.ensure(computeKeyURL(recordInformation.url), [] { return Vector<RecordInformation> { }; }).iterator->value;
+        sameURLRecords.append(WTFMove(recordInformation));
     });
 }
 
 void Cache::finishOpening(CompletionCallback&& callback, std::optional<Error>&& error)
 {
+    Vector<std::reference_wrapper<RecordInformation>> records;
+    for (auto& value : m_records.values()) {
+        for (auto& record : value)
+            records.append(record);
+    }
+    std::sort(records.begin(), records.end(), [&](const auto& a, const auto& b) {
+        return a.get().insertionTime < b.get().insertionTime;
+    });
+    for (auto& record : records)
+        record.get().identifier = ++m_nextRecordIdentifier;
+
     if (error) {
         m_state = State::Uninitialized;
         callback(error.value());
@@ -162,52 +236,94 @@
         callback(std::nullopt);
 }
 
-Vector<Record> Cache::retrieveRecords(const URL& url) const
+class ReadRecordTaskCounter : public RefCounted<ReadRecordTaskCounter> {
+public:
+    using ReadRecordsCallback = WTF::Function<void(Vector<Record>&&, Vector<uint64_t>&&)>;
+    static Ref<ReadRecordTaskCounter> create(ReadRecordsCallback&& callback) { return adoptRef(*new ReadRecordTaskCounter(WTFMove(callback))); }
+
+    ~ReadRecordTaskCounter()
+    {
+        ASSERT(RunLoop::isMain());
+        if (!m_callback)
+            return;
+        std::sort(m_records.begin(), m_records.end(), [&] (const auto& a, const auto& b) {
+            return a.identifier < b.identifier;
+        });
+        m_callback(WTFMove(m_records), WTFMove(m_failedRecords));
+    }
+
+    void appendRecord(Expected<Record, Error>&& result, uint64_t recordIdentifier, uint64_t updateCounter)
+    {
+        ASSERT(RunLoop::isMain());
+        if (!result.hasValue()) {
+            m_failedRecords.append(recordIdentifier);
+            return;
+        }
+        result.value().identifier = recordIdentifier;
+        result.value().updateResponseCounter = updateCounter;
+        m_records.append(WTFMove(result.value()));
+    }
+
+private:
+    explicit ReadRecordTaskCounter(ReadRecordsCallback&& callback)
+        : m_callback(WTFMove(callback))
+    {
+    }
+
+    ReadRecordsCallback m_callback;
+    Vector<Record> m_records;
+    Vector<uint64_t> m_failedRecords;
+};
+
+void Cache::retrieveRecord(const RecordInformation& record, Ref<ReadRecordTaskCounter>&& taskCounter)
 {
+    readRecordFromDisk(record, [caches = makeRef(m_caches), identifier = m_identifier, recordIdentifier = record.identifier, updateCounter = record.updateResponseCounter, taskCounter = WTFMove(taskCounter)](Expected<Record, Error>&& result) mutable {
+        auto* cache = caches->find(identifier);
+        if (!cache)
+            return;
+        taskCounter->appendRecord(WTFMove(result), recordIdentifier, updateCounter);
+    });
+}
+
+void Cache::retrieveRecords(const URL& url, RecordsCallback&& callback)
+{
+    ASSERT(m_state == State::Open);
+
+    auto taskCounter = ReadRecordTaskCounter::create([caches = makeRef(m_caches), identifier = m_identifier, callback = WTFMove(callback)](Vector<Record>&& records, Vector<uint64_t>&& failedRecordIdentifiers) mutable {
+        auto* cache = caches->find(identifier);
+        if (cache)
+            cache->removeFromRecordList(failedRecordIdentifiers);
+        callback(WTFMove(records));
+    });
+
     if (url.isNull()) {
-        Vector<Record> result;
         for (auto& records : m_records.values()) {
             for (auto& record : records)
-                result.append(toRecord(record));
+                retrieveRecord(record, taskCounter.copyRef());
         }
-        std::sort(result.begin(), result.end(), [](const auto& a, const auto& b) {
-            return a.identifier < b.identifier;
-        });
-        return result;
+        return;
     }
 
-    const auto* records = recordsFromURL(url);
+    auto* records = recordsFromURL(url);
     if (!records)
-        return { };
+        return;
 
-    Vector<Record> result;
-    result.reserveInitialCapacity(records->size());
     for (auto& record : *records)
-        result.append(toRecord(record));
-    return result;
+        retrieveRecord(record, taskCounter.copyRef());
 }
 
-static inline String computeKeyURL(const URL& url)
+RecordInformation& Cache::addRecord(Vector<RecordInformation>* records, const Record& record)
 {
-    URL keyURL = url;
-    if (keyURL.hasQuery())
-        keyURL.setQuery({ });
-    keyURL.removeFragmentIdentifier();
-    return keyURL;
-}
-
-RecordData& Cache::addRecord(Vector<RecordData>* records, Record&& record)
-{
     if (!records) {
         auto key = computeKeyURL(record.request.url());
         ASSERT(!m_records.contains(key));
-        records = &m_records.set(key, Vector<RecordData> { }).iterator->value;
+        records = &m_records.set(key, Vector<RecordInformation> { }).iterator->value;
     }
-    records->append(toRecordData(WTFMove(record)));
+    records->append(toRecordInformation(record));
     return records->last();
 }
 
-Vector<RecordData>* Cache::recordsFromURL(const URL& url)
+Vector<RecordInformation>* Cache::recordsFromURL(const URL& url)
 {
     auto iterator = m_records.find(computeKeyURL(url));
     if (iterator == m_records.end())
@@ -215,7 +331,7 @@
     return &iterator->value;
 }
 
-const Vector<RecordData>* Cache::recordsFromURL(const URL& url) const
+const Vector<RecordInformation>* Cache::recordsFromURL(const URL& url) const
 {
     auto iterator = m_records.find(computeKeyURL(url));
     if (iterator == m_records.end())
@@ -223,12 +339,53 @@
     return &iterator->value;
 }
 
+class AsynchronousPutTaskCounter : public RefCounted<AsynchronousPutTaskCounter> {
+public:
+    static Ref<AsynchronousPutTaskCounter> create(RecordIdentifiersCallback&& callback) { return adoptRef(*new AsynchronousPutTaskCounter(WTFMove(callback))); }
+    ~AsynchronousPutTaskCounter()
+    {
+        ASSERT(RunLoop::isMain());
+        if (!m_callback)
+            return;
+        if (m_error) {
+            m_callback(makeUnexpected(m_error.value()));
+            return;
+        }
+        m_callback(WTFMove(m_recordIdentifiers));
+    }
+
+    void setError(Error error)
+    {
+        ASSERT(RunLoop::isMain());
+        if (m_error)
+            return;
+
+        m_error = error;
+    }
+
+    void addRecordIdentifier(uint64_t identifier)
+    {
+        m_recordIdentifiers.append(identifier);
+    }
+
+private:
+    explicit AsynchronousPutTaskCounter(RecordIdentifiersCallback&& callback)
+        : m_callback(WTFMove(callback))
+    {
+    }
+
+    std::optional<Error> m_error;
+    RecordIdentifiersCallback m_callback;
+    Vector<uint64_t> m_recordIdentifiers;
+};
+
 void Cache::put(Vector<Record>&& records, RecordIdentifiersCallback&& callback)
 {
-    bool shouldWriteRecordList { false };
+    ASSERT(m_state == State::Open);
+
+    auto taskCounter = AsynchronousPutTaskCounter::create(WTFMove(callback));
+
     WebCore::CacheQueryOptions options;
-    Vector<uint64_t> recordIdentifiers;
-    recordIdentifiers.reserveInitialCapacity(records.size());
     for (auto& record : records) {
         auto* sameURLRecords = recordsFromURL(record.request.url());
 
@@ -235,46 +392,27 @@
         auto matchingRecords = queryCache(sameURLRecords, record.request, options);
         if (matchingRecords.isEmpty()) {
             record.identifier = ++m_nextRecordIdentifier;
-            recordIdentifiers.uncheckedAppend(record.identifier);
+            taskCounter->addRecordIdentifier(record.identifier);
 
-            shouldWriteRecordList = true;
-            auto& recordToWrite = addRecord(sameURLRecords, WTFMove(record));
-            writeRecordToDisk(recordToWrite);
+            auto& recordToWrite = addRecord(sameURLRecords, record);
+            writeRecordToDisk(recordToWrite, WTFMove(record), taskCounter.copyRef());
         } else {
             auto identifier = matchingRecords[0];
             auto position = sameURLRecords->findMatching([&](const auto& item) { return item.identifier == identifier; });
             ASSERT(position != notFound);
             if (position != notFound) {
-                auto& existingRecord = (*sameURLRecords)[position];
-                recordIdentifiers.uncheckedAppend(identifier);
-                ++existingRecord.updateResponseCounter;
-
-                // FIXME: Handle the case where data is null.
-                ASSERT(existingRecord.data);
-                existingRecord.data->responseHeadersGuard = record.responseHeadersGuard;
-                existingRecord.data->response = WTFMove(record.response);
-                existingRecord.data->responseBody = WTFMove(record.responseBody);
-                updateVaryInformation(existingRecord);
-                writeRecordToDisk(existingRecord);
+                auto& existingRecord = sameURLRecords->at(position);
+                taskCounter->addRecordIdentifier(identifier);
+                updateRecordToDisk(existingRecord, WTFMove(record), taskCounter.copyRef());
             }
         }
     }
-
-    if (!shouldWriteRecordList) {
-        callback(WTFMove(recordIdentifiers));
-        return;
-    }
-    writeRecordsList([callback = WTFMove(callback), recordIdentifiers = WTFMove(recordIdentifiers)](std::optional<Error>&& error) mutable {
-        if (error) {
-            callback(makeUnexpected(error.value()));
-            return;
-        }
-        callback(WTFMove(recordIdentifiers));
-    });
 }
 
 void Cache::remove(WebCore::ResourceRequest&& request, WebCore::CacheQueryOptions&& options, RecordIdentifiersCallback&& callback)
 {
+    ASSERT(m_state == State::Open);
+
     auto* records = recordsFromURL(request.url());
     auto recordIdentifiers = queryCache(records, request, options);
     if (recordIdentifiers.isEmpty()) {
@@ -289,37 +427,160 @@
         return shouldRemove;
     });
 
-    writeRecordsList([callback = WTFMove(callback), recordIdentifiers = WTFMove(recordIdentifiers)](std::optional<Error>&& error) mutable {
-        if (error) {
-            callback(makeUnexpected(error.value()));
+    callback(WTFMove(recordIdentifiers));
+}
+
+void Cache::removeFromRecordList(const Vector<uint64_t>& recordIdentifiers)
+{
+    if (recordIdentifiers.isEmpty())
+        return;
+
+    for (auto& records : m_records.values()) {
+        auto* cache = this;
+        records.removeAllMatching([cache, &recordIdentifiers](const auto& item) {
+            return notFound != recordIdentifiers.findMatching([cache, &item](const auto& identifier) {
+                if (item.identifier != identifier)
+                    return false;
+                cache->removeRecordFromDisk(item);
+                return true;
+            });
+        });
+    }
+}
+
+void Cache::writeRecordToDisk(const RecordInformation& recordInformation, Record&& record, Ref<AsynchronousPutTaskCounter>&& taskCounter)
+{
+    m_caches.writeRecord(*this, recordInformation, WTFMove(record), [taskCounter = WTFMove(taskCounter)](std::optional<Error>&& error) {
+        if (error)
+            taskCounter->setError(error.value());
+    });
+}
+
+void Cache::updateRecordToDisk(RecordInformation& existingRecord, Record&& record, Ref<AsynchronousPutTaskCounter>&& taskCounter)
+{
+    ++existingRecord.updateResponseCounter;
+    readRecordFromDisk(existingRecord, [caches = makeRef(m_caches), identifier = m_identifier, recordIdentifier = existingRecord.identifier, record = WTFMove(record), taskCounter = WTFMove(taskCounter)](Expected<Record, Error>&& result) mutable {
+        if (!result.hasValue())
             return;
-        }
-        callback(WTFMove(recordIdentifiers));
+
+        auto* cache = caches->find(identifier);
+        if (!cache)
+            return;
+
+        auto* sameURLRecords = cache->recordsFromURL(result.value().request.url());
+        if (!sameURLRecords)
+            return;
+
+        auto position = sameURLRecords->findMatching([&] (const auto& item) { return item.identifier == recordIdentifier; });
+        if (position == notFound)
+            return;
+        auto& recordInfo = sameURLRecords->at(position);
+
+        auto& recordFromDisk = result.value();
+        record.requestHeadersGuard = recordFromDisk.requestHeadersGuard;
+        record.request = WTFMove(recordFromDisk.request);
+        record.options = WTFMove(recordFromDisk.options);
+        record.referrer = WTFMove(recordFromDisk.referrer);
+
+        updateVaryInformation(recordInfo, record.request, record.response);
+
+        cache->writeRecordToDisk(recordInfo, WTFMove(record), WTFMove(taskCounter));
     });
 }
 
-void Cache::readRecordsList(CompletionCallback&& callback)
+void Cache::readRecordFromDisk(const RecordInformation& record, WTF::Function<void(Expected<Record, Error>&&)>&& callback)
 {
-    // FIXME: Implement this.
-    callback(std::nullopt);
+    m_caches.readRecord(record.key, WTFMove(callback));
 }
 
-void Cache::writeRecordsList(CompletionCallback&& callback)
+void Cache::removeRecordFromDisk(const RecordInformation& record)
 {
-    // FIXME: Implement this.
-    callback(std::nullopt);
+    m_caches.removeRecord(record.key);
 }
 
-void Cache::writeRecordToDisk(RecordData&)
+Storage::Record Cache::encode(const RecordInformation& recordInformation, const Record& record)
 {
-    // FIXME: Implement this.
+    WTF::Persistence::Encoder encoder;
+    encoder << recordInformation.insertionTime;
+    encoder << record.requestHeadersGuard;
+    record.request.encodeWithoutPlatformData(encoder);
+    encoder << record.options;
+    encoder << record.referrer;
+
+    encoder << record.responseHeadersGuard;
+    encoder << record.response;
+
+    encoder.encodeChecksum();
+
+    Data header(encoder.buffer(), encoder.bufferSize());
+    Data body;
+    WTF::switchOn(record.responseBody, [](const Ref<WebCore::FormData>& formData) {
+        // FIXME: Store form data body.
+    }, [&](const Ref<WebCore::SharedBuffer>& buffer) {
+        body = { reinterpret_cast<const uint8_t*>(buffer->data()), buffer->size() };
+    }, [](const std::nullptr_t&) {
+    });
+
+    return { recordInformation.key, { }, header, body, { } };
 }
 
-void Cache::removeRecordFromDisk(RecordData&)
+static inline std::optional<std::pair<Record, double>> decodeRecordHeader(const Storage::Record& storage)
 {
-    // FIXME: Implement this.
+    WTF::Persistence::Decoder decoder(storage.header.data(), storage.header.size());
+
+    Record record;
+
+    double insertionTime;
+    if (!decoder.decode(insertionTime))
+        return std::nullopt;
+
+    if (!decoder.decode(record.requestHeadersGuard))
+        return std::nullopt;
+
+    if (!record.request.decodeWithoutPlatformData(decoder))
+        return std::nullopt;
+
+    if (!decoder.decode(record.options))
+        return std::nullopt;
+
+    if (!decoder.decode(record.referrer))
+        return std::nullopt;
+
+    if (!decoder.decode(record.responseHeadersGuard))
+        return std::nullopt;
+
+    if (!decoder.decode(record.response))
+        return std::nullopt;
+
+    if (!decoder.verifyChecksum())
+        return std::nullopt;
+
+    return std::make_pair(WTFMove(record), insertionTime);
 }
 
+std::optional<Record> Cache::decode(const Storage::Record& storage)
+{
+    auto result = decodeRecordHeader(storage);
+
+    if (!result)
+        return std::nullopt;
+
+    auto record = WTFMove(result->first);
+    record.responseBody = WebCore::SharedBuffer::create(storage.body.data(), storage.body.size());
+
+    return WTFMove(record);
+}
+
+Vector<Key> Cache::keys() const
+{
+    Vector<Key> keys;
+    for (auto& records : m_records.values()) {
+        for (auto& record : records)
+            keys.append(record.key);
+    }
+    return keys;
+}
+
 } // namespace CacheStorage
 
 } // namespace WebKit

Modified: trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCache.h (222072 => 222073)


--- trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCache.h	2017-09-15 03:15:59 UTC (rev 222072)
+++ trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCache.h	2017-09-15 03:56:49 UTC (rev 222073)
@@ -25,7 +25,7 @@
 
 #pragma once
 
-#include "NetworkCacheKey.h"
+#include "NetworkCacheStorage.h"
 #include <WebCore/DOMCacheEngine.h>
 #include <wtf/Vector.h>
 #include <wtf/text/WTFString.h>
@@ -36,7 +36,7 @@
 
 class Caches;
 
-struct RecordData {
+struct RecordInformation {
     NetworkCache::Key key;
     double insertionTime { 0 };
 
@@ -46,58 +46,61 @@
     WebCore::URL url;
     bool hasVaryStar { false };
     HashMap<String, String> varyHeaders;
+};
 
-    struct Data {
-        WebCore::FetchHeaders::Guard requestHeadersGuard;
-        WebCore::ResourceRequest request;
-        WebCore::FetchOptions options;
-        String referrer;
+class AsynchronousPutTaskCounter;
+class ReadRecordTaskCounter;
 
-        WebCore::FetchHeaders::Guard responseHeadersGuard;
-        WebCore::ResourceResponse response;
-        WebCore::DOMCacheEngine::ResponseBody responseBody;
-    };
-
-    std::optional<Data> data;
-};
-
 class Cache {
 public:
     enum class State { Uninitialized, Opening, Open };
-    Cache(Caches&, uint64_t identifier, State, String&& name);
+    Cache(Caches&, uint64_t identifier, State, String&& name, String&& uniqueName);
 
+    bool isOpened() const { return m_state == State::Open; }
     void open(WebCore::DOMCacheEngine::CompletionCallback&&);
 
     uint64_t identifier() const { return m_identifier; }
     const String& name() const { return m_name; }
+    const String& uniqueName() const { return m_uniqueName; }
     bool isActive() const { return m_state != State::Uninitialized; }
 
-    Vector<WebCore::DOMCacheEngine::Record> retrieveRecords(const WebCore::URL&) const;
+    void retrieveRecords(const WebCore::URL&, WebCore::DOMCacheEngine::RecordsCallback&&);
     WebCore::DOMCacheEngine::CacheInfo info() const { return { m_identifier, m_name }; }
 
     void put(Vector<WebCore::DOMCacheEngine::Record>&&, WebCore::DOMCacheEngine::RecordIdentifiersCallback&&);
     void remove(WebCore::ResourceRequest&&, WebCore::CacheQueryOptions&&, WebCore::DOMCacheEngine::RecordIdentifiersCallback&&);
 
+    Vector<NetworkCache::Key> keys() const;
+
     void dispose();
     void clearMemoryRepresentation();
- 
+
+    static std::optional<WebCore::DOMCacheEngine::Record> decode(const NetworkCache::Storage::Record&);
+    static NetworkCache::Storage::Record encode(const RecordInformation&, const WebCore::DOMCacheEngine::Record&);
+
 private:
-    Vector<RecordData>* recordsFromURL(const WebCore::URL&);
-    const Vector<RecordData>* recordsFromURL(const WebCore::URL&) const;
-    RecordData& addRecord(Vector<RecordData>*, WebCore::DOMCacheEngine::Record&&);
+    Vector<RecordInformation>* recordsFromURL(const WebCore::URL&);
+    const Vector<RecordInformation>* recordsFromURL(const WebCore::URL&) const;
+    RecordInformation& addRecord(Vector<RecordInformation>*, const WebCore::DOMCacheEngine::Record&);
 
+    RecordInformation toRecordInformation(const WebCore::DOMCacheEngine::Record&);
+
     void finishOpening(WebCore::DOMCacheEngine::CompletionCallback&&, std::optional<WebCore::DOMCacheEngine::Error>&&);
+    void retrieveRecord(const RecordInformation&, Ref<ReadRecordTaskCounter>&&);
 
     void readRecordsList(WebCore::DOMCacheEngine::CompletionCallback&&);
-    void writeRecordsList(WebCore::DOMCacheEngine::CompletionCallback&&);
-    void writeRecordToDisk(RecordData&);
-    void removeRecordFromDisk(RecordData&);
+    void writeRecordToDisk(const RecordInformation&, WebCore::DOMCacheEngine::Record&&, Ref<AsynchronousPutTaskCounter>&&);
+    void updateRecordToDisk(RecordInformation&, WebCore::DOMCacheEngine::Record&&, Ref<AsynchronousPutTaskCounter>&&);
+    void removeRecordFromDisk(const RecordInformation&);
+    void readRecordFromDisk(const RecordInformation&, WTF::Function<void(Expected<WebCore::DOMCacheEngine::Record, WebCore::DOMCacheEngine::Error>&&)>&&);
+    void removeFromRecordList(const Vector<uint64_t>&);
 
     Caches& m_caches;
     State m_state;
     uint64_t m_identifier { 0 };
     String m_name;
-    HashMap<String, Vector<RecordData>> m_records;
+    String m_uniqueName;
+    HashMap<String, Vector<RecordInformation>> m_records;
     uint64_t m_nextRecordIdentifier { 0 };
     Vector<WebCore::DOMCacheEngine::CompletionCallback> m_pendingOpeningCallbacks;
 };

Modified: trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCaches.cpp (222072 => 222073)


--- trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCaches.cpp	2017-09-15 03:15:59 UTC (rev 222072)
+++ trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCaches.cpp	2017-09-15 03:56:49 UTC (rev 222073)
@@ -27,6 +27,7 @@
 #include "CacheStorageEngine.h"
 
 #include "NetworkCacheCoders.h"
+#include <wtf/UUID.h>
 #include <wtf/text/StringBuilder.h>
 
 using namespace WebCore::DOMCacheEngine;
@@ -59,11 +60,18 @@
 
 void Caches::initialize(WebCore::DOMCacheEngine::CompletionCallback&& callback)
 {
-    if (m_isInitialized || m_rootPath.isNull()) {
+    if (m_isInitialized) {
         callback(std::nullopt);
         return;
     }
 
+    if (m_rootPath.isNull()) {
+        makeDirty();
+        m_isInitialized = true;
+        callback(std::nullopt);
+        return;
+    }
+
     if (m_storage) {
         m_pendingInitializationCallbacks.append(WTFMove(callback));
         return;
@@ -75,6 +83,7 @@
         return;
     }
     m_storage = storage.releaseNonNull();
+    m_storage->writeWithoutWaiting();
     readCachesFromDisk([this, callback = WTFMove(callback)](Expected<Vector<Cache>, Error>&& result) {
         makeDirty();
 
@@ -120,7 +129,9 @@
 
 void Caches::open(const String& name, CacheIdentifierCallback&& callback)
 {
+    ASSERT(m_isInitialized);
     ASSERT(m_engine);
+
     if (auto* cache = find(name)) {
         cache->open([cacheIdentifier = cache->identifier(), callback = WTFMove(callback)](std::optional<Error>&& error) mutable {
             if (error) {
@@ -135,7 +146,7 @@
     makeDirty();
 
     uint64_t cacheIdentifier = m_engine->nextCacheIdentifier();
-    m_caches.append(Cache { *this, cacheIdentifier, Cache::State::Open, String { name } });
+    m_caches.append(Cache { *this, cacheIdentifier, Cache::State::Open, String { name }, createCanonicalUUIDString() });
 
     writeCachesToDisk([callback = WTFMove(callback), cacheIdentifier](std::optional<Error>&& error) mutable {
         callback(CacheIdentifierOperationResult { cacheIdentifier, !!error });
@@ -144,6 +155,7 @@
 
 void Caches::remove(uint64_t identifier, CacheIdentifierCallback&& callback)
 {
+    ASSERT(m_isInitialized);
     ASSERT(m_engine);
 
     auto position = m_caches.findMatching([&](const auto& item) { return item.identifier() == identifier; });
@@ -168,6 +180,9 @@
 {
     auto position = m_removedCaches.findMatching([&](const auto& item) { return item.identifier() == cache.identifier(); });
     if (position != notFound) {
+        if (m_storage)
+            m_storage->remove(cache.keys(), { });
+
         m_removedCaches.remove(position);
         return;
     }
@@ -184,13 +199,15 @@
 
     uint64_t size = caches.size();
     encoder << size;
-    for (auto& cache : caches)
+    for (auto& cache : caches) {
         encoder << cache.name();
+        encoder << cache.uniqueName();
+    }
 
     return Data { encoder.buffer(), encoder.bufferSize() };
 }
 
-static inline Expected<Vector<String>, Error> decodeCachesNames(const Data& data, int error)
+static inline Expected<Vector<std::pair<String, String>>, Error> decodeCachesNames(const Data& data, int error)
 {
     if (error)
         return makeUnexpected(Error::ReadDisk);
@@ -200,14 +217,17 @@
     if (!decoder.decode(count))
         return makeUnexpected(Error::ReadDisk);
 
-    Vector<String> names;
+    Vector<std::pair<String, String>> names;
     names.reserveInitialCapacity(count);
     for (size_t index = 0; index < count; ++index) {
         String name;
         if (!decoder.decode(name))
             return makeUnexpected(Error::ReadDisk);
+        String uniqueName;
+        if (!decoder.decode(uniqueName))
+            return makeUnexpected(Error::ReadDisk);
 
-        names.uncheckedAppend(WTFMove(name));
+        names.uncheckedAppend(std::pair<String, String> { WTFMove(name), WTFMove(uniqueName) });
     }
     return names;
 }
@@ -240,8 +260,8 @@
             callback(makeUnexpected(result.error()));
             return;
         }
-        callback(WTF::map(WTFMove(result.value()), [this] (String&& name) {
-            return Cache { *this, m_engine->nextCacheIdentifier(), Cache::State::Uninitialized, WTFMove(name) };
+        callback(WTF::map(WTFMove(result.value()), [this] (auto&& pair) {
+            return Cache { *this, m_engine->nextCacheIdentifier(), Cache::State::Uninitialized, WTFMove(pair.first), WTFMove(pair.second) };
         }));
     });
 }
@@ -266,6 +286,76 @@
     });
 }
 
+void Caches::readRecordsList(Cache& cache, NetworkCache::Storage::TraverseHandler&& callback)
+{
+    ASSERT(m_isInitialized);
+
+    if (!m_storage) {
+        callback(nullptr, { });
+        return;
+    }
+    m_storage->traverse(cache.uniqueName(), 0, [protectedStorage = makeRef(*m_storage), callback = WTFMove(callback)](const auto* storage, const auto& information) {
+        callback(storage, information);
+    });
+}
+
+void Caches::writeRecord(const Cache& cache, const RecordInformation& recordInformation, Record&& record, CompletionCallback&& callback)
+{
+    ASSERT(m_isInitialized);
+
+    // FIXME: Check for storage quota.
+    if (!shouldPersist()) {
+        m_volatileStorage.set(recordInformation.key, WTFMove(record));
+        return;
+    }
+
+    m_storage->store(Cache::encode(recordInformation, record), [protectedStorage = makeRef(*m_storage), callback = WTFMove(callback)](const Data&) {
+        callback(std::nullopt);
+    });
+}
+
+void Caches::readRecord(const NetworkCache::Key& key, WTF::Function<void(Expected<Record, Error>&&)>&& callback)
+{
+    ASSERT(m_isInitialized);
+
+    if (!shouldPersist()) {
+        auto iterator = m_volatileStorage.find(key);
+        if (iterator == m_volatileStorage.end()) {
+            callback(makeUnexpected(Error::Internal));
+            return;
+        }
+        callback(iterator->value.copy());
+        return;
+    }
+
+    m_storage->retrieve(key, 4, [protectedStorage = makeRef(*m_storage), callback = WTFMove(callback)](std::unique_ptr<Storage::Record> storage) mutable {
+        if (!storage) {
+            callback(makeUnexpected(Error::ReadDisk));
+            return false;
+        }
+
+        auto record = Cache::decode(*storage);
+        if (!record) {
+            callback(makeUnexpected(Error::ReadDisk));
+            return false;
+        }
+
+        callback(WTFMove(record.value()));
+        return true;
+    });
+}
+
+void Caches::removeRecord(const NetworkCache::Key& key)
+{
+    ASSERT(m_isInitialized);
+
+    if (!shouldPersist()) {
+        m_volatileStorage.remove(key);
+        return;
+    }
+    m_storage->remove(key);
+}
+
 void Caches::clearMemoryRepresentation()
 {
     makeDirty();
@@ -272,8 +362,6 @@
     m_caches.clear();
     m_isInitialized = false;
     m_storage = nullptr;
-    if (m_engine)
-        m_engine->removeCaches(m_origin);
 }
 
 bool Caches::isDirty(uint64_t updateCounter) const
@@ -282,6 +370,17 @@
     return m_updateCounter != updateCounter;
 }
 
+const NetworkCache::Salt& Caches::salt() const
+{
+    if (m_engine)
+        return m_engine->salt();
+
+    if (!m_volatileSalt)
+        m_volatileSalt = Salt { };
+
+    return m_volatileSalt.value();
+}
+
 CacheInfos Caches::cacheInfos(uint64_t updateCounter) const
 {
     Vector<CacheInfo> cacheInfos;

Modified: trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCaches.h (222072 => 222073)


--- trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCaches.h	2017-09-15 03:15:59 UTC (rev 222072)
+++ trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCaches.h	2017-09-15 03:56:49 UTC (rev 222073)
@@ -52,6 +52,15 @@
     Cache* find(uint64_t identifier);
     void appendRepresentation(StringBuilder&) const;
 
+    void readRecordsList(Cache&, NetworkCache::Storage::TraverseHandler&&);
+    void readRecord(const NetworkCache::Key&, WTF::Function<void(Expected<WebCore::DOMCacheEngine::Record, WebCore::DOMCacheEngine::Error>&&)>&&);
+    void writeRecord(const Cache&, const RecordInformation&, WebCore::DOMCacheEngine::Record&&, WebCore::DOMCacheEngine::CompletionCallback&&);
+    void removeRecord(const NetworkCache::Key&);
+
+    const NetworkCache::Salt& salt() const;
+
+    bool shouldPersist() const { return !m_rootPath.isNull(); }
+
 private:
     Caches(Engine&, String&& origin);
 
@@ -58,11 +67,8 @@
     void readCachesFromDisk(WTF::Function<void(Expected<Vector<Cache>, WebCore::DOMCacheEngine::Error>&&)>&&);
     void writeCachesToDisk(WebCore::DOMCacheEngine::CompletionCallback&&);
 
-
     Cache* find(const String& name);
 
-    bool shouldPersist() const { return !m_rootPath.isNull(); }
-
     void makeDirty() { ++m_updateCounter; }
     bool isDirty(uint64_t updateCounter) const;
 
@@ -74,6 +80,8 @@
     Vector<Cache> m_caches;
     Vector<Cache> m_removedCaches;
     RefPtr<NetworkCache::Storage> m_storage;
+    HashMap<NetworkCache::Key, WebCore::DOMCacheEngine::Record> m_volatileStorage;
+    mutable std::optional<NetworkCache::Salt> m_volatileSalt;
     Vector<WebCore::DOMCacheEngine::CompletionCallback> m_pendingInitializationCallbacks;
 };
 

Modified: trunk/Source/WebKit/NetworkProcess/cache/NetworkCacheStorage.cpp (222072 => 222073)


--- trunk/Source/WebKit/NetworkProcess/cache/NetworkCacheStorage.cpp	2017-09-15 03:15:59 UTC (rev 222072)
+++ trunk/Source/WebKit/NetworkProcess/cache/NetworkCacheStorage.cpp	2017-09-15 03:56:49 UTC (rev 222073)
@@ -846,10 +846,7 @@
     if (!isInitialWrite)
         return;
 
-    // Delay the start of writes a bit to avoid affecting early page load.
-    // Completing writes will dispatch more writes without delay.
-    static const Seconds initialWriteDelay = 1_s;
-    m_writeOperationDispatchTimer.startOneShot(initialWriteDelay);
+    m_writeOperationDispatchTimer.startOneShot(m_initialWriteDelay);
 }
 
 void Storage::traverse(const String& type, TraverseFlags flags, TraverseHandler&& traverseHandler)

Modified: trunk/Source/WebKit/NetworkProcess/cache/NetworkCacheStorage.h (222072 => 222073)


--- trunk/Source/WebKit/NetworkProcess/cache/NetworkCacheStorage.h	2017-09-15 03:15:59 UTC (rev 222072)
+++ trunk/Source/WebKit/NetworkProcess/cache/NetworkCacheStorage.h	2017-09-15 03:56:49 UTC (rev 222073)
@@ -104,6 +104,8 @@
 
     ~Storage();
 
+    void writeWithoutWaiting() { m_initialWriteDelay = 0_s; };
+
 private:
     Storage(const String& directoryPath, Mode, Salt);
 
@@ -182,6 +184,10 @@
     Ref<WorkQueue> m_serialBackgroundIOQueue;
 
     BlobStorage m_blobStorage;
+
+    // By default, delay the start of writes a bit to avoid affecting early page load.
+    // Completing writes will dispatch more writes without delay.
+    Seconds m_initialWriteDelay { 1_s };
 };
 
 // FIXME: Remove, used by NetworkCacheStatistics only.
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to