[Fetch API] Support Request cache mode
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 11 Oct 2016 10:53:22 +0000 (10:53 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 11 Oct 2016 10:53:22 +0000 (10:53 +0000)
https://bugs.webkit.org/show_bug.cgi?id=162281

Patch by Youenn Fablet <youenn@apple.com> on 2016-10-11
Reviewed by Alex Christensen.

LayoutTests/imported/w3c:

* web-platform-tests/fetch/api/request/request-cache-expected.txt: Rebasing test now that more tests are passing.
* web-platform-tests/fetch/api/request/request-error-expected.txt:
* web-platform-tests/fetch/api/request/request-error.html: Adding test to ensure only-if-cached is used with same-origin fetch mode.

Source/WebCore:

Covered by updated test.

Added support for only-if-cached mode at Request level.

Added support for cache mode at CachedResourceLoader, by setting HTTP headers and ResourceRequest cache policy
based on https://fetch.spec.whatwg.org/#concept-request-cache-mode and https://fetch.spec.whatwg.org/#http-network-or-cache-fetch.

Disabled default cache policy computation (done in FrameLoader) when cache mode is different from the default.
Activated no-store cache mode for EventSource as per https://html.spec.whatwg.org/#the-eventsource-interface.

* Modules/fetch/FetchRequest.cpp:
(WebCore::setCache): Adding support for only-if-cached.
(WebCore::buildOptions): Throw if only-if-cached and fetch mode is not same-origin.
* loader/FetchOptions.h: Adding support for only-if-cached.
* loader/FrameLoader.cpp:
(WebCore::FrameLoader::defaultRequestCachingPolicy): Introduced to ease readability.
(WebCore::FrameLoader::addExtraFieldsToRequest): Updating cache policy only if request has the default cache policy.
This allows bypassing the default behavior for fetch cache mode different from "default".
* loader/FrameLoader.h:
* loader/cache/CachedResourceLoader.cpp:
(WebCore::updateRequestAccordingCacheMode): Introduced to set headers and request cache policy according fetch cache mode.
(WebCore::CachedResourceLoader::requestResource):
(WebCore::CachedResourceLoader::loadResource):
(WebCore::CachedResourceLoader::determineRevalidationPolicy): Ensure bypassing the memory cache in no-store and reload cases.
We reload in case of cache mode=reload to refresh the meory cache entry.
* loader/cache/CachedResourceRequest.h:
(WebCore::CachedResourceRequest::setCacheModeToNoStore):
* page/EventSource.cpp:
(WebCore::EventSource::connect): Use no-store cache mode as per https://html.spec.whatwg.org/#the-eventsource-interface.
* platform/network/HTTPHeaderMap.cpp:
(WebCore::HTTPHeaderMap::addIfNotPresent): Helper routine.
* platform/network/HTTPHeaderMap.h:
* platform/network/HTTPHeaderValues.cpp:
* platform/network/HTTPHeaderValues.h:
* platform/network/ResourceRequestBase.cpp:
(WebCore::ResourceRequestBase::addHTTPHeaderFieldIfNotPresent):
(WebCore::ResourceRequestBase::addHTTPHeaderField):
(WebCore::ResourceRequestBase::hasHTTPHeaderField):
* platform/network/ResourceRequestBase.h:

LayoutTests:

Activating request-cache.html tests for WK1, but not yet for WK2.

* TestExpectations:
* platform/mac/TestExpectations:
* platform/wk2/TestExpectations:

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

23 files changed:
LayoutTests/ChangeLog
LayoutTests/TestExpectations
LayoutTests/imported/w3c/ChangeLog
LayoutTests/imported/w3c/web-platform-tests/fetch/api/request/request-cache-expected.txt
LayoutTests/imported/w3c/web-platform-tests/fetch/api/request/request-error-expected.txt
LayoutTests/imported/w3c/web-platform-tests/fetch/api/request/request-error.html
LayoutTests/platform/mac/TestExpectations
LayoutTests/platform/wk2/TestExpectations
Source/WebCore/ChangeLog
Source/WebCore/Modules/fetch/FetchRequest.cpp
Source/WebCore/loader/FetchOptions.h
Source/WebCore/loader/FrameLoader.cpp
Source/WebCore/loader/FrameLoader.h
Source/WebCore/loader/cache/CachedResourceLoader.cpp
Source/WebCore/loader/cache/CachedResourceLoader.h
Source/WebCore/loader/cache/CachedResourceRequest.h
Source/WebCore/page/EventSource.cpp
Source/WebCore/platform/network/HTTPHeaderMap.cpp
Source/WebCore/platform/network/HTTPHeaderMap.h
Source/WebCore/platform/network/HTTPHeaderValues.cpp
Source/WebCore/platform/network/HTTPHeaderValues.h
Source/WebCore/platform/network/ResourceRequestBase.cpp
Source/WebCore/platform/network/ResourceRequestBase.h

index 57123aa..8cf6d11 100644 (file)
@@ -1,3 +1,16 @@
+2016-10-11  Youenn Fablet  <youenn@apple.com>
+
+        [Fetch API] Support Request cache mode
+        https://bugs.webkit.org/show_bug.cgi?id=162281
+
+        Reviewed by Alex Christensen.
+
+        Activating request-cache.html tests for WK1, but not yet for WK2.
+
+        * TestExpectations:
+        * platform/mac/TestExpectations:
+        * platform/wk2/TestExpectations:
+
 2016-10-11  Chris Dumez  <cdumez@apple.com>
 
         Update IDBVersionChangeEvent to stop using legacy [ConstructorTemplate=Event]
index 5cb82a1..0888272 100644 (file)
@@ -282,8 +282,6 @@ imported/w3c/web-platform-tests/XMLHttpRequest/send-redirect-to-non-cors.htm [ S
 # Failing assertion with dynamic message
 imported/w3c/web-platform-tests/XMLHttpRequest/responsexml-document-properties.htm [ Failure ]
 
-imported/w3c/web-platform-tests/fetch/api/request/request-cache.html [ Skip ]
-
 webkit.org/b/161176 [ Debug ] imported/w3c/web-platform-tests/url/url-setters.html [ Skip ]
 
 webkit.org/b/157068 imported/w3c/web-platform-tests/fetch/nosniff/importscripts.html [ Skip ]
index 4be11b9..8b5a953 100644 (file)
@@ -1,3 +1,14 @@
+2016-10-11  Youenn Fablet  <youenn@apple.com>
+
+        [Fetch API] Support Request cache mode
+        https://bugs.webkit.org/show_bug.cgi?id=162281
+
+        Reviewed by Alex Christensen.
+
+        * web-platform-tests/fetch/api/request/request-cache-expected.txt: Rebasing test now that more tests are passing.
+        * web-platform-tests/fetch/api/request/request-error-expected.txt:
+        * web-platform-tests/fetch/api/request/request-error.html: Adding test to ensure only-if-cached is used with same-origin fetch mode.
+
 2016-10-10  Chris Dumez  <cdumez@apple.com>
 
         Add support for languagechange event
index ec1501a..5529cb0 100644 (file)
@@ -5,10 +5,10 @@ PASS RequestCache "default" mode checks the cache for previously cached content
 PASS RequestCache "default" mode checks the cache for previously cached content and avoids going to the network if a fresh response exists with date and fresh response 
 PASS RequestCache "no-cache" mode revalidates stale responses found in the cache with Etag and stale response 
 PASS RequestCache "no-cache" mode revalidates stale responses found in the cache with date and stale response 
-FAIL RequestCache "no-cache" mode revalidates fresh responses found in the cache with Etag and fresh response assert_equals: expected 2 but got 1
-FAIL RequestCache "no-cache" mode revalidates fresh responses found in the cache with date and fresh response assert_equals: expected 2 but got 1
-FAIL RequestCache "force-cache" mode checks the cache for previously cached content and avoid revalidation for stale responses with Etag and stale response assert_equals: expected 1 but got 2
-FAIL RequestCache "force-cache" mode checks the cache for previously cached content and avoid revalidation for stale responses with date and stale response assert_equals: expected 1 but got 2
+PASS RequestCache "no-cache" mode revalidates fresh responses found in the cache with Etag and fresh response 
+PASS RequestCache "no-cache" mode revalidates fresh responses found in the cache with date and fresh response 
+PASS RequestCache "force-cache" mode checks the cache for previously cached content and avoid revalidation for stale responses with Etag and stale response 
+PASS RequestCache "force-cache" mode checks the cache for previously cached content and avoid revalidation for stale responses with date and stale response 
 PASS RequestCache "force-cache" mode checks the cache for previously cached content and avoid revalidation for fresh responses with Etag and fresh response 
 PASS RequestCache "force-cache" mode checks the cache for previously cached content and avoid revalidation for fresh responses with date and fresh response 
 PASS RequestCache "force-cache" mode checks the cache for previously cached content and goes to the network if a cached response is not found with Etag and stale response 
@@ -23,10 +23,10 @@ PASS RequestCache "force-cache" stores the response in the cache if it goes to t
 PASS RequestCache "force-cache" stores the response in the cache if it goes to the network with date and stale response 
 PASS RequestCache "force-cache" stores the response in the cache if it goes to the network with Etag and fresh response 
 PASS RequestCache "force-cache" stores the response in the cache if it goes to the network with date and fresh response 
-FAIL RequestCache "only-if-cached" mode checks the cache for previously cached content and avoids revalidation for stale responses with Etag and stale response promise_test: Unhandled rejection with value: object "TypeError: Type error"
-FAIL RequestCache "only-if-cached" mode checks the cache for previously cached content and avoids revalidation for stale responses with date and stale response promise_test: Unhandled rejection with value: object "TypeError: Type error"
-FAIL RequestCache "only-if-cached" mode checks the cache for previously cached content and avoids revalidation for fresh responses with Etag and fresh response promise_test: Unhandled rejection with value: object "TypeError: Type error"
-FAIL RequestCache "only-if-cached" mode checks the cache for previously cached content and avoids revalidation for fresh responses with date and fresh response promise_test: Unhandled rejection with value: object "TypeError: Type error"
+PASS RequestCache "only-if-cached" mode checks the cache for previously cached content and avoids revalidation for stale responses with Etag and stale response 
+PASS RequestCache "only-if-cached" mode checks the cache for previously cached content and avoids revalidation for stale responses with date and stale response 
+PASS RequestCache "only-if-cached" mode checks the cache for previously cached content and avoids revalidation for fresh responses with Etag and fresh response 
+PASS RequestCache "only-if-cached" mode checks the cache for previously cached content and avoids revalidation for fresh responses with date and fresh response 
 PASS RequestCache "only-if-cached" mode checks the cache for previously cached content and does not go to the network if a cached response is not found with Etag and fresh response 
 PASS RequestCache "only-if-cached" mode checks the cache for previously cached content and does not go to the network if a cached response is not found with date and fresh response 
 FAIL RequestCache "only-if-cached" (with "same-origin") uses cached same-origin redirects to same-origin content with Etag and fresh response promise_test: Unhandled rejection with value: object "TypeError: Type error"
@@ -37,21 +37,21 @@ PASS RequestCache "only-if-cached" (with "same-origin") does not follow redirect
 PASS RequestCache "only-if-cached" (with "same-origin") does not follow redirects across origins and rejects with date and fresh response 
 PASS RequestCache "only-if-cached" (with "same-origin") does not follow redirects across origins and rejects with Etag and stale response 
 PASS RequestCache "only-if-cached" (with "same-origin") does not follow redirects across origins and rejects with date and stale response 
-FAIL RequestCache "no-store" mode does not check the cache for previously cached content and goes to the network regardless with Etag and stale response assert_equals: expected (undefined) undefined but got (string) "\"0.04196530864700354\""
-FAIL RequestCache "no-store" mode does not check the cache for previously cached content and goes to the network regardless with date and stale response assert_equals: expected (undefined) undefined but got (string) "Fri, 09 Sep 2016 08:49:10 GMT"
-FAIL RequestCache "no-store" mode does not check the cache for previously cached content and goes to the network regardless with Etag and fresh response assert_equals: expected 2 but got 1
-FAIL RequestCache "no-store" mode does not check the cache for previously cached content and goes to the network regardless with date and fresh response assert_equals: expected 2 but got 1
-FAIL RequestCache "no-store" mode does not store the response in the cache with Etag and stale response assert_equals: expected (undefined) undefined but got (string) "\"0.12846053184726147\""
-FAIL RequestCache "no-store" mode does not store the response in the cache with date and stale response assert_equals: expected (undefined) undefined but got (string) "Fri, 09 Sep 2016 08:49:10 GMT"
-FAIL RequestCache "no-store" mode does not store the response in the cache with Etag and fresh response assert_equals: expected 2 but got 1
-FAIL RequestCache "no-store" mode does not store the response in the cache with date and fresh response assert_equals: expected 2 but got 1
+PASS RequestCache "no-store" mode does not check the cache for previously cached content and goes to the network regardless with Etag and stale response 
+PASS RequestCache "no-store" mode does not check the cache for previously cached content and goes to the network regardless with date and stale response 
+PASS RequestCache "no-store" mode does not check the cache for previously cached content and goes to the network regardless with Etag and fresh response 
+PASS RequestCache "no-store" mode does not check the cache for previously cached content and goes to the network regardless with date and fresh response 
+PASS RequestCache "no-store" mode does not store the response in the cache with Etag and stale response 
+PASS RequestCache "no-store" mode does not store the response in the cache with date and stale response 
+PASS RequestCache "no-store" mode does not store the response in the cache with Etag and fresh response 
+PASS RequestCache "no-store" mode does not store the response in the cache with date and fresh response 
 PASS RequestCache "default" mode with an If-Modified-Since header is treated similarly to "no-store" with Etag and stale response 
 PASS RequestCache "default" mode with an If-Modified-Since header is treated similarly to "no-store" with date and stale response 
 PASS RequestCache "default" mode with an If-Modified-Since header is treated similarly to "no-store" with Etag and fresh response 
 PASS RequestCache "default" mode with an If-Modified-Since header is treated similarly to "no-store" with date and fresh response 
 PASS RequestCache "default" mode with an If-Modified-Since header is treated similarly to "no-store" with Etag and stale response 
 PASS RequestCache "default" mode with an If-Modified-Since header is treated similarly to "no-store" with date and stale response 
-FAIL RequestCache "default" mode with an If-Modified-Since header is treated similarly to "no-store" with Etag and fresh response assert_equals: expected 2 but got 1
+PASS RequestCache "default" mode with an If-Modified-Since header is treated similarly to "no-store" with Etag and fresh response 
 PASS RequestCache "default" mode with an If-Modified-Since header is treated similarly to "no-store" with date and fresh response 
 PASS RequestCache "default" mode with an If-None-Match header is treated similarly to "no-store" with Etag and stale response 
 PASS RequestCache "default" mode with an If-None-Match header is treated similarly to "no-store" with date and stale response 
@@ -59,46 +59,46 @@ PASS RequestCache "default" mode with an If-None-Match header is treated similar
 PASS RequestCache "default" mode with an If-None-Match header is treated similarly to "no-store" with date and fresh response 
 PASS RequestCache "default" mode with an If-None-Match header is treated similarly to "no-store" with Etag and stale response 
 PASS RequestCache "default" mode with an If-None-Match header is treated similarly to "no-store" with date and stale response 
-FAIL RequestCache "default" mode with an If-None-Match header is treated similarly to "no-store" with Etag and fresh response assert_equals: expected 2 but got 1
-FAIL RequestCache "default" mode with an If-None-Match header is treated similarly to "no-store" with date and fresh response assert_equals: expected 2 but got 1
+PASS RequestCache "default" mode with an If-None-Match header is treated similarly to "no-store" with Etag and fresh response 
+PASS RequestCache "default" mode with an If-None-Match header is treated similarly to "no-store" with date and fresh response 
 PASS RequestCache "default" mode with an If-Unmodified-Since header is treated similarly to "no-store" with Etag and stale response 
 PASS RequestCache "default" mode with an If-Unmodified-Since header is treated similarly to "no-store" with date and stale response 
 PASS RequestCache "default" mode with an If-Unmodified-Since header is treated similarly to "no-store" with Etag and fresh response 
 PASS RequestCache "default" mode with an If-Unmodified-Since header is treated similarly to "no-store" with date and fresh response 
 PASS RequestCache "default" mode with an If-Unmodified-Since header is treated similarly to "no-store" with Etag and stale response 
 PASS RequestCache "default" mode with an If-Unmodified-Since header is treated similarly to "no-store" with date and stale response 
-FAIL RequestCache "default" mode with an If-Unmodified-Since header is treated similarly to "no-store" with Etag and fresh response assert_equals: expected 2 but got 1
-FAIL RequestCache "default" mode with an If-Unmodified-Since header is treated similarly to "no-store" with date and fresh response assert_equals: expected 2 but got 1
+PASS RequestCache "default" mode with an If-Unmodified-Since header is treated similarly to "no-store" with Etag and fresh response 
+PASS RequestCache "default" mode with an If-Unmodified-Since header is treated similarly to "no-store" with date and fresh response 
 PASS RequestCache "default" mode with an If-Match header is treated similarly to "no-store" with Etag and stale response 
 PASS RequestCache "default" mode with an If-Match header is treated similarly to "no-store" with date and stale response 
 PASS RequestCache "default" mode with an If-Match header is treated similarly to "no-store" with Etag and fresh response 
 PASS RequestCache "default" mode with an If-Match header is treated similarly to "no-store" with date and fresh response 
 PASS RequestCache "default" mode with an If-Match header is treated similarly to "no-store" with Etag and stale response 
 PASS RequestCache "default" mode with an If-Match header is treated similarly to "no-store" with date and stale response 
-FAIL RequestCache "default" mode with an If-Match header is treated similarly to "no-store" with Etag and fresh response assert_equals: expected 2 but got 1
-FAIL RequestCache "default" mode with an If-Match header is treated similarly to "no-store" with date and fresh response assert_equals: expected 2 but got 1
+PASS RequestCache "default" mode with an If-Match header is treated similarly to "no-store" with Etag and fresh response 
+PASS RequestCache "default" mode with an If-Match header is treated similarly to "no-store" with date and fresh response 
 PASS RequestCache "default" mode with an If-Range header is treated similarly to "no-store" with Etag and stale response 
 PASS RequestCache "default" mode with an If-Range header is treated similarly to "no-store" with date and stale response 
 PASS RequestCache "default" mode with an If-Range header is treated similarly to "no-store" with Etag and fresh response 
 PASS RequestCache "default" mode with an If-Range header is treated similarly to "no-store" with date and fresh response 
 PASS RequestCache "default" mode with an If-Range header is treated similarly to "no-store" with Etag and stale response 
 PASS RequestCache "default" mode with an If-Range header is treated similarly to "no-store" with date and stale response 
-FAIL RequestCache "default" mode with an If-Range header is treated similarly to "no-store" with Etag and fresh response assert_equals: expected 2 but got 1
-FAIL RequestCache "default" mode with an If-Range header is treated similarly to "no-store" with date and fresh response assert_equals: expected 2 but got 1
+PASS RequestCache "default" mode with an If-Range header is treated similarly to "no-store" with Etag and fresh response 
+PASS RequestCache "default" mode with an If-Range header is treated similarly to "no-store" with date and fresh response 
 PASS Responses with the "Cache-Control: no-store" header are not stored in the cache with Etag and stale response 
 PASS Responses with the "Cache-Control: no-store" header are not stored in the cache with date and stale response 
 PASS Responses with the "Cache-Control: no-store" header are not stored in the cache with Etag and fresh response 
 PASS Responses with the "Cache-Control: no-store" header are not stored in the cache with date and fresh response 
-FAIL RequestCache "reload" mode does not check the cache for previously cached content and goes to the network regardless with Etag and stale response assert_equals: expected (undefined) undefined but got (string) "\"0.7894894845056666\""
-FAIL RequestCache "reload" mode does not check the cache for previously cached content and goes to the network regardless with date and stale response assert_equals: expected (undefined) undefined but got (string) "Fri, 09 Sep 2016 08:49:10 GMT"
-FAIL RequestCache "reload" mode does not check the cache for previously cached content and goes to the network regardless with Etag and fresh response assert_equals: expected 2 but got 1
-FAIL RequestCache "reload" mode does not check the cache for previously cached content and goes to the network regardless with date and fresh response assert_equals: expected 2 but got 1
+PASS RequestCache "reload" mode does not check the cache for previously cached content and goes to the network regardless with Etag and stale response 
+PASS RequestCache "reload" mode does not check the cache for previously cached content and goes to the network regardless with date and stale response 
+PASS RequestCache "reload" mode does not check the cache for previously cached content and goes to the network regardless with Etag and fresh response 
+PASS RequestCache "reload" mode does not check the cache for previously cached content and goes to the network regardless with date and fresh response 
 PASS RequestCache "reload" mode does store the response in the cache with Etag and stale response 
 PASS RequestCache "reload" mode does store the response in the cache with date and stale response 
-PASS RequestCache "reload" mode does store the response in the cache with Etag and fresh response 
-PASS RequestCache "reload" mode does store the response in the cache with date and fresh response 
-FAIL RequestCache "reload" mode does store the response in the cache even if a previous response is already stored with Etag and stale response assert_equals: expected (undefined) undefined but got (string) "\"0.49509960167414313\""
-FAIL RequestCache "reload" mode does store the response in the cache even if a previous response is already stored with date and stale response assert_equals: expected (undefined) undefined but got (string) "Fri, 09 Sep 2016 08:49:10 GMT"
-FAIL RequestCache "reload" mode does store the response in the cache even if a previous response is already stored with Etag and fresh response assert_equals: expected 2 but got 1
-FAIL RequestCache "reload" mode does store the response in the cache even if a previous response is already stored with date and fresh response assert_equals: expected 2 but got 1
+FAIL RequestCache "reload" mode does store the response in the cache with Etag and fresh response assert_equals: expected 1 but got 2
+FAIL RequestCache "reload" mode does store the response in the cache with date and fresh response assert_equals: expected 1 but got 2
+PASS RequestCache "reload" mode does store the response in the cache even if a previous response is already stored with Etag and stale response 
+PASS RequestCache "reload" mode does store the response in the cache even if a previous response is already stored with date and stale response 
+PASS RequestCache "reload" mode does store the response in the cache even if a previous response is already stored with Etag and fresh response 
+PASS RequestCache "reload" mode does store the response in the cache even if a previous response is already stored with date and fresh response 
 
index a9e8e37..e80fba8 100644 (file)
@@ -19,4 +19,8 @@ PASS Bad mode init parameter value
 PASS Bad credentials init parameter value 
 PASS Bad cache init parameter value 
 PASS Bad redirect init parameter value 
+PASS Request with cache mode: only-if-cached and fetch mode: same-origin 
+PASS Request with cache mode: only-if-cached and fetch mode: cors 
+PASS Request with cache mode: only-if-cached and fetch mode: no-cors 
+PASS Request should not get its content-type from the init request if init headers are provided 
 
index c87429e..279aeda 100644 (file)
           assert_throws(new TypeError(), function() { new Request("", options); });
         },"Bad " + parameter +" init parameter value");
       });
