Title: [223073] trunk
Revision
223073
Author
[email protected]
Date
2017-10-09 15:17:05 -0700 (Mon, 09 Oct 2017)

Log Message

Add quota to cache API
https://bugs.webkit.org/show_bug.cgi?id=177552

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

Source/WebCore:

Tests: http/wpt/cache-storage/cache-quota.any.html

Storing padded opaque response body sizes within FetchResponse and CacheStorageConnection.
See https://github.com/whatwg/storage/issues/31 for the rationale about this padding.
Storing in CacheStorageConnection is needed for handling cloned network fetched created responses.
Storing in FetchResponse is needed for handling cloned cache-storage created opaque responses.

Adding internals to query and set the fuzzed size of a response.

* Modules/cache/CacheStorageConnection.cpp:
(WebCore::computeRealBodySize):
(WebCore::CacheStorageConnection::computeRecordBodySize):
(WebCore::CacheStorageConnection::setResponseBodySizeWithPadding):
(WebCore::CacheStorageConnection::responseBodySizeWithPadding const):
* Modules/cache/CacheStorageConnection.h:
* Modules/cache/DOMCache.cpp:
(WebCore::DOMCache::toConnectionRecord):
(WebCore::DOMCache::updateRecords):
* Modules/cache/DOMCache.h:
* Modules/cache/DOMCacheEngine.cpp:
(WebCore::DOMCacheEngine::errorToException):
(WebCore::DOMCacheEngine::Record::copy const):
* Modules/cache/DOMCacheEngine.h:
* Modules/cache/WorkerCacheStorageConnection.cpp:
(WebCore::toCrossThreadRecordData):
(WebCore::fromCrossThreadRecordData):
* Modules/fetch/FetchResponse.cpp:
(WebCore::FetchResponse::clone):
(WebCore::FetchResponse::BodyLoader::didReceiveResponse):
* Modules/fetch/FetchResponse.h:
* Modules/fetch/FetchResponse.idl:
* testing/Internals.cpp:
(WebCore::Internals::setResponseSizeWithPadding):
(WebCore::Internals::responseSizeWithPadding const):
* testing/Internals.h:
* testing/Internals.idl:

Source/WebKit:

Adding support for quota checking in CacheStorage::Caches.
It is passed to NetworkProcess at creation time.
Default quota size is configured to 400Ko by origin per default.
This value is suitable for testing.
Future patch should raise this default value and allows configuring it.

Quota is computed based on the response body size.
This size is padded at WebCore for opaque responses.
Size is stored persistently as opaque response padded size should remain stable.
See https://github.com/whatwg/storage/issues/31 for the rationale about this padding.

In case of putting several records at the same time, the size of all records
is computed so that all records will be written or rejected together.

Sending QuotaExceeded error when quota is exceeded.
Future effort should allow asking UIProcess for quota extension.

* NetworkProcess/NetworkProcess.cpp:
(WebKit::NetworkProcess::cacheStoragePerOriginQuota const):
* NetworkProcess/NetworkProcess.h:
* NetworkProcess/NetworkProcessCreationParameters.cpp:
(WebKit::NetworkProcessCreationParameters::encode const):
(WebKit::NetworkProcessCreationParameters::decode):
* NetworkProcess/NetworkProcessCreationParameters.h:
* NetworkProcess/cache/CacheStorageEngine.cpp:
(WebKit::CacheStorage::Engine::readCachesFromDisk):
* NetworkProcess/cache/CacheStorageEngineCache.cpp:
(WebKit::CacheStorage::Cache::toRecordInformation):
(WebKit::CacheStorage::isolatedCopy):
(WebKit::CacheStorage::Cache::open):
(WebKit::CacheStorage::Cache::storeRecords):
(WebKit::CacheStorage::Cache::put):
(WebKit::CacheStorage::Cache::writeRecordToDisk):
(WebKit::CacheStorage::Cache::updateRecordToDisk):
(WebKit::CacheStorage::Cache::removeRecordFromDisk):
(WebKit::CacheStorage::Cache::encode):
(WebKit::CacheStorage::Cache::decodeRecordHeader):
(WebKit::CacheStorage::Cache::decode):
* NetworkProcess/cache/CacheStorageEngineCache.h:
* NetworkProcess/cache/CacheStorageEngineCaches.cpp:
(WebKit::CacheStorage::Caches::Caches):
(WebKit::CacheStorage::Caches::initialize):
(WebKit::CacheStorage::Caches::initializeSize):
(WebKit::CacheStorage::Caches::requestSpace):
(WebKit::CacheStorage::Caches::writeRecord):
(WebKit::CacheStorage::Caches::removeRecord):
(WebKit::CacheStorage::Caches::removeCacheEntry):
* NetworkProcess/cache/CacheStorageEngineCaches.h:
(WebKit::CacheStorage::Caches::create):
(WebKit::CacheStorage::Caches::hasEnoughSpace const):
* NetworkProcess/cache/NetworkCacheStorage.cpp:
(WebKit::NetworkCache::Storage::traverse):
* NetworkProcess/cocoa/NetworkProcessCocoa.mm:
(WebKit::NetworkProcess::platformInitializeNetworkProcessCocoa):
* Shared/WebCoreArgumentCoders.cpp:
(IPC::ArgumentCoder<DOMCacheEngine::Record>::encode):
(IPC::ArgumentCoder<DOMCacheEngine::Record>::decode):
* UIProcess/API/APIProcessPoolConfiguration.cpp:
(API::ProcessPoolConfiguration::copy):
* UIProcess/API/APIProcessPoolConfiguration.h:
* UIProcess/WebProcessPool.cpp:
(WebKit::WebProcessPool::ensureNetworkProcess):

LayoutTests:

* http/wpt/cache-storage/cache-quota.https.any-expected.txt: Added.
* http/wpt/cache-storage/cache-quota.https.any.html: Added.
* http/wpt/cache-storage/cache-quota.https.any.js: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (223072 => 223073)


--- trunk/LayoutTests/ChangeLog	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/LayoutTests/ChangeLog	2017-10-09 22:17:05 UTC (rev 223073)
@@ -1,3 +1,14 @@
+2017-10-09  Youenn Fablet  <[email protected]>
+
+        Add quota to cache API
+        https://bugs.webkit.org/show_bug.cgi?id=177552
+
+        Reviewed by Alex Christensen.
+
+        * http/wpt/cache-storage/cache-quota.https.any-expected.txt: Added.
+        * http/wpt/cache-storage/cache-quota.https.any.html: Added.
+        * http/wpt/cache-storage/cache-quota.https.any.js: Added.
+
 2017-10-09  Matt Lewis  <[email protected]>
 
         Unskipped http/tests/cache/disk-cache/disk-cache-validation-no-body.html

Added: trunk/LayoutTests/http/wpt/cache-storage/cache-quota.any-expected.txt (0 => 223073)


--- trunk/LayoutTests/http/wpt/cache-storage/cache-quota.any-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/cache-storage/cache-quota.any-expected.txt	2017-10-09 22:17:05 UTC (rev 223073)
@@ -0,0 +1,7 @@
+
+PASS Testing synthetic response body size padding 
+PASS Testing non opaque response body size padding 
+PASS Testing opaque response body size padding 
+PASS Hitting cache quota for non opaque responses 
+PASS Hitting cache quota for padded responses 
+

Added: trunk/LayoutTests/http/wpt/cache-storage/cache-quota.any.html (0 => 223073)


--- trunk/LayoutTests/http/wpt/cache-storage/cache-quota.any.html	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/cache-storage/cache-quota.any.html	2017-10-09 22:17:05 UTC (rev 223073)
@@ -0,0 +1 @@
+<!-- This file is required for WebKit test infrastructure to run the templated test -->
\ No newline at end of file

Added: trunk/LayoutTests/http/wpt/cache-storage/cache-quota.any.js (0 => 223073)


