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.