+
+      function testOnlyIfCachedMode(fetchMode, ok) {
+        test(function() {
+          var options = {"cache": "only-if-cached", "mode": fetchMode};
+          if (ok)
+            new Request("test", options);
+          else
+            assert_throws(new TypeError(), function() { new Request("test", options); });
+        }, "Request with cache mode: only-if-cached and fetch mode: " + fetchMode);
+      }
+      testOnlyIfCachedMode("same-origin", true);
+      testOnlyIfCachedMode("cors", false);
+      testOnlyIfCachedMode("no-cors", false);
+
+      test(function() {
+        var initialHeaders = new Headers([["Content-Type", "potato"]]);
+        var initialRequest = new Request("", {"headers" : initialHeaders});
+        var headers = new Headers([]);
+        var request = new Request(initialRequest, {"headers" : headers});
+        assert_false(request.headers.has("Content-Type"));
+      }, "Request should not get its content-type from the init request if init headers are provided");
+
     </script>
   </body>
 </html>
index 8102e6c..ecb0df6 100644 (file)
@@ -1403,8 +1403,6 @@ webkit.org/b/158747 media/restore-from-page-cache.html [ Pass Failure ]
 # See also https://bugs.webkit.org/show_bug.cgi?id=162494
 webkit.org/b/151287 [ ElCapitan+ ] media/controls/inline-elements-dropoff-order.html [ Failure ]
 