--- trunk/LayoutTests/http/wpt/cache-storage/cache-quota.any.js	                        (rev 0)
+++ trunk/LayoutTests/http/wpt/cache-storage/cache-quota.any.js	2017-10-09 22:17:05 UTC (rev 223073)
@@ -0,0 +1,169 @@
+// META: script=/service-workers/cache-storage/resources/test-helpers.js
+// META: script=/common/get-host-info.sub.js
+
+var test_url = 'https://example.com/foo';
+var test_body = 'Hello world!';
+
+function getResponseBodySizeWithPadding(response)
+{
+    var cache;
+    var request = new Request("temp");
+    var clone = response.clone();
+    return self.caches.open("test").then((c) => {
+        cache = c;
+        return cache.put(request, clone);
+    }).then(() => {
+        return self.caches.delete("temp");
+    }).then(() => {
+        return window.internals.responseSizeWithPadding(clone);
+    });
+}
+
+promise_test(() => {
+    if (!window.internals)
+        return Promise.reject("Test requires internals");
+
+    var response = new Response("");
+    return getResponseBodySizeWithPadding(response).then(size => {
+        assert_equals(size, 0, "zero size synthetic response");
+        return getResponseBodySizeWithPadding(response.clone());
+    }).then((size) => {
+        assert_equals(size, 0, "zero size synthetic cloned response");
+
+        response = new Response("a");
+        return getResponseBodySizeWithPadding(response);
+    }).then((size) => {
+        assert_equals(size, 1, "non zero size synthetic response");
+        return getResponseBodySizeWithPadding(response.clone());
+    }).then((size) => {
+        assert_equals(size, 1, "non zero size synthetic cloned response");
+    })
+}, "Testing synthetic response body size padding");
+
+promise_test(() => {
+    if (!window.internals)
+        return Promise.reject("Test requires internals");
+
+    var paddedSize;
+    var response, responseClone;
+    return fetch("").then(r => {
+        response = r;
+        responseClone = response.clone();
+        return getResponseBodySizeWithPadding(response);
+    }).then((size) => {
+        paddedSize = size;
+        return response.arrayBuffer();
+    }).then((buffer) => {
+        assert_equals(buffer.byteLength, paddedSize, "non opaque network response");
+        return getResponseBodySizeWithPadding(responseClone);
+    }).then((size) => {
+        assert_equals(size, paddedSize, "non opaque network cloned response");
+    });
+}, "Testing non opaque response body size padding");
+
+promise_test(() => {
+    if (!window.internals)
+        return Promise.reject("Test requires internals");
+
+    var actualSize, paddedSize;
+    var response, responseClone1;
+    return fetch(get_host_info().HTTP_REMOTE_ORIGIN, {mode: "no-cors"}).then(r => {
+        response = r;
+    }).then(() => {
+        return fetch(get_host_info().HTTP_ORIGIN);
+    }).then(r => {
+        return r.arrayBuffer();
+    }).then((buffer) => {
+        actualSize = buffer.byteLength;
+    }).then(() => {
+        responseClone1 = response.clone();
+        return getResponseBodySizeWithPadding(response);
+    }).then((size) => {
+        paddedSize = size;
+        return getResponseBodySizeWithPadding(responseClone1);
+    }).then((size) => {
+        assert_not_equals(size, actualSize, "padded size should be different from actual size");
+        assert_equals(size, paddedSize, "opaque network cloned response");
+    });
+}, "Testing opaque response body size padding");
+
+async function doCleanup()
+{
+    var cachesKeys = await self.caches.keys();
+    for (let name of cachesKeys) {
+        let cache = await self.caches.open(name);
+        let keys = await cache.keys();
+        for (let key of keys)
+            await cache.delete(key);
+    }
+}
+
+promise_test((test) => {
+    var cache;
+    var response1ko = new Response(new ArrayBuffer(1 * 1024));
+    var response399ko = new Response(new ArrayBuffer(399 * 1024));
+
+    return doCleanup().then(() => {
+        return self.caches.open("temp1");
+    }).then((c) => {
+        cache = c;
+        return cache.put("399ko", response399ko.clone());
+    }).then(() => {
+        return cache.put("1ko-v1", response1ko.clone());
+    }).then(() => {
+        return cache.put("1ko-v2", response1ko.clone()).then(assert_unreached, (e) => {
+            assert_equals(e.name, "QuotaExceededError");
+        });
+   }).then(() => {
+        return cache.delete("1ko-v1");
+    }).then(() => {
+        return cache.put("1ko-v2", response1ko.clone());
+    }).then(() => {
+        return cache.delete("399ko");
+    }).then(() => {
+        return cache.delete("1ko-v1");
+    }).then(() => {
+        return cache.delete("1ko-v2");
+    });
+}, 'Hitting cache quota for non opaque responses');
+
+promise_test((test) => {
+    if (!window.internals)
+        return Promise.reject("Test requires internals");
+
+    var cache;
+    var response1ko = new Response(new ArrayBuffer(1 * 1024));
+    var responsePadded = new Response(new ArrayBuffer(1 * 1024));
+    var response200ko = new Response(new ArrayBuffer(200 * 1024));
+
+    return doCleanup().then(() => {
+        return self.caches.open("temp2");
+    }).then((c) => {
+        cache = c;
+        return fetch(get_host_info().HTTP_REMOTE_ORIGIN, {mode: "no-cors"});
+    }).then((r) => {
+        responsePadded = r;
+        internals.setResponseSizeWithPadding(responsePadded, 200 * 1024);
+        return cache.put("200ko", response200ko.clone());
+    }).then(() => {
+        return cache.put("1ko-padded-to-200ko", responsePadded.clone());
+    }).then(() => {
+        return cache.put("1ko", response1ko.clone()).then(assert_unreached, (e) => {
+            assert_equals(e.name, "QuotaExceededError");
+        });
+   }).then(() => {
+        return cache.delete("1ko-padded-to-200ko");
+    }).then(() => {
+        return cache.put("1ko-v2", response1ko.clone());
+    }).then(() => {
+        return cache.put("1ko-v3", response1ko.clone());
+    }).then(() => {
+        return cache.delete("200ko");
+    }).then(() => {
+        return cache.delete("1ko-v2");
+    }).then(() => {
+        return cache.delete("1ko-v3");
+    });
+}, 'Hitting cache quota for padded responses');
+
+done();

Modified: trunk/Source/WebCore/ChangeLog (223072 => 223073)


--- trunk/Source/WebCore/ChangeLog	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebCore/ChangeLog	2017-10-09 22:17:05 UTC (rev 223073)
@@ -1,3 +1,47 @@
+2017-10-09  Youenn Fablet  <[email protected]>
+
+        Add quota to cache API
+        https://bugs.webkit.org/show_bug.cgi?id=177552
+
+        Reviewed by Alex Christensen.
+
+        Tests: http/wpt/cache-storage/cache-quota.any.html
+
+        Storing padded opaque response body sizes within FetchResponse and CacheStorageConnection.
+        See https://github.com/whatwg/storage/issues/31 for the rationale about this padding.
+        Storing in CacheStorageConnection is needed for handling cloned network fetched created responses.
+        Storing in FetchResponse is needed for handling cloned cache-storage created opaque responses.
+
+        Adding internals to query and set the fuzzed size of a response.
+
+        * Modules/cache/CacheStorageConnection.cpp:
+        (WebCore::computeRealBodySize):
+        (WebCore::CacheStorageConnection::computeRecordBodySize):
+        (WebCore::CacheStorageConnection::setResponseBodySizeWithPadding):
+        (WebCore::CacheStorageConnection::responseBodySizeWithPadding const):
+        * Modules/cache/CacheStorageConnection.h:
+        * Modules/cache/DOMCache.cpp:
+        (WebCore::DOMCache::toConnectionRecord):
+        (WebCore::DOMCache::updateRecords):
+        * Modules/cache/DOMCache.h:
+        * Modules/cache/DOMCacheEngine.cpp:
+        (WebCore::DOMCacheEngine::errorToException):
+        (WebCore::DOMCacheEngine::Record::copy const):
+        * Modules/cache/DOMCacheEngine.h:
+        * Modules/cache/WorkerCacheStorageConnection.cpp:
+        (WebCore::toCrossThreadRecordData):
+        (WebCore::fromCrossThreadRecordData):
+        * Modules/fetch/FetchResponse.cpp:
+        (WebCore::FetchResponse::clone):
+        (WebCore::FetchResponse::BodyLoader::didReceiveResponse):
+        * Modules/fetch/FetchResponse.h:
+        * Modules/fetch/FetchResponse.idl:
+        * testing/Internals.cpp:
+        (WebCore::Internals::setResponseSizeWithPadding):
+        (WebCore::Internals::responseSizeWithPadding const):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+
 2017-10-09  Zalan Bujtas  <[email protected]>
 
         Remove redundant RenderObject::virtualContinuation

Modified: trunk/Source/WebCore/Modules/cache/CacheStorageConnection.cpp (223072 => 223073)


--- trunk/Source/WebCore/Modules/cache/CacheStorageConnection.cpp	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebCore/Modules/cache/CacheStorageConnection.cpp	2017-10-09 22:17:05 UTC (rev 223073)
@@ -27,6 +27,9 @@
 #include "config.h"
 #include "CacheStorageConnection.h"
 
+#include "FetchResponse.h"
+#include <wtf/RandomNumber.h>
+
 using namespace WebCore::DOMCacheEngine;
 
 namespace WebCore {
@@ -71,6 +74,37 @@
     doBatchDeleteOperation(requestIdentifier, cacheIdentifier, request, WTFMove(options));
 }
 
+static inline uint64_t computeRealBodySize(const DOMCacheEngine::ResponseBody& body)
+{
+    uint64_t result = 0;
+    WTF::switchOn(body, [&] (const Ref<WebCore::FormData>& formData) {
+        result = formData->lengthInBytes();
+    }, [&] (const Ref<WebCore::SharedBuffer>& buffer) {
+        result = buffer->size();
+    }, [] (const std::nullptr_t&) {
+    });
+    return result;
+}
+
+uint64_t CacheStorageConnection::computeRecordBodySize(const FetchResponse& response, const DOMCacheEngine::ResponseBody& body, ResourceResponse::Tainting tainting)
+{
+    if (!response.opaqueLoadIdentifier()) {
+        ASSERT_UNUSED(tainting, tainting != ResourceResponse::Tainting::Opaque);
+        return computeRealBodySize(body);
+    }
+
+    return m_opaqueResponseToSizeWithPaddingMap.ensure(response.opaqueLoadIdentifier(), [&] () {
+        uint64_t realSize = computeRealBodySize(body);
+
+        // Padding the size as per https://github.com/whatwg/storage/issues/31.
+        uint64_t sizeWithPadding = realSize + static_cast<uint64_t>(randomNumber() * 128000);
+        sizeWithPadding = ((sizeWithPadding / 32000) + 1) * 32000;
+
+        m_opaqueResponseToSizeWithPaddingMap.set(response.opaqueLoadIdentifier(), sizeWithPadding);
+        return sizeWithPadding;
+    }).iterator->value;
+}
+
 void CacheStorageConnection::batchPutOperation(uint64_t cacheIdentifier, Vector<Record>&& records, RecordIdentifiersCallback&& callback)
 {
     uint64_t requestIdentifier = ++m_lastRequestIdentifier;

Modified: trunk/Source/WebCore/Modules/cache/CacheStorageConnection.h (223072 => 223073)


--- trunk/Source/WebCore/Modules/cache/CacheStorageConnection.h	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebCore/Modules/cache/CacheStorageConnection.h	2017-10-09 22:17:05 UTC (rev 223073)
@@ -32,6 +32,8 @@
 
 namespace WebCore {
 
+class FetchResponse;
+
 class CacheStorageConnection : public ThreadSafeRefCounted<CacheStorageConnection> {
 public:
     static Ref<CacheStorageConnection> create() { return adoptRef(*new CacheStorageConnection()); }
@@ -44,6 +46,7 @@
     void retrieveRecords(uint64_t cacheIdentifier, const URL&, DOMCacheEngine::RecordsCallback&&);
     void batchDeleteOperation(uint64_t cacheIdentifier, const ResourceRequest&, CacheQueryOptions&&, DOMCacheEngine::RecordIdentifiersCallback&&);
     void batchPutOperation(uint64_t cacheIdentifier, Vector<DOMCacheEngine::Record>&&, DOMCacheEngine::RecordIdentifiersCallback&&);
+    uint64_t computeRecordBodySize(const FetchResponse&, const DOMCacheEngine::ResponseBody&, ResourceResponse::Tainting);
 
     virtual void reference(uint64_t /* cacheIdentifier */) { }
     virtual void dereference(uint64_t /* cacheIdentifier */) { }
@@ -78,6 +81,7 @@
     HashMap<uint64_t, DOMCacheEngine::CacheInfosCallback> m_retrieveCachesPendingRequests;
     HashMap<uint64_t, DOMCacheEngine::RecordsCallback> m_retrieveRecordsPendingRequests;
     HashMap<uint64_t, DOMCacheEngine::RecordIdentifiersCallback> m_batchDeleteAndPutPendingRequests;
+    HashMap<uint64_t, uint64_t> m_opaqueResponseToSizeWithPaddingMap;
 
     uint64_t m_lastRequestIdentifier { 0 };
 };

Modified: trunk/Source/WebCore/Modules/cache/DOMCache.cpp (223072 => 223073)


--- trunk/Source/WebCore/Modules/cache/DOMCache.cpp	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebCore/Modules/cache/DOMCache.cpp	2017-10-09 22:17:05 UTC (rev 223073)
@@ -38,8 +38,6 @@
 
 namespace WebCore {
 
-static Record toConnectionRecord(const FetchRequest&, FetchResponse&, ResponseBody&&);
-
 DOMCache::DOMCache(ScriptExecutionContext& context, String&& name, uint64_t identifier, Ref<CacheStorageConnection>&& connection)
     : ActiveDOMObject(&context)
     , m_name(WTFMove(name))
@@ -480,7 +478,7 @@
     });
 }
 
-Record toConnectionRecord(const FetchRequest& request, FetchResponse& response, DOMCacheEngine::ResponseBody&& responseBody)
+Record DOMCache::toConnectionRecord(const FetchRequest& request, FetchResponse& response, DOMCacheEngine::ResponseBody&& responseBody)
 {
     // FIXME: Add a setHTTPHeaderFields on ResourceResponseBase.
     ResourceResponse cachedResponse = response.resourceResponse();
@@ -493,9 +491,15 @@
     ASSERT(!cachedRequest.isNull());
     ASSERT(!cachedResponse.isNull());
 
+    auto sizeWithPadding = response.bodySizeWithPadding();
+    if (!sizeWithPadding) {
+        sizeWithPadding = m_connection->computeRecordBodySize(response, responseBody, cachedResponse.tainting());
+        response.setBodySizeWithPadding(sizeWithPadding);
+    }
+
     return { 0, 0,
         request.headers().guard(), WTFMove(cachedRequest), request.fetchOptions(), request.internalRequestReferrer(),
-        response.headers().guard(), WTFMove(cachedResponse), WTFMove(responseBody)
+        response.headers().guard(), WTFMove(cachedResponse), WTFMove(responseBody), sizeWithPadding
     };
 }
 
@@ -533,7 +537,7 @@
             if (current.updateResponseCounter != record.updateResponseCounter) {
                 auto responseHeaders = FetchHeaders::create(record.responseHeadersGuard, HTTPHeaderMap { record.response.httpHeaderFields() });
                 auto response = FetchResponse::create(*scriptExecutionContext(), std::nullopt, WTFMove(responseHeaders), WTFMove(record.response));
-                response->setBodyData(WTFMove(record.responseBody));
+                response->setBodyData(WTFMove(record.responseBody), record.responseBodySize);
 
                 current.response = WTFMove(response);
                 current.updateResponseCounter = record.updateResponseCounter;
@@ -545,7 +549,7 @@
 
             auto responseHeaders = FetchHeaders::create(record.responseHeadersGuard, HTTPHeaderMap { record.response.httpHeaderFields() });
             auto response = FetchResponse::create(*scriptExecutionContext(), std::nullopt, WTFMove(responseHeaders), WTFMove(record.response));
-            response->setBodyData(WTFMove(record.responseBody));
+            response->setBodyData(WTFMove(record.responseBody), record.responseBodySize);
 
             newRecords.append(CacheStorageRecord { record.identifier, record.updateResponseCounter, WTFMove(request), WTFMove(response) });
         }

Modified: trunk/Source/WebCore/Modules/cache/DOMCache.h (223072 => 223073)


--- trunk/Source/WebCore/Modules/cache/DOMCache.h	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebCore/Modules/cache/DOMCache.h	2017-10-09 22:17:05 UTC (rev 223073)
@@ -80,6 +80,7 @@
 
     void updateRecords(Vector<DOMCacheEngine::Record>&&);
     Vector<Ref<FetchResponse>> cloneResponses(const Vector<CacheStorageRecord>&);
+    DOMCacheEngine::Record toConnectionRecord(const FetchRequest&, FetchResponse&, DOMCacheEngine::ResponseBody&&);
 
     String m_name;
     uint64_t m_identifier;

Modified: trunk/Source/WebCore/Modules/cache/DOMCacheEngine.cpp (223072 => 223073)


--- trunk/Source/WebCore/Modules/cache/DOMCacheEngine.cpp	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebCore/Modules/cache/DOMCacheEngine.cpp	2017-10-09 22:17:05 UTC (rev 223073)
@@ -44,6 +44,8 @@
         return Exception { TypeError, ASCIILiteral("Failed reading data from the file system") };
     case Error::WriteDisk:
         return Exception { TypeError, ASCIILiteral("Failed writing data to the file system") };
+    case Error::QuotaExceeded:
+        return Exception { QuotaExceededError, ASCIILiteral("Quota exceeded") };
     default:
         return Exception { TypeError, ASCIILiteral("Internal error") };
     }
@@ -135,7 +137,7 @@
 
 Record Record::copy() const
 {
-    return Record { identifier, updateResponseCounter, requestHeadersGuard, request, options, referrer, responseHeadersGuard, response, copyResponseBody(responseBody) };
+    return Record { identifier, updateResponseCounter, requestHeadersGuard, request, options, referrer, responseHeadersGuard, response, copyResponseBody(responseBody), responseBodySize };
 }
 
 static inline CacheInfo isolateCacheInfo(const CacheInfo& info)

Modified: trunk/Source/WebCore/Modules/cache/DOMCacheEngine.h (223072 => 223073)


--- trunk/Source/WebCore/Modules/cache/DOMCacheEngine.h	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebCore/Modules/cache/DOMCacheEngine.h	2017-10-09 22:17:05 UTC (rev 223073)
@@ -42,6 +42,7 @@
     NotImplemented,
     ReadDisk,
     WriteDisk,