-webkit.org/b/159683 imported/w3c/web-platform-tests/fetch/api/request/request-cache.html [ Pass Failure ]
-
 webkit.org/b/158500 storage/indexeddb/database-close-private.html [ Pass Failure ]
 
 webkit.org/b/163122 imported/blink/storage/indexeddb/blob-valid-after-deletion.html [ Pass Timeout ]
index 9c29d4d..17569dd 100644 (file)
@@ -684,6 +684,9 @@ fast/images/gif-loop-count.html [ Pass ]
 # DumpRenderTree does not implement setWillSendRequestHTTPBody
 http/tests/misc/will-send-request-with-client-provided-http-body.html [ Pass ]
 
+# bug 162281
+imported/w3c/web-platform-tests/fetch/api/request/request-cache.html [ Skip ]
+
 ### END OF (5) Progressions, expected successes that are expected failures in WebKit1.
 ########################################
 
index 47c8402..c9a8491 100644 (file)
@@ -1,3 +1,50 @@
+2016-10-11  Youenn Fablet  <youenn@apple.com>
+
+        [Fetch API] Support Request cache mode
+        https://bugs.webkit.org/show_bug.cgi?id=162281
+
+        Reviewed by Alex Christensen.
+
+        Covered by updated test.
+
+        Added support for only-if-cached mode at Request level.
+
+        Added support for cache mode at CachedResourceLoader, by setting HTTP headers and ResourceRequest cache policy
+        based on https://fetch.spec.whatwg.org/#concept-request-cache-mode and https://fetch.spec.whatwg.org/#http-network-or-cache-fetch.
+
+        Disabled default cache policy computation (done in FrameLoader) when cache mode is different from the default.
+        Activated no-store cache mode for EventSource as per https://html.spec.whatwg.org/#the-eventsource-interface.
+
+        * Modules/fetch/FetchRequest.cpp:
+        (WebCore::setCache): Adding support for only-if-cached.
+        (WebCore::buildOptions): Throw if only-if-cached and fetch mode is not same-origin.
+        * loader/FetchOptions.h: Adding support for only-if-cached.
+        * loader/FrameLoader.cpp:
+        (WebCore::FrameLoader::defaultRequestCachingPolicy): Introduced to ease readability.
+        (WebCore::FrameLoader::addExtraFieldsToRequest): Updating cache policy only if request has the default cache policy.
+        This allows bypassing the default behavior for fetch cache mode different from "default".
+        * loader/FrameLoader.h:
+        * loader/cache/CachedResourceLoader.cpp:
+        (WebCore::updateRequestAccordingCacheMode): Introduced to set headers and request cache policy according fetch cache mode.
+        (WebCore::CachedResourceLoader::requestResource):
+        (WebCore::CachedResourceLoader::loadResource):
+        (WebCore::CachedResourceLoader::determineRevalidationPolicy): Ensure bypassing the memory cache in no-store and reload cases.
+        We reload in case of cache mode=reload to refresh the meory cache entry.
+        * loader/cache/CachedResourceRequest.h:
+        (WebCore::CachedResourceRequest::setCacheModeToNoStore):
+        * page/EventSource.cpp:
+        (WebCore::EventSource::connect): Use no-store cache mode as per https://html.spec.whatwg.org/#the-eventsource-interface.
+        * platform/network/HTTPHeaderMap.cpp:
+        (WebCore::HTTPHeaderMap::addIfNotPresent): Helper routine.
+        * platform/network/HTTPHeaderMap.h:
+        * platform/network/HTTPHeaderValues.cpp:
+        * platform/network/HTTPHeaderValues.h:
+        * platform/network/ResourceRequestBase.cpp:
+        (WebCore::ResourceRequestBase::addHTTPHeaderFieldIfNotPresent):
+        (WebCore::ResourceRequestBase::addHTTPHeaderField):
+        (WebCore::ResourceRequestBase::hasHTTPHeaderField):
+        * platform/network/ResourceRequestBase.h:
+
 2016-10-10  Antti Koivisto  <antti@apple.com>
 
         Stop copying author shadow pseudo rules into shadow tree style resolver