+    QuotaExceeded,
     Internal
 };
 
@@ -68,6 +69,7 @@
     FetchHeaders::Guard responseHeadersGuard;
     ResourceResponse response;
     ResponseBody responseBody;
+    uint64_t responseBodySize;
 };
 
 struct CacheInfo {
@@ -121,12 +123,12 @@
     decoder >> infos;
     if (!infos)
         return std::nullopt;
-    
+
     std::optional<uint64_t> updateCounter;
     decoder >> updateCounter;
     if (!updateCounter)
         return std::nullopt;
-    
+
     return {{ WTFMove(*infos), WTFMove(*updateCounter) }};
 }
 
@@ -142,7 +144,7 @@
     decoder >> identifier;
     if (!identifier)
         return std::nullopt;
-    
+
     std::optional<bool> hadStorageError;
     decoder >> hadStorageError;
     if (!hadStorageError)
@@ -161,6 +163,7 @@
         WebCore::DOMCacheEngine::Error::NotImplemented,
         WebCore::DOMCacheEngine::Error::ReadDisk,
         WebCore::DOMCacheEngine::Error::WriteDisk,
+        WebCore::DOMCacheEngine::Error::QuotaExceeded,
         WebCore::DOMCacheEngine::Error::Internal
     >;
 };

Modified: trunk/Source/WebCore/Modules/cache/WorkerCacheStorageConnection.cpp (223072 => 223073)


--- trunk/Source/WebCore/Modules/cache/WorkerCacheStorageConnection.cpp	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebCore/Modules/cache/WorkerCacheStorageConnection.cpp	2017-10-09 22:17:05 UTC (rev 223073)
@@ -53,6 +53,7 @@
     FetchHeaders::Guard responseHeadersGuard;
     ResourceResponse::CrossThreadData response;
     ResponseBody responseBody;
+    uint64_t responseBodySize;
 };
 
 static CrossThreadRecordData toCrossThreadRecordData(const Record& record)
@@ -66,7 +67,8 @@
         record.referrer.isolatedCopy(),
         record.responseHeadersGuard,
         record.response.crossThreadData(),
-        isolatedResponseBody(record.responseBody)
+        isolatedResponseBody(record.responseBody),
+        record.responseBodySize
     };
 }
 
@@ -81,7 +83,8 @@
         WTFMove(data.referrer),
         data.responseHeadersGuard,
         ResourceResponse::fromCrossThreadData(WTFMove(data.response)),
-        WTFMove(data.responseBody)
+        WTFMove(data.responseBody),
+        data.responseBodySize
     };
 }
 

Modified: trunk/Source/WebCore/Modules/fetch/FetchResponse.cpp (223072 => 223073)


--- trunk/Source/WebCore/Modules/fetch/FetchResponse.cpp	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebCore/Modules/fetch/FetchResponse.cpp	2017-10-09 22:17:05 UTC (rev 223073)
@@ -159,6 +159,8 @@
     clone->cloneBody(*this);
     if (isBodyOpaque())
         clone->setBodyAsOpaque();
+    clone->m_opaqueLoadIdentifier = m_opaqueLoadIdentifier;
+    clone->m_bodySizeWithPadding = m_bodySizeWithPadding;
     return WTFMove(clone);
 }
 
@@ -239,11 +241,14 @@
     m_response.unsetPendingActivity(&m_response);
 }
 
+static uint64_t nextOpaqueLoadIdentifier { 0 };
 void FetchResponse::BodyLoader::didReceiveResponse(const ResourceResponse& resourceResponse)
 {
     m_response.m_response = ResourceResponseBase::filter(resourceResponse);
-    if (resourceResponse.tainting() == ResourceResponse::Tainting::Opaque)
+    if (resourceResponse.tainting() == ResourceResponse::Tainting::Opaque) {
+        m_response.m_opaqueLoadIdentifier = ++nextOpaqueLoadIdentifier;
         m_response.setBodyAsOpaque();
+    }
 
     m_response.m_headers->filterAndFill(m_response.m_response.httpHeaderFields(), FetchHeaders::Guard::Response);
     m_response.updateContentType();
@@ -326,8 +331,9 @@
     m_bodyLoader->setConsumeDataCallback(WTFMove(callback));
 }
 
-void FetchResponse::setBodyData(ResponseData&& data)
+void FetchResponse::setBodyData(ResponseData&& data, uint64_t bodySizeWithPadding)
 {
+    m_bodySizeWithPadding = bodySizeWithPadding;
     WTF::switchOn(data,
         [this](Ref<FormData>& formData) {
             if (isBodyNull())

Modified: trunk/Source/WebCore/Modules/fetch/FetchResponse.h (223072 => 223073)


--- trunk/Source/WebCore/Modules/fetch/FetchResponse.h	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebCore/Modules/fetch/FetchResponse.h	2017-10-09 22:17:05 UTC (rev 223073)
@@ -87,7 +87,7 @@
 
     using ResponseData = Variant<std::nullptr_t, Ref<FormData>, Ref<SharedBuffer>>;
     ResponseData consumeBody();
-    void setBodyData(ResponseData&&);
+    void setBodyData(ResponseData&&, uint64_t bodySizeWithPadding);
 
     bool isLoading() const { return !!m_bodyLoader; }
 
@@ -97,6 +97,10 @@
 
     const ResourceResponse& resourceResponse() const { return m_response; }
 
+    uint64_t bodySizeWithPadding() const { return m_bodySizeWithPadding; }
+    void setBodySizeWithPadding(uint64_t size) { m_bodySizeWithPadding = size; }
+    uint64_t opaqueLoadIdentifier() const { return m_opaqueLoadIdentifier; }
+
 private:
     FetchResponse(ScriptExecutionContext&, std::optional<FetchBody>&&, Ref<FetchHeaders>&&, ResourceResponse&&);
 
@@ -138,6 +142,9 @@
     ResourceResponse m_response;
     std::optional<BodyLoader> m_bodyLoader;
     mutable String m_responseURL;
+    // Opaque responses will padd their body size when used with Cache API.
+    uint64_t m_bodySizeWithPadding { 0 };
+    uint64_t m_opaqueLoadIdentifier { 0 };
 };
 
 inline Ref<FetchResponse> FetchResponse::create(ScriptExecutionContext& context, std::optional<FetchBody>&& body, Ref<FetchHeaders>&& headers, ResourceResponse&& response)

Modified: trunk/Source/WebCore/Modules/fetch/FetchResponse.idl (223072 => 223073)


--- trunk/Source/WebCore/Modules/fetch/FetchResponse.idl	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebCore/Modules/fetch/FetchResponse.idl	2017-10-09 22:17:05 UTC (rev 223073)
@@ -43,6 +43,7 @@
     ConstructorCallWith=ScriptExecutionContext,
     ConstructorMayThrowException,
     EnabledAtRuntime=FetchAPI,
+    ExportToWrappedFunction,
     Exposed=(Window,Worker),
     InterfaceName=Response,
 ] interface FetchResponse {

Modified: trunk/Source/WebCore/testing/Internals.cpp (223072 => 223073)


--- trunk/Source/WebCore/testing/Internals.cpp	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebCore/testing/Internals.cpp	2017-10-09 22:17:05 UTC (rev 223073)
@@ -58,6 +58,7 @@
 #include "Element.h"
 #include "EventHandler.h"
 #include "ExtensionStyleSheets.h"
+#include "FetchResponse.h"
 #include "File.h"
 #include "FontCache.h"
 #include "FormController.h"
@@ -4174,4 +4175,14 @@
     contextDocument()->setConsoleMessageListener(WTFMove(listener));
 }
 
+void Internals::setResponseSizeWithPadding(FetchResponse& response, uint64_t size)
+{
+    response.setBodySizeWithPadding(size);
+}
+
+uint64_t Internals::responseSizeWithPadding(FetchResponse& response) const
+{
+    return response.bodySizeWithPadding();
+}
+
 } // namespace WebCore

Modified: trunk/Source/WebCore/testing/Internals.h (223072 => 223073)


--- trunk/Source/WebCore/testing/Internals.h	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebCore/testing/Internals.h	2017-10-09 22:17:05 UTC (rev 223073)
@@ -50,6 +50,7 @@
 class DOMWindow;
 class Document;
 class Element;
+class FetchResponse;
 class File;
 class Frame;
 class GCObservation;
@@ -600,6 +601,8 @@
 
     void clearCacheStorageMemoryRepresentation(DOMPromiseDeferred<void>&&);
     void cacheStorageEngineRepresentation(DOMPromiseDeferred<IDLDOMString>&&);
+    void setResponseSizeWithPadding(FetchResponse&, uint64_t size);
+    uint64_t responseSizeWithPadding(FetchResponse&) const;
 
     void setConsoleMessageListener(RefPtr<StringCallback>&&);
 

Modified: trunk/Source/WebCore/testing/Internals.idl (223072 => 223073)


--- trunk/Source/WebCore/testing/Internals.idl	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebCore/testing/Internals.idl	2017-10-09 22:17:05 UTC (rev 223073)
@@ -547,6 +547,8 @@
 
     Promise<void> clearCacheStorageMemoryRepresentation();
     Promise<DOMString> cacheStorageEngineRepresentation();
+    void setResponseSizeWithPadding(FetchResponse response, unsigned long long size);
+    unsigned long long responseSizeWithPadding(FetchResponse response);
 
     void setConsoleMessageListener(StringCallback callback);
 

Modified: trunk/Source/WebKit/ChangeLog (223072 => 223073)


--- trunk/Source/WebKit/ChangeLog	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebKit/ChangeLog	2017-10-09 22:17:05 UTC (rev 223073)
@@ -1,3 +1,73 @@
+2017-10-09  Youenn Fablet  <[email protected]>
+
+        Add quota to cache API
+        https://bugs.webkit.org/show_bug.cgi?id=177552
+
+        Reviewed by Alex Christensen.
+
+        Adding support for quota checking in CacheStorage::Caches.
+        It is passed to NetworkProcess at creation time.
+        Default quota size is configured to 400Ko by origin per default.
+        This value is suitable for testing.
+        Future patch should raise this default value and allows configuring it.
+
+        Quota is computed based on the response body size.
+        This size is padded at WebCore for opaque responses.
+        Size is stored persistently as opaque response padded size should remain stable.
+        See https://github.com/whatwg/storage/issues/31 for the rationale about this padding.
+
+        In case of putting several records at the same time, the size of all records
+        is computed so that all records will be written or rejected together.
+
+        Sending QuotaExceeded error when quota is exceeded.
+        Future effort should allow asking UIProcess for quota extension.
+
+        * NetworkProcess/NetworkProcess.cpp:
+        (WebKit::NetworkProcess::cacheStoragePerOriginQuota const):
+        * NetworkProcess/NetworkProcess.h:
+        * NetworkProcess/NetworkProcessCreationParameters.cpp:
+        (WebKit::NetworkProcessCreationParameters::encode const):
+        (WebKit::NetworkProcessCreationParameters::decode):
+        * NetworkProcess/NetworkProcessCreationParameters.h:
+        * NetworkProcess/cache/CacheStorageEngine.cpp:
+        (WebKit::CacheStorage::Engine::readCachesFromDisk):
+        * NetworkProcess/cache/CacheStorageEngineCache.cpp:
+        (WebKit::CacheStorage::Cache::toRecordInformation):
+        (WebKit::CacheStorage::isolatedCopy):
+        (WebKit::CacheStorage::Cache::open):
+        (WebKit::CacheStorage::Cache::storeRecords):
+        (WebKit::CacheStorage::Cache::put):
+        (WebKit::CacheStorage::Cache::writeRecordToDisk):
+        (WebKit::CacheStorage::Cache::updateRecordToDisk):
+        (WebKit::CacheStorage::Cache::removeRecordFromDisk):
+        (WebKit::CacheStorage::Cache::encode):
+        (WebKit::CacheStorage::Cache::decodeRecordHeader):
+        (WebKit::CacheStorage::Cache::decode):
+        * NetworkProcess/cache/CacheStorageEngineCache.h:
+        * NetworkProcess/cache/CacheStorageEngineCaches.cpp:
+        (WebKit::CacheStorage::Caches::Caches):
+        (WebKit::CacheStorage::Caches::initialize):
+        (WebKit::CacheStorage::Caches::initializeSize):
+        (WebKit::CacheStorage::Caches::requestSpace):
+        (WebKit::CacheStorage::Caches::writeRecord):
+        (WebKit::CacheStorage::Caches::removeRecord):
+        (WebKit::CacheStorage::Caches::removeCacheEntry):
+        * NetworkProcess/cache/CacheStorageEngineCaches.h:
+        (WebKit::CacheStorage::Caches::create):
+        (WebKit::CacheStorage::Caches::hasEnoughSpace const):
+        * NetworkProcess/cache/NetworkCacheStorage.cpp:
+        (WebKit::NetworkCache::Storage::traverse):
+        * NetworkProcess/cocoa/NetworkProcessCocoa.mm:
+        (WebKit::NetworkProcess::platformInitializeNetworkProcessCocoa):
+        * Shared/WebCoreArgumentCoders.cpp:
+        (IPC::ArgumentCoder<DOMCacheEngine::Record>::encode):
+        (IPC::ArgumentCoder<DOMCacheEngine::Record>::decode):
+        * UIProcess/API/APIProcessPoolConfiguration.cpp:
+        (API::ProcessPoolConfiguration::copy):
+        * UIProcess/API/APIProcessPoolConfiguration.h:
+        * UIProcess/WebProcessPool.cpp:
+        (WebKit::WebProcessPool::ensureNetworkProcess):
+
 2017-10-09  Sam Weinig  <[email protected]>
 
         Make HashMap::keys() and HashMap::values() work with WTF::map/WTF::copyToVector

Modified: trunk/Source/WebKit/NetworkProcess/NetworkProcess.cpp (223072 => 223073)


--- trunk/Source/WebKit/NetworkProcess/NetworkProcess.cpp	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebKit/NetworkProcess/NetworkProcess.cpp	2017-10-09 22:17:05 UTC (rev 223073)
@@ -766,6 +766,11 @@
 #endif
 }
 