index 60e3e7b..9afccb8 100644 (file)
@@ -98,6 +98,8 @@ static Optional<Exception> setCache(FetchOptions& options, const String& cache)
         options.cache = FetchOptions::Cache::NoCache;
     else if (cache == "force-cache")
         options.cache = FetchOptions::Cache::ForceCache;
+    else if (cache == "only-if-cached")
+        options.cache = FetchOptions::Cache::OnlyIfCached;
     else
         return Exception { TypeError, ASCIILiteral("Bad cache mode value.") };
     return Nullopt;
@@ -195,6 +197,9 @@ static Optional<Exception> buildOptions(FetchRequest::InternalRequest& request,
             return exception;
     }
 
+    if (request.options.cache == FetchOptions::Cache::OnlyIfCached && request.options.mode != FetchOptions::Mode::SameOrigin)
+        return Exception { TypeError, ASCIILiteral("only-if-cached cache option requires fetch mode to be same-origin.")  };
+
     if (init.get("redirect", value)) {
         exception = setRedirect(request.options, value);
         if (exception)
index 499c81b..b733461 100644 (file)
@@ -43,7 +43,7 @@ struct FetchOptions {
     enum class Credentials { Omit, SameOrigin, Include };
     Credentials credentials { Credentials::Omit };
 
-    enum class Cache { Default, NoStore, Reload, NoCache, ForceCache };
+    enum class Cache { Default, NoStore, Reload, NoCache, ForceCache, OnlyIfCached };
     Cache cache { Cache::Default };
 
     enum class Redirect { Follow, Error, Manual };
index 81ed14b..20a82ad 100644 (file)
@@ -2562,36 +2562,14 @@ void FrameLoader::addExtraFieldsToMainResourceRequest(ResourceRequest& request)
     addHTTPUpgradeInsecureRequestsIfNeeded(request);
 }
 
-void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, FrameLoadType loadType, bool mainResource)
+ResourceRequestCachePolicy FrameLoader::defaultRequestCachingPolicy(const ResourceRequest& request, FrameLoadType loadType, bool isMainResource)
 {
-    Page* page = frame().page();
-    bool cachingDisabled = page && page->isResourceCachingDisabled();
-
-    if (cachingDisabled)
-        request.setCachePolicy(ReloadIgnoringCacheData);
-
-    // Don't set the cookie policy URL if it's already been set.
-    // But make sure to set it on all requests regardless of protocol, as it has significance beyond the cookie policy (<rdar://problem/6616664>).
-    if (request.firstPartyForCookies().isEmpty()) {
-        if (mainResource && m_frame.isMainFrame())
-            request.setFirstPartyForCookies(request.url());
-        else if (Document* document = m_frame.document())
-            request.setFirstPartyForCookies(document->firstPartyForCookies());
-    }
-
-    // The remaining modifications are only necessary for HTTP and HTTPS.
-    if (!request.url().isEmpty() && !request.url().protocolIsInHTTPFamily())
-        return;
-
-    applyUserAgent(request);
-
-    if (cachingDisabled) {
-        // Cache policy was already set above in the non-HTTP-specific code.
-        loadType = FrameLoadType::ReloadFromOrigin;
-    } else if (!mainResource) {
+    if (m_overrideCachePolicyForTesting)
+        return m_overrideCachePolicyForTesting.value();
+    if (!isMainResource) {
         if (request.isConditional())
-            request.setCachePolicy(ReloadIgnoringCacheData);
-        else if (documentLoader()->isLoadingInAPISense()) {
+            return ReloadIgnoringCacheData;
+        if (documentLoader()->isLoadingInAPISense()) {
             // If we inherit cache policy from a main resource, we use the DocumentLoader's
             // original request cache policy for two reasons:
             // 1. For POST requests, we mutate the cache policy for the main resource,
@@ -2602,22 +2580,41 @@ void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, FrameLoadTyp
             ResourceRequestCachePolicy mainDocumentOriginalCachePolicy = documentLoader()->originalRequest().cachePolicy();
             // Back-forward navigations try to load main resource from cache only to avoid re-submitting form data, and start over (with a warning dialog) if that fails.
             // This policy is set on initial request too, but should not be inherited.
-            ResourceRequestCachePolicy subresourceCachePolicy = (mainDocumentOriginalCachePolicy == ReturnCacheDataDontLoad) ? ReturnCacheDataElseLoad : mainDocumentOriginalCachePolicy;
-            request.setCachePolicy(subresourceCachePolicy);
-        } else
-            request.setCachePolicy(UseProtocolCachePolicy);
-
+            return (mainDocumentOriginalCachePolicy == ReturnCacheDataDontLoad) ? ReturnCacheDataElseLoad : mainDocumentOriginalCachePolicy;
+        }
     // FIXME: Other FrameLoader functions have duplicated code for setting cache policy of main request when reloading.
     // It seems better to manage it explicitly than to hide the logic inside addExtraFieldsToRequest().
     } else if (loadType == FrameLoadType::Reload || loadType == FrameLoadType::ReloadFromOrigin || request.isConditional())
+        return ReloadIgnoringCacheData;
+
+    return UseProtocolCachePolicy;
+}
+
+void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, FrameLoadType loadType, bool isMainResource)
+{
+    // Don't set the cookie policy URL if it's already been set.
+    // But make sure to set it on all requests regardless of protocol, as it has significance beyond the cookie policy (<rdar://problem/6616664>).
+    if (request.firstPartyForCookies().isEmpty()) {
+        if (isMainResource && m_frame.isMainFrame())
+            request.setFirstPartyForCookies(request.url());
+        else if (Document* document = m_frame.document())
+            request.setFirstPartyForCookies(document->firstPartyForCookies());
+    }
+
+    Page* page = frame().page();
+    bool hasSpecificCachePolicy = request.cachePolicy() != UseProtocolCachePolicy;
+
+    if (page && page->isResourceCachingDisabled()) {
         request.setCachePolicy(ReloadIgnoringCacheData);
+        loadType = FrameLoadType::ReloadFromOrigin;
+    } else if (!hasSpecificCachePolicy)
+        request.setCachePolicy(defaultRequestCachingPolicy(request, loadType, isMainResource));
 
-    if (m_overrideCachePolicyForTesting)
-        request.setCachePolicy(m_overrideCachePolicyForTesting.value());
-    if (m_overrideResourceLoadPriorityForTesting)
-        request.setPriority(m_overrideResourceLoadPriorityForTesting.value());
+    // The remaining modifications are only necessary for HTTP and HTTPS.
+    if (!request.url().isEmpty() && !request.url().protocolIsInHTTPFamily())
+        return;
 
-    if (request.cachePolicy() == ReloadIgnoringCacheData) {
+    if (!hasSpecificCachePolicy && request.cachePolicy() == ReloadIgnoringCacheData) {
         if (loadType == FrameLoadType::Reload)
             request.setHTTPHeaderField(HTTPHeaderName::CacheControl, "max-age=0");
         else if (loadType == FrameLoadType::ReloadFromOrigin) {
@@ -2626,7 +2623,12 @@ void FrameLoader::addExtraFieldsToRequest(ResourceRequest& request, FrameLoadTyp
         }
     }
 
-    if (mainResource)
+    if (m_overrideResourceLoadPriorityForTesting)
+        request.setPriority(m_overrideResourceLoadPriorityForTesting.value());
+
+    applyUserAgent(request);
+
+    if (isMainResource)
         request.setHTTPAccept(defaultAcceptHeader);
 
     // Make sure we send the Origin header.
index 601478c..614e526 100644 (file)
@@ -311,16 +311,17 @@ private:
     bool allChildrenAreComplete() const; // immediate children, not all descendants
 
     void checkTimerFired();
-    
+
     void loadSameDocumentItem(HistoryItem&);
     void loadDifferentDocumentItem(HistoryItem&, FrameLoadType, FormSubmissionCacheLoadPolicy);
-    
+
     void loadProvisionalItemFromCachedPage();
 
     void updateFirstPartyForCookies();
     void setFirstPartyForCookies(const URL&);
-    
+
     void addExtraFieldsToRequest(ResourceRequest&, FrameLoadType, bool isMainResource);
+    ResourceRequestCachePolicy defaultRequestCachingPolicy(const ResourceRequest&, FrameLoadType, bool isMainResource);
 
     void clearProvisionalLoad();
     void transitionToCommitted(CachedPage*);
index 210910d..f1fab22 100644 (file)
@@ -51,6 +51,7 @@
 #include "FrameLoaderClient.h"
 #include "HTMLElement.h"
 #include "HTMLFrameOwnerElement.h"
+#include "HTTPHeaderValues.h"
 #include "LoaderStrategy.h"
 #include "LocalizedStrings.h"
 #include "Logging.h"
@@ -663,6 +664,49 @@ void CachedResourceLoader::prepareFetch(CachedResource::Type type, CachedResourc
     // FIXME: Decide whether to support client hints
 }
 
+static inline void updateRequestAccordingCacheMode(CachedResourceRequest& request)
+{
+    if (request.options().cache == FetchOptions::Cache::Default
+            && (request.resourceRequest().hasHTTPHeaderField(HTTPHeaderName::IfModifiedSince)
+                || request.resourceRequest().hasHTTPHeaderField(HTTPHeaderName::IfNoneMatch)
+                || request.resourceRequest().hasHTTPHeaderField(HTTPHeaderName::IfUnmodifiedSince)
+                || request.resourceRequest().hasHTTPHeaderField(HTTPHeaderName::IfMatch)
+                || request.resourceRequest().hasHTTPHeaderField(HTTPHeaderName::IfRange)))
+        request.setCacheModeToNoStore();
+
+    switch (request.options().cache) {
+    case FetchOptions::Cache::NoCache:
+        request.mutableResourceRequest().setCachePolicy(ReloadIgnoringCacheData);
+        request.mutableResourceRequest().addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::CacheControl, HTTPHeaderValues::maxAge0());
+        break;
+    case FetchOptions::Cache::NoStore:
+        request.setCachingPolicy(CachingPolicy::DisallowCaching);
+        request.mutableResourceRequest().setCachePolicy(ReloadIgnoringCacheData);
+        request.mutableResourceRequest().addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::Pragma, HTTPHeaderValues::noCache());
+        request.mutableResourceRequest().addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::CacheControl, HTTPHeaderValues::noCache());
+        break;
+    case FetchOptions::Cache::Reload:
+        request.mutableResourceRequest().setCachePolicy(ReloadIgnoringCacheData);
+        request.mutableResourceRequest().addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::Pragma, HTTPHeaderValues::noCache());
+        request.mutableResourceRequest().addHTTPHeaderFieldIfNotPresent(HTTPHeaderName::CacheControl, HTTPHeaderValues::noCache());
+        break;
+    case FetchOptions::Cache::Default:
+        break;
+    case FetchOptions::Cache::ForceCache:
+        request.mutableResourceRequest().setCachePolicy(ReturnCacheDataElseLoad);
+        break;
+    case FetchOptions::Cache::OnlyIfCached:
+        request.mutableResourceRequest().setCachePolicy(ReturnCacheDataDontLoad);
+        break;
+    }
+}
+
+void CachedResourceLoader::updateHTTPRequestHeaders(CachedResourceRequest& resourceRequest)
+{
+    // Implementing steps 10 to 12 of https://fetch.spec.whatwg.org/#http-network-or-cache-fetch
+    updateRequestAccordingCacheMode(resourceRequest);
+}
+
 CachedResourceHandle<CachedResource> CachedResourceLoader::requestResource(CachedResource::Type type, CachedResourceRequest&& request, ForPreload forPreload, DeferOption defer)
 {
     if (Document* document = this->document())
@@ -720,6 +764,9 @@ CachedResourceHandle<CachedResource> CachedResourceLoader::requestResource(Cache
     loadTiming.markStartTimeAndFetchStart();
 #endif
 
+    if (request.resourceRequest().url().protocolIsInHTTPFamily())
+        updateHTTPRequestHeaders(request);
+
     auto& memoryCache = MemoryCache::singleton();
     if (request.allowsCaching() && memoryCache.disabled()) {
         DocumentResourceMap::iterator it = m_documentResources.find(url.string());
@@ -844,7 +891,8 @@ CachedResourceHandle<CachedResource> CachedResourceLoader::revalidateResource(Ca
 CachedResourceHandle<CachedResource> CachedResourceLoader::loadResource(CachedResource::Type type, CachedResourceRequest&& request)
 {
     auto& memoryCache = MemoryCache::singleton();
-    ASSERT(!request.allowsCaching() || !memoryCache.resourceForRequest(request.resourceRequest(), sessionID()));
+    ASSERT(!request.allowsCaching() || !memoryCache.resourceForRequest(request.resourceRequest(), sessionID())
+        || request.options().cache == FetchOptions::Cache::NoCache || request.options().cache == FetchOptions::Cache::NoStore || request.options().cache == FetchOptions::Cache::Reload);
 
     LOG(ResourceLoading, "Loading CachedResource for '%s'.", request.resourceRequest().url().stringCenterEllipsizedToLength().latin1().data());
 
@@ -897,6 +945,12 @@ CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalida
     if (!existingResource)
         return Load;
 
+    if (cachedResourceRequest.options().cache == FetchOptions::Cache::NoStore)
+        return Load;
+
+    if (cachedResourceRequest.options().cache == FetchOptions::Cache::Reload)
+        return Reload;
+
     // We already have a preload going for this URL.
     if (forPreload == ForPreload::Yes && existingResource->isPreloaded())
         return Use;
@@ -931,10 +985,12 @@ CachedResourceLoader::RevalidationPolicy CachedResourceLoader::determineRevalida
     if (defer == DeferOption::DeferredByClient)
         return Reload;
 
-    // Don't reload resources while pasting.
-    if (m_allowStaleResources)
+    // Don't reload resources while pasting or if cache mode allows stale resources.
+    if (m_allowStaleResources || cachedResourceRequest.options().cache == FetchOptions::Cache::ForceCache || cachedResourceRequest.options().cache == FetchOptions::Cache::OnlyIfCached)
         return Use;
 
+    ASSERT(cachedResourceRequest.options().cache == FetchOptions::Cache::Default || cachedResourceRequest.options().cache == FetchOptions::Cache::NoCache);
+
     // Always use preloads.
     if (existingResource->isPreloaded())
         return Use;
index f8f1522..e9eed8d 100644 (file)
@@ -154,6 +154,7 @@ private:
     enum class ForPreload { Yes, No };
     enum class DeferOption { NoDefer, DeferredByClient };
 
+    void updateHTTPRequestHeaders(CachedResourceRequest&);
     CachedResourceHandle<CachedResource> requestResource(CachedResource::Type, CachedResourceRequest&&, ForPreload = ForPreload::No, DeferOption = DeferOption::NoDefer);
     void prepareFetch(CachedResource::Type, CachedResourceRequest&);
     CachedResourceHandle<CachedResource> revalidateResource(CachedResourceRequest&&, CachedResource&);
index 2829410..a1377dc 100644 (file)
@@ -60,6 +60,8 @@ public:
     RefPtr<SecurityOrigin> releaseOrigin() { return WTFMove(m_origin); }
     SecurityOrigin* origin() const { return m_origin.get(); }
 
+    void setCacheModeToNoStore() { m_options.cache = FetchOptions::Cache::NoStore; }
+
 private:
     ResourceRequest m_resourceRequest;
     String m_charset;
index 442bb9b..f702aa7 100644 (file)
@@ -108,6 +108,7 @@ void EventSource::connect()
     options.credentials = m_withCredentials ? FetchOptions::Credentials::Include : FetchOptions::Credentials::SameOrigin;
     options.preflightPolicy = PreventPreflight;
     options.mode = FetchOptions::Mode::Cors;
+    options.cache = FetchOptions::Cache::NoStore;
     options.dataBufferingPolicy = DoNotBufferData;
     options.contentSecurityPolicyEnforcement = scriptExecutionContext()->shouldBypassMainWorldContentSecurityPolicy() ? ContentSecurityPolicyEnforcement::DoNotEnforce : ContentSecurityPolicyEnforcement::EnforceConnectSrcDirective;
 
index bb20f70..4fc8148 100644 (file)
@@ -103,6 +103,11 @@ void HTTPHeaderMap::add(const String& name, const String& value)
     add(headerName, value);
 }
 
+bool HTTPHeaderMap::addIfNotPresent(HTTPHeaderName headerName, const String& value)
+{
+    return m_commonHeaders.add(headerName, value).isNewEntry;
+}
+
 bool HTTPHeaderMap::contains(const String& name) const
 {
     HTTPHeaderName headerName;
index 3709f25..1495a6c 100644 (file)
@@ -141,6 +141,7 @@ public:
     WEBCORE_EXPORT String get(HTTPHeaderName) const;
     void set(HTTPHeaderName, const String& value);
     void add(HTTPHeaderName, const String& value);
+    bool addIfNotPresent(HTTPHeaderName, const String&);
     bool contains(HTTPHeaderName) const;
     WEBCORE_EXPORT bool remove(HTTPHeaderName);
 
index 4642d05..72df220 100644 (file)
@@ -44,6 +44,18 @@ const String& formURLEncodedContentType()
     return contentType;
 }
 
+const String& noCache()
+{
+    static NeverDestroyed<const String> value(ASCIILiteral("no-cache"));
+    return value;
+}
+
+const String& maxAge0()
+{
+    static NeverDestroyed<const String> value(ASCIILiteral("max-age=0"));
+    return value;
+}
+
 }
 
 }
index 299b5a7..9d85f96 100644 (file)
@@ -32,7 +32,8 @@ namespace HTTPHeaderValues {
 
 const String& textPlainContentType();
 const String& formURLEncodedContentType();
-
+const String& noCache();
+const String& maxAge0();
 }
 
 }
index a6b5356..f223bfe 100644 (file)
@@ -464,6 +464,17 @@ void ResourceRequestBase::setPriority(ResourceLoadPriority priority)
         m_platformRequestUpdated = false;
 }
 