+uint64_t NetworkProcess::cacheStoragePerOriginQuota() const
+{
+    return m_cacheStoragePerOriginQuota;
+}
+
 #if !PLATFORM(COCOA)
 void NetworkProcess::initializeProcess(const ChildProcessInitializationParameters&)
 {

Modified: trunk/Source/WebKit/NetworkProcess/NetworkProcess.h (223072 => 223073)


--- trunk/Source/WebKit/NetworkProcess/NetworkProcess.h	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebKit/NetworkProcess/NetworkProcess.h	2017-10-09 22:17:05 UTC (rev 223073)
@@ -145,6 +145,7 @@
 
     Seconds loadThrottleLatency() const { return m_loadThrottleLatency; }
     String cacheStorageDirectory(PAL::SessionID) const;
+    uint64_t cacheStoragePerOriginQuota() const;
 
     void preconnectTo(const WebCore::URL&, WebCore::StoredCredentialsPolicy);
 
@@ -237,6 +238,7 @@
     Vector<RefPtr<NetworkConnectionToWebProcess>> m_webProcessConnections;
 
     String m_cacheStorageDirectory;
+    uint64_t m_cacheStoragePerOriginQuota { 0 };
     String m_diskCacheDirectory;
     bool m_hasSetCacheModel;
     CacheModel m_cacheModel;

Modified: trunk/Source/WebKit/NetworkProcess/NetworkProcessCreationParameters.cpp (223072 => 223073)


--- trunk/Source/WebKit/NetworkProcess/NetworkProcessCreationParameters.cpp	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebKit/NetworkProcess/NetworkProcessCreationParameters.cpp	2017-10-09 22:17:05 UTC (rev 223073)
@@ -46,6 +46,7 @@
     encoder << diskCacheSizeOverride;
     encoder << canHandleHTTPSServerTrustEvaluation;
     encoder << cacheStorageDirectory;
+    encoder << cacheStoragePerOriginQuota;
     encoder << cacheStorageDirectoryExtensionHandle;
     encoder << diskCacheDirectory;
     encoder << diskCacheDirectoryExtensionHandle;
@@ -116,6 +117,8 @@
         return false;
     if (!decoder.decode(result.cacheStorageDirectory))
         return false;
+    if (!decoder.decode(result.cacheStoragePerOriginQuota))
+        return false;
     if (!decoder.decode(result.cacheStorageDirectoryExtensionHandle))
         return false;
     if (!decoder.decode(result.diskCacheDirectory))

Modified: trunk/Source/WebKit/NetworkProcess/NetworkProcessCreationParameters.h (223072 => 223073)


--- trunk/Source/WebKit/NetworkProcess/NetworkProcessCreationParameters.h	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebKit/NetworkProcess/NetworkProcessCreationParameters.h	2017-10-09 22:17:05 UTC (rev 223073)
@@ -56,6 +56,7 @@
     bool canHandleHTTPSServerTrustEvaluation { true };
 
     String cacheStorageDirectory;
+    uint64_t cacheStoragePerOriginQuota;
     SandboxExtension::Handle cacheStorageDirectoryExtensionHandle;
     String diskCacheDirectory;
     SandboxExtension::Handle diskCacheDirectoryExtensionHandle;

Modified: trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngine.cpp (223072 => 223073)


--- trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngine.cpp	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngine.cpp	2017-10-09 22:17:05 UTC (rev 223073)
@@ -190,7 +190,7 @@
 {
     initialize([this, origin, callback = WTFMove(callback)](std::optional<Error>&& error) mutable {
         auto& caches = m_caches.ensure(origin, [&origin, this] {
-            return Caches::create(*this, String { origin });
+            return Caches::create(*this, String { origin }, NetworkProcess::singleton().cacheStoragePerOriginQuota());
         }).iterator->value;
 
         if (caches->isInitialized()) {

Modified: trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCache.cpp (223072 => 223073)


--- trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCache.cpp	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCache.cpp	2017-10-09 22:17:05 UTC (rev 223073)
@@ -52,8 +52,6 @@
 
 namespace CacheStorage {
 
-static std::optional<std::pair<Record, double>> decodeRecordHeader(const Storage::Record&);
-
 static inline String computeKeyURL(const URL& url)
 {
     URL keyURL { url };
@@ -100,7 +98,7 @@
 RecordInformation Cache::toRecordInformation(const Record& record)
 {
     Key key { ASCIILiteral("record"), m_uniqueName, { }, createCanonicalUUIDString(), m_caches.salt() };
-    RecordInformation recordInformation { WTFMove(key), monotonicallyIncreasingTimeMS(), record.identifier, 0 , record.request.url(), false, { } };
+    RecordInformation recordInformation { WTFMove(key), monotonicallyIncreasingTimeMS(), record.identifier, 0 , record.responseBodySize, record.request.url(), false, { } };
 
     updateVaryInformation(recordInformation, record.request, record.response);
 
@@ -123,6 +121,11 @@
 
 void Cache::clearMemoryRepresentation()
 {
+    for (auto& records : m_records.values()) {
+        for (auto& record : records)
+            removeRecordFromDisk(record);
+    }
+
     m_records = { };
     m_nextRecordIdentifier = 0;
     m_state = State::Uninitialized;
@@ -130,7 +133,7 @@
 
 static RecordInformation isolatedCopy(const RecordInformation& information)
 {
-    auto result = RecordInformation { information.key, information.insertionTime, information.identifier, information.updateResponseCounter, information.url.isolatedCopy(), information.hasVaryStar, { } };
+    auto result = RecordInformation { information.key, information.insertionTime, information.identifier, information.updateResponseCounter, information.size, information.url.isolatedCopy(), information.hasVaryStar, { } };
     HashMap<String, String> varyHeaders;
     for (const auto& keyValue : information.varyHeaders)
         varyHeaders.set(keyValue.key.isolatedCopy(), keyValue.value.isolatedCopy());
@@ -175,7 +178,7 @@
         if (!storageRecord) {
             RunLoop::main().dispatch([caches = WTFMove(caches), callback = WTFMove(callback), traversalResult = isolatedCopy(WTFMove(traversalResult)) ]() mutable {
                 for (auto& key : traversalResult.failedRecords)
-                    caches->removeRecord(key);
+                    caches->removeCacheEntry(key);
 
                 auto* cache = caches->find(traversalResult.cacheIdentifier);
                 if (!cache) {
@@ -194,10 +197,10 @@
             return;
         }
 
-        auto& record = decoded->first;
-        auto insertionTime = decoded->second;
+        auto& record = decoded->record;
+        auto insertionTime = decoded->insertionTime;
 
-        RecordInformation recordInformation { storageRecord->key, insertionTime, 0, 0, record.request.url(), false, { } };
+        RecordInformation recordInformation { storageRecord->key, insertionTime, 0, 0, record.responseBodySize, record.request.url(), false, { } };
         updateVaryInformation(recordInformation, record.request, record.response);
 
         auto& sameURLRecords = traversalResult.records.ensure(computeKeyURL(recordInformation.url), [] { return Vector<RecordInformation> { }; }).iterator->value;
@@ -377,36 +380,67 @@
     Vector<uint64_t> m_recordIdentifiers;
 };
 
-void Cache::put(Vector<Record>&& records, RecordIdentifiersCallback&& callback)
+void Cache::storeRecords(Vector<Record>&& records, RecordIdentifiersCallback&& callback)
 {
-    ASSERT(m_state == State::Open);
-
     auto taskCounter = AsynchronousPutTaskCounter::create(WTFMove(callback));
 
     WebCore::CacheQueryOptions options;
     for (auto& record : records) {
         auto* sameURLRecords = recordsFromURL(record.request.url());
+        auto matchingRecords = queryCache(sameURLRecords, record.request, options);
 
-        auto matchingRecords = queryCache(sameURLRecords, record.request, options);
-        if (matchingRecords.isEmpty()) {
+        auto position = !matchingRecords.isEmpty() ? sameURLRecords->findMatching([&](const auto& item) { return item.identifier == matchingRecords[0]; }) : notFound;
+
+        if (position == notFound) {
             record.identifier = ++m_nextRecordIdentifier;
             taskCounter->addRecordIdentifier(record.identifier);
 
             auto& recordToWrite = addRecord(sameURLRecords, record);
-            writeRecordToDisk(recordToWrite, WTFMove(record), taskCounter.copyRef());
+            writeRecordToDisk(recordToWrite, WTFMove(record), taskCounter.copyRef(), 0);
         } 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->at(position);
-                taskCounter->addRecordIdentifier(identifier);
-                updateRecordToDisk(existingRecord, WTFMove(record), taskCounter.copyRef());
-            }
+            auto& existingRecord = sameURLRecords->at(position);
+            taskCounter->addRecordIdentifier(existingRecord.identifier);
+            updateRecordToDisk(existingRecord, WTFMove(record), taskCounter.copyRef());
         }
     }
 }
 
+void Cache::put(Vector<Record>&& records, RecordIdentifiersCallback&& callback)
+{
+    ASSERT(m_state == State::Open);
+
+    WebCore::CacheQueryOptions options;
+    uint64_t spaceRequired = 0;
+
+    for (auto& record : records) {
+        auto* sameURLRecords = recordsFromURL(record.request.url());
+        auto matchingRecords = queryCache(sameURLRecords, record.request, options);
+
+        auto position = (sameURLRecords && !matchingRecords.isEmpty()) ? sameURLRecords->findMatching([&](const auto& item) { return item.identifier == matchingRecords[0]; }) : notFound;
+
+        spaceRequired += record.responseBodySize;
+        if (position != notFound)
+            spaceRequired -= sameURLRecords->at(position).size;
+    }
+
+    if (m_caches.hasEnoughSpace(spaceRequired)) {
+        storeRecords(WTFMove(records), WTFMove(callback));
+        return;
+    }
+
+    m_caches.requestSpace(spaceRequired, [caches = makeRef(m_caches), identifier = m_identifier, records = WTFMove(records), callback = WTFMove(callback)](std::optional<DOMCacheEngine::Error>&& error) mutable {
+        if (error) {
+            callback(makeUnexpected(error.value()));
+            return;
+        }
+        auto* cache = caches->find(identifier);
+        if (!cache)
+            return;
+
+        cache->storeRecords(WTFMove(records), WTFMove(callback));
+    });
+}
+
 void Cache::remove(WebCore::ResourceRequest&& request, WebCore::CacheQueryOptions&& options, RecordIdentifiersCallback&& callback)
 {
     ASSERT(m_state == State::Open);
@@ -446,9 +480,9 @@
     }
 }
 
-void Cache::writeRecordToDisk(const RecordInformation& recordInformation, Record&& record, Ref<AsynchronousPutTaskCounter>&& taskCounter)
+void Cache::writeRecordToDisk(const RecordInformation& recordInformation, Record&& record, Ref<AsynchronousPutTaskCounter>&& taskCounter, uint64_t previousRecordSize)
 {
-    m_caches.writeRecord(*this, recordInformation, WTFMove(record), [taskCounter = WTFMove(taskCounter)](std::optional<Error>&& error) {
+    m_caches.writeRecord(*this, recordInformation, WTFMove(record), previousRecordSize, [taskCounter = WTFMove(taskCounter)](std::optional<Error>&& error) {
         if (error)
             taskCounter->setError(error.value());
     });
@@ -473,6 +507,8 @@
         if (position == notFound)
             return;
         auto& recordInfo = sameURLRecords->at(position);
+        auto previousSize = recordInfo.size;
+        recordInfo.size = record.responseBodySize;
 
         auto& recordFromDisk = result.value();
         record.requestHeadersGuard = recordFromDisk.requestHeadersGuard;
@@ -482,7 +518,7 @@
 
         updateVaryInformation(recordInfo, record.request, record.response);
 
-        cache->writeRecordToDisk(recordInfo, WTFMove(record), WTFMove(taskCounter));
+        cache->writeRecordToDisk(recordInfo, WTFMove(record), WTFMove(taskCounter), previousSize);
     });
 }
 
@@ -493,7 +529,7 @@
 
 void Cache::removeRecordFromDisk(const RecordInformation& record)
 {
-    m_caches.removeRecord(record.key);
+    m_caches.removeRecord(record);
 }
 
 Storage::Record Cache::encode(const RecordInformation& recordInformation, const Record& record)
@@ -500,6 +536,7 @@
 {
     WTF::Persistence::Encoder encoder;
     encoder << recordInformation.insertionTime;
+    encoder << recordInformation.size;
     encoder << record.requestHeadersGuard;
     record.request.encodeWithoutPlatformData(encoder);
     encoder << record.options;
@@ -507,6 +544,7 @@
 
     encoder << record.responseHeadersGuard;
     encoder << record.response;
+    encoder << record.responseBodySize;
 
     encoder.encodeChecksum();
 
@@ -522,7 +560,7 @@
     return { recordInformation.key, { }, header, body, { } };
 }
 
-static inline std::optional<std::pair<Record, double>> decodeRecordHeader(const Storage::Record& storage)
+std::optional<Cache::DecodedRecord> Cache::decodeRecordHeader(const Storage::Record& storage)
 {
     WTF::Persistence::Decoder decoder(storage.header.data(), storage.header.size());
 
@@ -532,6 +570,10 @@
     if (!decoder.decode(insertionTime))
         return std::nullopt;
 
+    uint64_t size;
+    if (!decoder.decode(size))
+        return std::nullopt;
+
     if (!decoder.decode(record.requestHeadersGuard))
         return std::nullopt;
 
@@ -550,10 +592,13 @@
     if (!decoder.decode(record.response))
         return std::nullopt;
 
+    if (!decoder.decode(record.responseBodySize))
+        return std::nullopt;
+
     if (!decoder.verifyChecksum())
         return std::nullopt;
 
-    return std::make_pair(WTFMove(record), insertionTime);
+    return DecodedRecord { insertionTime, size, WTFMove(record) };
 }
 
 std::optional<Record> Cache::decode(const Storage::Record& storage)
@@ -563,7 +608,7 @@
     if (!result)
         return std::nullopt;
 
-    auto record = WTFMove(result->first);
+    auto record = WTFMove(result->record);
     record.responseBody = WebCore::SharedBuffer::create(storage.body.data(), storage.body.size());
 
     return WTFMove(record);

Modified: trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCache.h (223072 => 223073)


--- trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCache.h	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCache.h	2017-10-09 22:17:05 UTC (rev 223073)
@@ -43,6 +43,8 @@
     uint64_t identifier { 0 };
     uint64_t updateResponseCounter { 0 };
 
+    uint64_t size { 0 };
+
     WebCore::URL url;
     bool hasVaryStar { false };
     HashMap<String, String> varyHeaders;
@@ -78,11 +80,26 @@
     static std::optional<WebCore::DOMCacheEngine::Record> decode(const NetworkCache::Storage::Record&);
     static NetworkCache::Storage::Record encode(const RecordInformation&, const WebCore::DOMCacheEngine::Record&);
 
+    struct DecodedRecord {
+        DecodedRecord(double insertionTime, uint64_t size, WebCore::DOMCacheEngine::Record&& record)
+            : insertionTime(insertionTime)
+            , size(size)
+            , record(WTFMove(record))
+        { }
+
+        double insertionTime { 0 };
+        uint64_t size { 0 };
+        WebCore::DOMCacheEngine::Record record;
+    };
+    static std::optional<DecodedRecord> decodeRecordHeader(const NetworkCache::Storage::Record&);
+
 private:
     Vector<RecordInformation>* recordsFromURL(const WebCore::URL&);
     const Vector<RecordInformation>* recordsFromURL(const WebCore::URL&) const;
     RecordInformation& addRecord(Vector<RecordInformation>*, const WebCore::DOMCacheEngine::Record&);
 
+    void storeRecords(Vector<WebCore::DOMCacheEngine::Record>&&, WebCore::DOMCacheEngine::RecordIdentifiersCallback&&);
+
     RecordInformation toRecordInformation(const WebCore::DOMCacheEngine::Record&);
 
     void finishOpening(WebCore::DOMCacheEngine::CompletionCallback&&, std::optional<WebCore::DOMCacheEngine::Error>&&);
@@ -89,7 +106,7 @@
     void retrieveRecord(const RecordInformation&, Ref<ReadRecordTaskCounter>&&);
 
     void readRecordsList(WebCore::DOMCacheEngine::CompletionCallback&&);
-    void writeRecordToDisk(const RecordInformation&, WebCore::DOMCacheEngine::Record&&, Ref<AsynchronousPutTaskCounter>&&);
+    void writeRecordToDisk(const RecordInformation&, WebCore::DOMCacheEngine::Record&&, Ref<AsynchronousPutTaskCounter>&&, uint64_t previousRecordSize);
     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>&&)>&&);

Modified: trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCaches.cpp (223072 => 223073)


--- trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCaches.cpp	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCaches.cpp	2017-10-09 22:17:05 UTC (rev 223073)
@@ -51,10 +51,11 @@
     return WebCore::pathByAppendingComponent(cachesRootPath, ASCIILiteral("cacheslist"));
 }
 
-Caches::Caches(Engine& engine, String&& origin)
+Caches::Caches(Engine& engine, String&& origin, uint64_t quota)
     : m_engine(&engine)
     , m_origin(WTFMove(origin))
     , m_rootPath(cachesRootPath(engine, m_origin))
+    , m_quota(quota)
 {
 }
 
@@ -84,7 +85,7 @@
     }
     m_storage = storage.releaseNonNull();
     m_storage->writeWithoutWaiting();
-    readCachesFromDisk([this, callback = WTFMove(callback)](Expected<Vector<Cache>, Error>&& result) {
+    readCachesFromDisk([this, callback = WTFMove(callback)](Expected<Vector<Cache>, Error>&& result) mutable {
         makeDirty();
 
         if (!result.hasValue()) {
@@ -96,12 +97,28 @@
             return;
         }
         m_caches = WTFMove(result.value());
-        m_isInitialized = true;
-        callback(std::nullopt);
+        initializeSize(WTFMove(callback));
+    });
+}
 
-        auto pendingCallbacks = WTFMove(m_pendingInitializationCallbacks);
-        for (auto& callback : pendingCallbacks)
+void Caches::initializeSize(WebCore::DOMCacheEngine::CompletionCallback&& callback)
+{
+    uint64_t size = 0;
+    m_storage->traverse({ }, 0, [protectedThis = makeRef(*this), this, protectedStorage = makeRef(*m_storage), callback = WTFMove(callback), size](const auto* storage, const auto& information) mutable {
+        if (!storage) {
+            m_size = size;
+            m_isInitialized = true;
             callback(std::nullopt);
+
+            auto pendingCallbacks = WTFMove(m_pendingInitializationCallbacks);
+            for (auto& callback : pendingCallbacks)
+                callback(std::nullopt);
+
+            return;
+        }
+        auto decoded = Cache::decodeRecordHeader(*storage);
+        if (decoded)
+            size += decoded->size;
     });
 }
 
@@ -299,11 +316,23 @@
     });
 }
 
-void Caches::writeRecord(const Cache& cache, const RecordInformation& recordInformation, Record&& record, CompletionCallback&& callback)
+void Caches::requestSpace(uint64_t spaceRequired, WebCore::DOMCacheEngine::CompletionCallback&& callback)
 {
+    // FIXME: Implement quota increase request.
+    ASSERT(m_quota < m_size + spaceRequired);
+    callback(Error::QuotaExceeded);
+}
+
+void Caches::writeRecord(const Cache& cache, const RecordInformation& recordInformation, Record&& record, uint64_t previousRecordSize, CompletionCallback&& callback)
+{
     ASSERT(m_isInitialized);
 
-    // FIXME: Check for storage quota.
+    ASSERT(m_size >= previousRecordSize);
+    m_size += recordInformation.size;
+    m_size -= previousRecordSize;
+
+    ASSERT(m_size <= m_quota);
+
     if (!shouldPersist()) {
         m_volatileStorage.set(recordInformation.key, WTFMove(record));
         return;
@@ -345,10 +374,19 @@
     });
 }
 
-void Caches::removeRecord(const NetworkCache::Key& key)
+void Caches::removeRecord(const RecordInformation& record)
 {
     ASSERT(m_isInitialized);
 
+    ASSERT(m_size >= record.size);
+    m_size -= record.size;
+    removeCacheEntry(record.key);
+}
+
+void Caches::removeCacheEntry(const NetworkCache::Key& key)
+{
+    ASSERT(m_isInitialized);
+
     if (!shouldPersist()) {
         m_volatileStorage.remove(key);
         return;

Modified: trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCaches.h (223072 => 223073)


--- trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCaches.h	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebKit/NetworkProcess/cache/CacheStorageEngineCaches.h	2017-10-09 22:17:05 UTC (rev 223073)
@@ -36,7 +36,7 @@
 
 class Caches : public RefCounted<Caches> {
 public:
-    static Ref<Caches> create(Engine& engine, String&& origin) { return adoptRef(*new Caches { engine, WTFMove(origin) }); }
+    static Ref<Caches> create(Engine& engine, String&& origin, uint64_t quota) { return adoptRef(*new Caches { engine, WTFMove(origin), quota }); }
 
     void initialize(WebCore::DOMCacheEngine::CompletionCallback&&);
     void open(const String& name, WebCore::DOMCacheEngine::CacheIdentifierCallback&&);
@@ -54,16 +54,22 @@
 
     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&);
 
+    bool hasEnoughSpace(uint64_t spaceRequired) const { return m_quota >= m_size + spaceRequired; }
+    void requestSpace(uint64_t spaceRequired, WebCore::DOMCacheEngine::CompletionCallback&&);
+    void writeRecord(const Cache&, const RecordInformation&, WebCore::DOMCacheEngine::Record&&, uint64_t previousRecordSize, WebCore::DOMCacheEngine::CompletionCallback&&);
+
+    void removeCacheEntry(const NetworkCache::Key&);
+    void removeRecord(const RecordInformation&);
+
     const NetworkCache::Salt& salt() const;
 
     bool shouldPersist() const { return !m_rootPath.isNull(); }
 
 private:
-    Caches(Engine&, String&& origin);
+    Caches(Engine&, String&& origin, uint64_t quota);
 
+    void initializeSize(WebCore::DOMCacheEngine::CompletionCallback&&);
     void readCachesFromDisk(WTF::Function<void(Expected<Vector<Cache>, WebCore::DOMCacheEngine::Error>&&)>&&);
     void writeCachesToDisk(WebCore::DOMCacheEngine::CompletionCallback&&);
 
@@ -77,6 +83,8 @@
     uint64_t m_updateCounter { 0 };
     String m_origin;
     String m_rootPath;
+    uint64_t m_quota { 0 };
+    uint64_t m_size { 0 };
     Vector<Cache> m_caches;
     Vector<Cache> m_removedCaches;
     RefPtr<NetworkCache::Storage> m_storage;

Modified: trunk/Source/WebKit/NetworkProcess/cache/NetworkCacheStorage.cpp (223072 => 223073)


--- trunk/Source/WebKit/NetworkProcess/cache/NetworkCacheStorage.cpp	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebKit/NetworkProcess/cache/NetworkCacheStorage.cpp	2017-10-09 22:17:05 UTC (rev 223073)
@@ -861,7 +861,7 @@
 
     ioQueue().dispatch([this, &traverseOperation] {
         traverseRecordsFiles(recordsPath(), traverseOperation.type, [this, &traverseOperation](const String& fileName, const String& hashString, const String& type, bool isBlob, const String& recordDirectoryPath) {
-            ASSERT(type == traverseOperation.type);
+            ASSERT(type == traverseOperation.type || traverseOperation.type.isEmpty());
             if (isBlob)
                 return;
 

Modified: trunk/Source/WebKit/NetworkProcess/cocoa/NetworkProcessCocoa.mm (223072 => 223073)


--- trunk/Source/WebKit/NetworkProcess/cocoa/NetworkProcessCocoa.mm	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebKit/NetworkProcess/cocoa/NetworkProcessCocoa.mm	2017-10-09 22:17:05 UTC (rev 223073)
@@ -107,6 +107,7 @@
 
     if (!parameters.cacheStorageDirectory.isNull()) {
         m_cacheStorageDirectory = parameters.cacheStorageDirectory;
+        m_cacheStoragePerOriginQuota = parameters.cacheStoragePerOriginQuota;
         SandboxExtension::consumePermanently(parameters.cacheStorageDirectoryExtensionHandle);
     }
 

Modified: trunk/Source/WebKit/Shared/WebCoreArgumentCoders.cpp (223072 => 223073)


--- trunk/Source/WebKit/Shared/WebCoreArgumentCoders.cpp	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebKit/Shared/WebCoreArgumentCoders.cpp	2017-10-09 22:17:05 UTC (rev 223073)
@@ -261,6 +261,7 @@
     encoder << record.responseHeadersGuard;
     encoder << record.response;
     encoder << record.updateResponseCounter;
+    encoder << record.responseBodySize;
 
     WTF::switchOn(record.responseBody, [&](const Ref<SharedBuffer>& buffer) {
         encoder << true;
@@ -309,6 +310,10 @@
     if (!decoder.decode(updateResponseCounter))
         return std::nullopt;
 
+    uint64_t responseBodySize;
+    if (!decoder.decode(responseBodySize))
+        return std::nullopt;
+
     WebCore::DOMCacheEngine::ResponseBody responseBody;
     bool hasSharedBufferBody;
     if (!decoder.decode(hasSharedBufferBody))
@@ -332,7 +337,7 @@
         }
     }
 
-    return {{ WTFMove(identifier), WTFMove(updateResponseCounter), WTFMove(requestHeadersGuard), WTFMove(request), WTFMove(options), WTFMove(referrer), WTFMove(responseHeadersGuard), WTFMove(response), WTFMove(responseBody) }};
+    return {{ WTFMove(identifier), WTFMove(updateResponseCounter), WTFMove(requestHeadersGuard), WTFMove(request), WTFMove(options), WTFMove(referrer), WTFMove(responseHeadersGuard), WTFMove(response), WTFMove(responseBody), responseBodySize }};
 }
 
 void ArgumentCoder<EventTrackingRegions>::encode(Encoder& encoder, const EventTrackingRegions& eventTrackingRegions)

Modified: trunk/Source/WebKit/UIProcess/API/APIProcessPoolConfiguration.cpp (223072 => 223073)


--- trunk/Source/WebKit/UIProcess/API/APIProcessPoolConfiguration.cpp	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebKit/UIProcess/API/APIProcessPoolConfiguration.cpp	2017-10-09 22:17:05 UTC (rev 223073)
@@ -111,7 +111,8 @@
     copy->m_applicationCacheDirectory = this->m_applicationCacheDirectory;
     copy->m_applicationCacheFlatFileSubdirectoryName = this->m_applicationCacheFlatFileSubdirectoryName;
     copy->m_cacheStorageDirectory = this->m_cacheStorageDirectory;
-    copy->m_diskCacheDirectory = this->m_diskCacheDirectory;
+    copy->m_cacheStorageDirectory = this->m_cacheStorageDirectory;
+    copy->m_cacheStoragePerOriginQuota = this->m_cacheStoragePerOriginQuota;
     copy->m_mediaCacheDirectory = this->m_mediaCacheDirectory;
     copy->m_indexedDBDatabaseDirectory = this->m_indexedDBDatabaseDirectory;
     copy->m_injectedBundlePath = this->m_injectedBundlePath;

Modified: trunk/Source/WebKit/UIProcess/API/APIProcessPoolConfiguration.h (223072 => 223073)


--- trunk/Source/WebKit/UIProcess/API/APIProcessPoolConfiguration.h	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebKit/UIProcess/API/APIProcessPoolConfiguration.h	2017-10-09 22:17:05 UTC (rev 223073)
@@ -69,6 +69,7 @@
 
     const WTF::String& cacheStorageDirectory() const { return m_cacheStorageDirectory; }
     void setCacheStorageDirectory(WTF::String&& cacheStorageDirectory) { m_cacheStorageDirectory = WTFMove(cacheStorageDirectory); }
+    uint64_t cacheStoragePerOriginQuota() const { return m_cacheStoragePerOriginQuota; }
 
     const WTF::String& diskCacheDirectory() const { return m_diskCacheDirectory; }
     void setDiskCacheDirectory(const WTF::String& diskCacheDirectory) { m_diskCacheDirectory = diskCacheDirectory; }
@@ -152,6 +153,7 @@
     WTF::String m_applicationCacheDirectory;
     WTF::String m_applicationCacheFlatFileSubdirectoryName;
     WTF::String m_cacheStorageDirectory;
+    uint64_t m_cacheStoragePerOriginQuota { 400 * 1024 };
     WTF::String m_diskCacheDirectory;
     WTF::String m_mediaCacheDirectory;
     WTF::String m_indexedDBDatabaseDirectory;

Modified: trunk/Source/WebKit/UIProcess/WebProcessPool.cpp (223072 => 223073)


--- trunk/Source/WebKit/UIProcess/WebProcessPool.cpp	2017-10-09 21:57:52 UTC (rev 223072)
+++ trunk/Source/WebKit/UIProcess/WebProcessPool.cpp	2017-10-09 22:17:05 UTC (rev 223073)
@@ -434,6 +434,7 @@
         parameters.urlSchemesRegisteredForCustomProtocols.append(scheme);
 
     parameters.cacheStorageDirectory = m_configuration->cacheStorageDirectory();
+    parameters.cacheStoragePerOriginQuota = m_configuration->cacheStoragePerOriginQuota();
     if (!parameters.cacheStorageDirectory.isEmpty())
         SandboxExtension::createHandleForReadWriteDirectory(parameters.cacheStorageDirectory, parameters.cacheStorageDirectoryExtensionHandle);
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to