+void ResourceRequestBase::addHTTPHeaderFieldIfNotPresent(HTTPHeaderName name, const String& value)
+{
+    updateResourceRequest();
+
+    if (!m_httpHeaderFields.addIfNotPresent(name, value))
+        return;
+
+    if (url().protocolIsInHTTPFamily())
+        m_platformRequestUpdated = false;
+}
+
 void ResourceRequestBase::addHTTPHeaderField(HTTPHeaderName name, const String& value)
 {
     updateResourceRequest();
@@ -477,13 +488,18 @@ void ResourceRequestBase::addHTTPHeaderField(HTTPHeaderName name, const String&
 void ResourceRequestBase::addHTTPHeaderField(const String& name, const String& value)
 {
     updateResourceRequest();
-    
+
     m_httpHeaderFields.add(name, value);
-    
+
     if (url().protocolIsInHTTPFamily())
         m_platformRequestUpdated = false;
 }
-    
+
+bool ResourceRequestBase::hasHTTPHeaderField(HTTPHeaderName headerName) const
+{
+    return m_httpHeaderFields.contains(headerName);
+}
+
 void ResourceRequestBase::setHTTPHeaderFields(HTTPHeaderMap headerFields)
 {
     updateResourceRequest();
index 010acc5..49fb67f 100644 (file)
@@ -85,6 +85,9 @@ public:
     WEBCORE_EXPORT void setHTTPHeaderField(HTTPHeaderName, const String& value);
     void addHTTPHeaderField(HTTPHeaderName, const String& value);
     void addHTTPHeaderField(const String& name, const String& value);
+    void addHTTPHeaderFieldIfNotPresent(HTTPHeaderName, const String&);
+
+    bool hasHTTPHeaderField(HTTPHeaderName) const;
 
     // Instead of passing a string literal to any of these functions, just use a HTTPHeaderName instead.
     template<size_t length> String httpHeaderField(const char (&)[length]) const = delete;