Source/WebCore/ChangeLog

 12019-10-02 Rob Buis <rbuis@igalia.com>
 2
 3 Support stale-while-revalidate cache strategy
 4 https://bugs.webkit.org/show_bug.cgi?id=201461
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Start parsing the stale-while-revalidate Cache-Control directive
 9 and expose it on ResourceResponse.
 10
 11 Tests: imported/w3c/web-platform-tests/fetch/stale-while-revalidate/fetch-sw.https.html
 12 imported/w3c/web-platform-tests/fetch/stale-while-revalidate/fetch.html
 13 imported/w3c/web-platform-tests/fetch/stale-while-revalidate/stale-css.html
 14 imported/w3c/web-platform-tests/fetch/stale-while-revalidate/stale-image.html
 15 imported/w3c/web-platform-tests/fetch/stale-while-revalidate/stale-script.html
 16
 17 * platform/network/CacheValidation.cpp:
 18 (WebCore::parseCacheControlDirectives):
 19 * platform/network/CacheValidation.h:
 20 (WebCore::CacheControlDirectives::CacheControlDirectives):
 21 * platform/network/ResourceResponseBase.cpp:
 22 (WebCore::ResourceResponseBase::cacheControlStaleWhileRevalidate const):
 23 * platform/network/ResourceResponseBase.h:
 24
1252019-10-02 Antoine Quint <graouts@apple.com>
226
327 [Web Animations] Implement replaced animations

Source/WebKit/ChangeLog

 12019-10-02 Rob Buis <rbuis@igalia.com>
 2
 3 Support stale-while-revalidate cache strategy
 4 https://bugs.webkit.org/show_bug.cgi?id=201461
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Add a new UseDecision value AsyncRevalidate for async revalidation. This is used
 9 when the retrieved cache entry is a stale-while-revalidate response [1].
 10 In case of AsyncRevalidate, a check is made to see if there is a
 11 current async revalidation ongoing for the entry, if not one is
 12 started. Regardless, the stale entry is returned, until either the
 13 async revalidation ends successfully or at the moment where the
 14 response expires for real.
 15
 16 [1] https://fetch.spec.whatwg.org/#concept-stale-while-revalidate-response
 17
 18 * NetworkProcess/cache/NetworkCache.cpp:
 19 (WebKit::NetworkCache::responseHasExpired):
 20 (WebKit::NetworkCache::responseNeedsRevalidation):
 21 (WebKit::NetworkCache::makeUseDecision):
 22 (WebKit::NetworkCache::makeStoreDecision):
 23 (WebKit::NetworkCache::Cache::retrieve):
 24 * NetworkProcess/cache/NetworkCache.h:
 25 * NetworkProcess/cache/NetworkCacheSpeculativeLoadManager.cpp:
 26 (WebKit::NetworkCache::constructRevalidationRequest):
 27 (WebKit::NetworkCache::AsyncRevalidation::AsyncRevalidation):
 28 (WebKit::NetworkCache::AsyncRevalidation::load const):
 29 (WebKit::NetworkCache::AsyncRevalidation::staleWhileRevalidateEnding):
 30 (WebKit::NetworkCache::SpeculativeLoadManager::startAsyncRevalidationIfNeeded):
 31 * NetworkProcess/cache/NetworkCacheSpeculativeLoadManager.h:
 32
1332019-10-02 Zan Dobersek <zdobersek@igalia.com>
234
335 Unreviewed build fix for non-Mac ports after r250600.

Source/WebCore/platform/network/CacheValidation.cpp

@@CacheControlDirectives parseCacheControlDirectives(const HTTPHeaderMap& headers)
313313 double maxStale = directives[i].second.toDouble(&ok);
314314 if (ok)
315315 result.maxStale = Seconds { maxStale };
316  } else if (equalLettersIgnoringASCIICase(directives[i].first, "immutable"))
 316 } else if (equalLettersIgnoringASCIICase(directives[i].first, "immutable")) {
317317 result.immutable = true;
 318 } else if (equalLettersIgnoringASCIICase(directives[i].first, "stale-while-revalidate")) {
 319 if (result.staleWhileRevalidate) {
 320 // First stale-while-revalidate directive wins if there are multiple ones.
 321 continue;
 322 }
 323 bool ok;
 324 double staleWhileRevalidate = directives[i].second.toDouble(&ok);
 325 if (ok) {
 326 result.maxStale = Seconds { staleWhileRevalidate };
 327 result.staleWhileRevalidate = true;
 328 }
 329 }
318330 }
319331 }
320332

Source/WebCore/platform/network/CacheValidation.h

@@struct CacheControlDirectives {
6565 , noStore(false)
6666 , mustRevalidate(false)
6767 , immutable(false)
 68 , staleWhileRevalidate(false)
6869 { }
6970
7071 Markable<Seconds, Seconds::MarkableTraits> maxAge;

@@struct CacheControlDirectives {
7374 bool noStore : 1;
7475 bool mustRevalidate : 1;
7576 bool immutable : 1;
 77 bool staleWhileRevalidate : 1;
7678};
7779WEBCORE_EXPORT CacheControlDirectives parseCacheControlDirectives(const HTTPHeaderMap&);
7880

Source/WebCore/platform/network/ResourceResponseBase.cpp

@@Optional<Seconds> ResourceResponseBase::cacheControlMaxAge() const
655655 return m_cacheControlDirectives.maxAge;
656656}
657657
 658Optional<Seconds> ResourceResponseBase::cacheControlStaleWhileRevalidate() const
 659{
 660 if (!m_haveParsedCacheControlHeader)
 661 parseCacheControlDirectives();
 662 if (m_cacheControlDirectives.staleWhileRevalidate)
 663 return m_cacheControlDirectives.maxStale;
 664 return WTF::nullopt;
 665}
 666
658667static Optional<WallTime> parseDateValueInHeader(const HTTPHeaderMap& headers, HTTPHeaderName headerName)
659668{
660669 String headerValue = headers.get(headerName);

Source/WebCore/platform/network/ResourceResponseBase.h

@@public:
136136 WEBCORE_EXPORT bool cacheControlContainsImmutable() const;
137137 WEBCORE_EXPORT bool hasCacheValidatorFields() const;
138138 WEBCORE_EXPORT Optional<Seconds> cacheControlMaxAge() const;
 139 WEBCORE_EXPORT Optional<Seconds> cacheControlStaleWhileRevalidate() const;
139140 WEBCORE_EXPORT Optional<WallTime> date() const;
140141 WEBCORE_EXPORT Optional<Seconds> age() const;
141142 WEBCORE_EXPORT Optional<WallTime> expires() const;

Source/WebKit/NetworkProcess/cache/NetworkCache.cpp

@@static bool cachePolicyAllowsExpired(WebCore::ResourceRequestCachePolicy policy)
152152 return false;
153153}
154154
155 static bool responseHasExpired(const WebCore::ResourceResponse& response, WallTime timestamp, Optional<Seconds> maxStale)
 155static bool responseHasExpired(const WebCore::ResourceResponse& response, WallTime timestamp, Optional<Seconds> maxStale, bool& inResponseStaleness)
156156{
157157 if (response.cacheControlContainsNoCache())
158158 return true;

@@static bool responseHasExpired(const WebCore::ResourceResponse& response, WallTi
162162
163163 auto maximumStaleness = maxStale ? maxStale.value() : 0_ms;
164164 bool hasExpired = age - lifetime > maximumStaleness;
 165#if ENABLE(NETWORK_CACHE_SPECULATIVE_REVALIDATION)
 166 if (hasExpired) {
 167 auto responseMaxStaleness = response.cacheControlStaleWhileRevalidate();
 168 maximumStaleness += responseMaxStaleness ? responseMaxStaleness.value() : 0_ms;
 169 inResponseStaleness = age - lifetime < maximumStaleness;
 170 }
 171#endif
165172
166173#ifndef LOG_DISABLED
167174 if (hasExpired)
168  LOG(NetworkCache, "(NetworkProcess) needsRevalidation hasExpired age=%f lifetime=%f max-stale=%g", age, lifetime, maxStale);
 175 LOG(NetworkCache, "(NetworkProcess) needsRevalidation hasExpired age=%f lifetime=%f max-staleness=%f", age, lifetime, maximumStaleness);
169176#endif
170177
171178 return hasExpired;
172179}
173180
174 static bool responseNeedsRevalidation(const WebCore::ResourceResponse& response, const WebCore::ResourceRequest& request, WallTime timestamp)
 181static UseDecision responseNeedsRevalidation(const WebCore::ResourceResponse& response, const WebCore::ResourceRequest& request, WallTime timestamp)
175182{
176183 auto requestDirectives = WebCore::parseCacheControlDirectives(request.httpHeaderFields());
177184 if (requestDirectives.noCache)
178  return true;
 185 return UseDecision::Validate;
179186 // For requests we ignore max-age values other than zero.
180187 if (requestDirectives.maxAge && requestDirectives.maxAge.value() == 0_ms)
181  return true;
 188 return UseDecision::Validate;
182189
183  return responseHasExpired(response, timestamp, requestDirectives.maxStale);
 190 bool inResponseStaleness = false;
 191 if (responseHasExpired(response, timestamp, requestDirectives.maxStale, inResponseStaleness))
 192 return inResponseStaleness ? UseDecision::AsyncRevalidate : UseDecision::Validate;
 193 return UseDecision::Use;
184194}
185195
186196static UseDecision makeUseDecision(NetworkProcess& networkProcess, const PAL::SessionID& sessionID, const Entry& entry, const WebCore::ResourceRequest& request)

@@static UseDecision makeUseDecision(NetworkProcess& networkProcess, const PAL::Se
197207 if (cachePolicyAllowsExpired(request.cachePolicy()))
198208 return UseDecision::Use;
199209
200  if (!responseNeedsRevalidation(entry.response(), request, entry.timeStamp()))
201  return UseDecision::Use;
 210 auto decision = responseNeedsRevalidation(entry.response(), request, entry.timeStamp());
 211 if (decision != UseDecision::Validate)
 212 return decision;
202213
203214 if (!entry.response().hasCacheValidatorFields())
204215 return UseDecision::NoDueToMissingValidatorFields;

@@static StoreDecision makeStoreDecision(const WebCore::ResourceRequest& originalR
251262 bool storeUnconditionallyForHistoryNavigation = isMainResource || originalRequest.priority() == WebCore::ResourceLoadPriority::VeryHigh;
252263 if (!storeUnconditionallyForHistoryNavigation) {
253264 auto now = WallTime::now();
254  bool hasNonZeroLifetime = !response.cacheControlContainsNoCache() && WebCore::computeFreshnessLifetimeForHTTPFamily(response, now) > 0_ms;
255 
 265 Seconds allowedStale;
 266 if (auto value = response.cacheControlStaleWhileRevalidate())
 267 allowedStale = value.value();
 268 bool hasNonZeroLifetime = !response.cacheControlContainsNoCache() && (WebCore::computeFreshnessLifetimeForHTTPFamily(response, now) > 0_ms || allowedStale > 0_ms);
256269 bool possiblyReusable = response.hasCacheValidatorFields() || hasNonZeroLifetime;
257270 if (!possiblyReusable)
258271 return StoreDecision::NoDueToUnlikelyToReuse;

@@void Cache::retrieve(const WebCore::ResourceRequest& request, const GlobalFrameI
334347 }
335348#endif
336349
337  m_storage->retrieve(storageKey, priority, [request, completionHandler = WTFMove(completionHandler), info = WTFMove(info), storageKey, networkProcess = makeRef(networkProcess()), sessionID = m_sessionID](auto record, auto timings) mutable {
 350 m_storage->retrieve(storageKey, priority, [this, request, completionHandler = WTFMove(completionHandler), info = WTFMove(info), storageKey, networkProcess = makeRef(networkProcess()), sessionID = m_sessionID, frameID](auto record, auto timings) mutable {
338351 info.storageTimings = timings;
339352
340353 if (!record) {
341354 LOG(NetworkCache, "(NetworkProcess) not found in storage");
342 
343355 completeRetrieve(WTFMove(completionHandler), nullptr, info);
344356 return false;
345357 }

@@void Cache::retrieve(const WebCore::ResourceRequest& request, const GlobalFrameI
350362
351363 auto useDecision = entry ? makeUseDecision(networkProcess, sessionID, *entry, request) : UseDecision::NoDueToDecodeFailure;
352364 switch (useDecision) {
 365 case UseDecision::AsyncRevalidate: {
 366#if ENABLE(NETWORK_CACHE_SPECULATIVE_REVALIDATION)
 367 if (m_speculativeLoadManager) {
 368 std::unique_ptr<Entry> entryCopy = makeUnique<Entry>(*entry);
 369 m_speculativeLoadManager->startAsyncRevalidationIfNeeded(request, storageKey, WTFMove(entryCopy), frameID);
 370 }
 371#endif
 372 FALLTHROUGH;
 373 }
353374 case UseDecision::Use:
354375 break;
355376 case UseDecision::Validate:

Source/WebKit/NetworkProcess/cache/NetworkCache.h

@@enum class StoreDecision {
8282enum class UseDecision {
8383 Use,
8484 Validate,
 85 AsyncRevalidate,
8586 NoDueToVaryingHeaderMismatch,
8687 NoDueToMissingValidatorFields,
8788 NoDueToDecodeFailure,

Source/WebKit/NetworkProcess/cache/NetworkCacheSpeculativeLoadManager.cpp

@@void SpeculativeLoadManager::revalidateSubresource(const SubresourceInfo& subres
486486 });
487487 m_pendingPreloads.add(key, WTFMove(revalidator));
488488}
489 
 489
 490static inline ResourceRequest constructRevalidationRequest(const Key& key, const ResourceRequest& request, const Entry* entry)
 491{
 492 ResourceRequest revalidationRequest = request;
 493 if (!key.partition().isEmpty())
 494 revalidationRequest.setCachePartition(key.partition());
 495 ASSERT_WITH_MESSAGE(key.range().isEmpty(), "range is not supported");
 496
 497 revalidationRequest.makeUnconditional();
 498 if (entry) {
 499 String eTag = entry->response().httpHeaderField(HTTPHeaderName::ETag);
 500 if (!eTag.isEmpty())
 501 revalidationRequest.setHTTPHeaderField(HTTPHeaderName::IfNoneMatch, eTag);
 502
 503 String lastModified = entry->response().httpHeaderField(HTTPHeaderName::LastModified);
 504 if (!lastModified.isEmpty())
 505 revalidationRequest.setHTTPHeaderField(HTTPHeaderName::IfModifiedSince, lastModified);
 506 }
 507
 508 revalidationRequest.setPriority(WebCore::ResourceLoadPriority::Low);
 509
 510 return revalidationRequest;
 511}
 512
 513class AsyncRevalidation {
 514 WTF_MAKE_FAST_ALLOCATED;
 515public:
 516 typedef Function<void (bool)> AsyncRevalidationCompletionHandler;
 517 AsyncRevalidation(Cache& cache, const GlobalFrameID& frameID, const ResourceRequest& request, std::unique_ptr<NetworkCache::Entry> entry, AsyncRevalidationCompletionHandler&& handler)
 518 : m_timer(*this, &AsyncRevalidation::staleWhileRevalidateEnding)
 519 , m_completionHandler(WTFMove(handler))
 520 {
 521 auto key = entry->key();
 522 auto revalidationRequest = constructRevalidationRequest(key, request, entry.get());
 523 auto age = WebCore::computeCurrentAge(entry->response(), entry->timeStamp());
 524 auto lifetime = WebCore::computeFreshnessLifetimeForHTTPFamily(entry->response(), entry->timeStamp());
 525 auto responseMaxStaleness = entry->response().cacheControlStaleWhileRevalidate();
 526 ASSERT(responseMaxStaleness);
 527 m_timer.startOneShot(*responseMaxStaleness - (age - lifetime));
 528 m_load = makeUnique<SpeculativeLoad>(cache, frameID, revalidationRequest, WTFMove(entry), [this, key, revalidationRequest](std::unique_ptr<Entry> revalidatedEntry) {
 529 ASSERT(!revalidatedEntry || !revalidatedEntry->needsValidation());
 530 ASSERT(!revalidatedEntry || revalidatedEntry->key() == key);
 531 m_completionHandler(revalidatedEntry.get());
 532 });
 533 }
 534
 535 const SpeculativeLoad& load() const { return *m_load; }
 536
 537 void staleWhileRevalidateEnding() { m_completionHandler(true); }
 538
 539private:
 540 std::unique_ptr<SpeculativeLoad> m_load;
 541 Timer m_timer;
 542 AsyncRevalidationCompletionHandler m_completionHandler;
 543};
 544
 545void SpeculativeLoadManager::startAsyncRevalidationIfNeeded(const ResourceRequest& request, const NetworkCache::Key& key, std::unique_ptr<Entry> entry, const GlobalFrameID& frameID)
 546{
 547 auto* pendingAsyncRevalidation = m_pendingAsyncRevalidations.get(key);
 548 if (pendingAsyncRevalidation && canUsePendingPreload(pendingAsyncRevalidation->load(), request))
 549 return;
 550
 551 auto revalidator = makeUnique<AsyncRevalidation>(m_cache, frameID, request, WTFMove(entry), [this, key](bool ending) {
 552 if (ending) {
 553 m_pendingAsyncRevalidations.take(key);
 554 LOG(NetworkCache, "(NetworkProcess) Async revalidation completed for '%s':", key.identifier().utf8().data());
 555 }
 556 });
 557 m_pendingAsyncRevalidations.add(key, WTFMove(revalidator));
 558}
 559
490560static bool canRevalidate(const SubresourceInfo& subresourceInfo, const Entry* entry)
491561{
492562 ASSERT(!subresourceInfo.isTransient());

Source/WebKit/NetworkProcess/cache/NetworkCacheSpeculativeLoadManager.h

@@namespace WebKit {
3838
3939namespace NetworkCache {
4040
 41class AsyncRevalidation;
4142class Entry;
4243class SpeculativeLoad;
4344class SubresourceInfo;

@@public:
5657 bool canRetrieve(const Key& storageKey, const WebCore::ResourceRequest&, const GlobalFrameID&) const;
5758 void retrieve(const Key& storageKey, RetrieveCompletionHandler&&);
5859
 60 void startAsyncRevalidationIfNeeded(const WebCore::ResourceRequest&, const NetworkCache::Key&, std::unique_ptr<Entry>, const GlobalFrameID&);
 61
5962private:
6063 class PreloadedEntry;
6164

@@private:
8184
8285 HashMap<Key, std::unique_ptr<PreloadedEntry>> m_preloadedEntries;
8386
 87 HashMap<Key, std::unique_ptr<AsyncRevalidation>> m_pendingAsyncRevalidations;
 88
8489 class ExpiringEntry;
8590 HashMap<Key, std::unique_ptr<ExpiringEntry>> m_notPreloadedEntries; // For logging.
8691};

LayoutTests/ChangeLog

 12019-10-02 Rob Buis <rbuis@igalia.com>
 2
 3 Support stale-while-revalidate cache strategy
 4 https://bugs.webkit.org/show_bug.cgi?id=201461
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Skip newly imported tests for WK1.
 9
 10 * platform/ios-wk1/TestExpectations:
 11 * platform/mac-wk1/TestExpectations:
 12
1132019-10-02 Carlos Garcia Campos <cgarcia@igalia.com>
214
315 Unreviewed GTK gardening. Add missing results after r250276.

LayoutTests/imported/w3c/ChangeLog

 12019-10-02 Rob Buis <rbuis@igalia.com>
 2
 3 Support stale-while-revalidate cache strategy
 4 https://bugs.webkit.org/show_bug.cgi?id=201461
 5
 6 Reviewed by NOBODY (OOPS!).
 7
 8 Import stale-while-revalidate WPT tests.
 9
 10 * resources/import-expectations.json:
 11 * web-platform-tests/fetch/stale-while-revalidate/fetch-expected.txt: Added.
 12 * web-platform-tests/fetch/stale-while-revalidate/fetch-sw.https-expected.txt: Added.
 13 * web-platform-tests/fetch/stale-while-revalidate/fetch-sw.https.html: Added.
 14 * web-platform-tests/fetch/stale-while-revalidate/fetch.html: Added.
 15 * web-platform-tests/fetch/stale-while-revalidate/resources/stale-css.py: Added.
 16 (main):
 17 * web-platform-tests/fetch/stale-while-revalidate/resources/stale-image.py: Added.
 18 (main):
 19 * web-platform-tests/fetch/stale-while-revalidate/resources/stale-script.py: Added.
 20 (id_token):
 21 (main):
 22 * web-platform-tests/fetch/stale-while-revalidate/resources/w3c-import.log: Added.
 23 * web-platform-tests/fetch/stale-while-revalidate/stale-css-expected.txt: Added.
 24 * web-platform-tests/fetch/stale-while-revalidate/stale-css.html: Added.
 25 * web-platform-tests/fetch/stale-while-revalidate/stale-image-expected.txt: Added.
 26 * web-platform-tests/fetch/stale-while-revalidate/stale-image.html: Added.
 27 * web-platform-tests/fetch/stale-while-revalidate/stale-script-expected.txt: Added.
 28 * web-platform-tests/fetch/stale-while-revalidate/stale-script.html: Added.
 29 * web-platform-tests/fetch/stale-while-revalidate/sw-intercept.js: Added.
 30 (async.broadcast):
 31 * web-platform-tests/fetch/stale-while-revalidate/w3c-import.log: Added.
 32
1332019-10-02 Antoine Quint <graouts@apple.com>
234
335 [Web Animations] Implement replaced animations

LayoutTests/imported/w3c/resources/import-expectations.json

159159 "web-platform-tests/fetch/api": "import",
160160 "web-platform-tests/fetch/api/cors": "import",
161161 "web-platform-tests/fetch/range": "import",
 162 "web-platform-tests/fetch/stale-while-revalidate": "import",
162163 "web-platform-tests/fullscreen": "skip",
163164 "web-platform-tests/gamepad": "skip",
164165 "web-platform-tests/generic-sensor": "skip",

LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/fetch-expected.txt

 1
 2PASS Second fetch returns same response
 3

LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/fetch-sw.https-expected.txt

 1
 2PASS Second fetch returns same response
 3

LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/fetch-sw.https.html

 1<!DOCTYPE html>
 2<html>
 3<head>
 4 <meta charset="utf-8">
 5 <title>Stale Revalidation Requests don't get sent to service worker</title>
 6 <script src="/resources/testharness.js"></script>
 7 <script src="/resources/testharnessreport.js"></script>
 8 <script src="../../service-workers/service-worker/resources/test-helpers.sub.js"></script>
 9 <script src="/common/utils.js"></script>
 10</head>
 11<body>
 12<script>
 13
 14 // Duplicating this resource to make service worker scoping simpler.
 15 async function setupRegistrationAndWaitToBeControlled(t, scope) {
 16 const controlled = new Promise((resolve) => {
 17 navigator.serviceWorker.oncontrollerchange = () => { resolve(); };
 18 });
 19 const reg = await navigator.serviceWorker.register('sw-intercept.js');
 20 await wait_for_state(t, reg.installing, 'activated');
 21 await controlled;
 22 add_completion_callback(_ => reg.unregister());
 23 return reg;
 24 }
 25
 26 // Using 250ms polling interval to provide enough 'network calmness' to give
 27 // the background low priority revalidation request a chance to kick in.
 28 function wait250ms(test) {
 29 return new Promise(resolve => {
 30 test.step_timeout(() => {
 31 resolve();
 32 }, 250);
 33 });
 34 }
 35
 36 promise_test(async (test) => {
 37 var request_token = token();
 38 const uri = 'resources/stale-script.py?token=' + request_token;
 39
 40 await setupRegistrationAndWaitToBeControlled(test, 'resources/stale-script.py');
 41
 42 var service_worker_count = 0;
 43 navigator.serviceWorker.addEventListener('message', function once(event) {
 44 if (event.data.endsWith(uri)) {
 45 service_worker_count++;
 46 }
 47 });
 48
 49 const response = await fetch(uri);
 50 const response2 = await fetch(uri);
 51 assert_equals(response.headers.get('Unique-Id'), response2.headers.get('Unique-Id'));
 52 while(true) {
 53 const revalidation_check = await fetch(`resources/stale-script.py?query&token=` + request_token);
 54 if (revalidation_check.headers.get('Count') == '2') {
 55 // The service worker should not see the revalidation request.
 56 assert_equals(service_worker_count, 2);
 57 break;
 58 }
 59 await wait250ms(test);
 60 }
 61 }, 'Second fetch returns same response');
 62
 63</script>
 64</body>
 65</html>

LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/fetch.html

 1<!DOCTYPE html>
 2<meta charset="utf-8">
 3<title>Tests Stale While Revalidate is not executed for fetch API</title>
 4<script src="/resources/testharness.js"></script>
 5<script src="/resources/testharnessreport.js"></script>
 6<script src="/common/utils.js"></script>
 7<script>
 8function wait25ms(test) {
 9 return new Promise(resolve => {
 10 test.step_timeout(() => {
 11 resolve();
 12 }, 25);
 13 });
 14}
 15
 16promise_test(async (test) => {
 17 var request_token = token();
 18
 19 const response = await fetch(`resources/stale-script.py?token=` + request_token);
 20 const response2 = await fetch(`resources/stale-script.py?token=` + request_token);
 21
 22 assert_equals(response.headers.get('Unique-Id'), response2.headers.get('Unique-Id'));
 23
 24 while(true) {
 25 const revalidation_check = await fetch(`resources/stale-script.py?query&token=` + request_token);
 26 if (revalidation_check.headers.get('Count') == '2') {
 27 break;
 28 }
 29 await wait25ms(test);
 30 }
 31}, 'Second fetch returns same response');
 32</script>

LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/resources/stale-css.py

 1def main(request, response):
 2
 3 token = request.GET.first("token", None)
 4 is_query = request.GET.first("query", None) != None
 5 with request.server.stash.lock:
 6 value = request.server.stash.take(token)
 7 count = 0
 8 if value != None:
 9 count = int(value)
 10 if is_query:
 11 if count < 2:
 12 request.server.stash.put(token, count)
 13 else:
 14 count = count + 1
 15 request.server.stash.put(token, count)
 16 if is_query:
 17 headers = [("Count", count)]
 18 content = ""
 19 return 200, headers, content
 20 else:
 21 content = "body { background: rgb(0, 128, 0); }"
 22 if count > 1:
 23 content = "body { background: rgb(255, 0, 0); }"
 24
 25 headers = [("Content-Type", "text/css"),
 26 ("Cache-Control", "private, max-age=0, stale-while-revalidate=60")]
 27
 28 return 200, headers, content

LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/resources/stale-image.py

 1import os.path
 2
 3def main(request, response):
 4
 5 token = request.GET.first("token", None)
 6 is_query = request.GET.first("query", None) != None
 7 with request.server.stash.lock:
 8 value = request.server.stash.take(token)
 9 count = 0
 10 if value != None:
 11 count = int(value)
 12 if is_query:
 13 if count < 2:
 14 request.server.stash.put(token, count)
 15 else:
 16 count = count + 1
 17 request.server.stash.put(token, count)
 18
 19 if is_query:
 20 headers = [("Count", count)]
 21 content = ""
 22 return 200, headers, content
 23 else:
 24 filename = "green-16x16.png"
 25 if count > 1:
 26 filename = "green-256x256.png"
 27
 28 path = os.path.join(os.path.dirname(__file__), "../../../images", filename)
 29 body = open(path, "rb").read()
 30
 31 response.add_required_headers = False
 32 response.writer.write_status(200)
 33 response.writer.write_header("content-length", len(body))
 34 response.writer.write_header("Cache-Control", "private, max-age=0, stale-while-revalidate=60")
 35 response.writer.write_header("content-type", "image/png")
 36 response.writer.end_headers()
 37
 38 response.writer.write(body)

LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/resources/stale-script.py

 1import random, string, datetime
 2
 3def id_token():
 4 letters = string.ascii_lowercase
 5 return ''.join(random.choice(letters) for i in range(20))
 6
 7def main(request, response):
 8 token = request.GET.first("token", None)
 9 is_query = request.GET.first("query", None) != None
 10 with request.server.stash.lock:
 11 value = request.server.stash.take(token)
 12 count = 0
 13 if value != None:
 14 count = int(value)
 15 if is_query:
 16 if count < 2:
 17 request.server.stash.put(token, count)
 18 else:
 19 count = count + 1
 20 request.server.stash.put(token, count)
 21
 22 if is_query:
 23 headers = [("Count", count)]
 24 content = ""
 25 return 200, headers, content
 26 else:
 27 unique_id = id_token()
 28 headers = [("Content-Type", "text/javascript"),
 29 ("Cache-Control", "private, max-age=0, stale-while-revalidate=60"),
 30 ("Unique-Id", unique_id)]
 31 content = "report('{}')".format(unique_id)
 32 return 200, headers, content

LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/resources/w3c-import.log

 1The tests in this directory were imported from the W3C repository.
 2Do NOT modify these tests directly in WebKit.
 3Instead, create a pull request on the WPT github:
 4 https://github.com/web-platform-tests/wpt
 5
 6Then run the Tools/Scripts/import-w3c-tests in WebKit to reimport
 7
 8Do NOT modify or remove this file.
 9
 10------------------------------------------------------------------------
 11Properties requiring vendor prefixes:
 12None
 13Property values requiring vendor prefixes:
 14None
 15------------------------------------------------------------------------
 16List of files:
 17/LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/resources/stale-css.py
 18/LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/resources/stale-image.py
 19/LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/resources/stale-script.py

LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/stale-css-expected.txt

 1
 2PASS Cache returns stale resource
 3

LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/stale-css.html

 1<!DOCTYPE html>
 2<meta charset="utf-8">
 3<title>Tests Stale While Revalidate works for css</title>
 4<script src="/resources/testharness.js"></script>
 5<script src="/resources/testharnessreport.js"></script>
 6<script src="/common/utils.js"></script>
 7<body>
 8<script>
 9
 10var request_token = token();
 11async_test(t => {
 12 window.onload = t.step_func(() => {
 13 t.step_timeout(() => {
 14 assert_equals(window.getComputedStyle(document.body).getPropertyValue('background-color'), "rgb(0, 128, 0)");
 15 var link2 = document.createElement("link");
 16 link2.onload = t.step_func(() => {
 17 assert_equals(window.getComputedStyle(document.body).getPropertyValue('background-color'), "rgb(0, 128, 0)");
 18 var checkResult = () => {
 19 // We poll because we don't know when the revalidation will occur.
 20 fetch("resources/stale-css.py?query&token=" + request_token).then(t.step_func((response) => {
 21 var count = response.headers.get("Count");
 22 if (count == '2') {
 23 t.done();
 24 } else {
 25 t.step_timeout(checkResult, 25);
 26 }
 27 }));
 28 };
 29 t.step_timeout(checkResult, 25);
 30 });
 31 link2.rel = "stylesheet";
 32 link2.type = "text/css";
 33 link2.href = "resources/stale-css.py?token=" + request_token;
 34 document.body.appendChild(link2);
 35 }, 0);
 36 });
 37}, 'Cache returns stale resource');
 38
 39var link = document.createElement("link");
 40link.rel = "stylesheet";
 41link.type = "text/css";
 42link.href = "resources/stale-css.py?token=" + request_token;
 43document.body.appendChild(link);
 44</script>
 45</body>

LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/stale-image-expected.txt

 1
 2
 3PASS Cache returns stale resource
 4

LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/stale-image.html

 1<!DOCTYPE html>
 2<meta charset="utf-8">
 3<title>Tests Stale While Revalidate works for images</title>
 4<script src="/resources/testharness.js"></script>
 5<script src="/resources/testharnessreport.js"></script>
 6<script src="/common/utils.js"></script>
 7<body>
 8<!--
 9Use a child document to load the second stale image into because
 10an image loaded into the same document will skip cache-control headers.
 11See: https://html.spec.whatwg.org/#the-list-of-available-images
 12-->
 13<iframe id="child" srcdoc=""></iframe>
 14<script>
 15
 16var request_token = token();
 17async_test(t => {
 18 window.onload = t.step_func(() => {
 19 t.step_timeout(() => {
 20 assert_equals(document.getElementById("firstimage").width, 16, "Width is 16");
 21 var childDocument = document.getElementById('child').contentDocument;
 22 var img2 = childDocument.createElement("img");
 23 img2.onload = t.step_func(() => {
 24 assert_equals(img2.width, 16, "image dimension");
 25 var checkResult = () => {
 26 // We poll because we don't know when the revalidation will occur.
 27 fetch("resources/stale-image.py?query&token=" + request_token).then(t.step_func((response) => {
 28 var count = response.headers.get("Count");
 29 if (count == '2') {
 30 t.done();
 31 } else {
 32 t.step_timeout(checkResult, 25);
 33 }
 34 }));
 35 };
 36 t.step_timeout(checkResult, 25);
 37 });
 38 img2.src = "resources/stale-image.py?token=" + request_token;
 39 childDocument.body.appendChild(img2);
 40 }, 0);
 41 });
 42}, 'Cache returns stale resource');
 43
 44var img = document.createElement("img");
 45img.src = "resources/stale-image.py?token=" + request_token;
 46img.id = "firstimage";
 47document.body.appendChild(img);
 48</script>
 49</body>

LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/stale-script-expected.txt

 1
 2PASS Cache returns stale resource
 3

LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/stale-script.html

 1<!DOCTYPE html>
 2<meta charset="utf-8">
 3<title>Tests Stale While Revalidate works for scripts</title>
 4<script src="/resources/testharness.js"></script>
 5<script src="/resources/testharnessreport.js"></script>
 6<script src="/common/utils.js"></script>
 7<body>
 8<script>
 9var last_modified;
 10var last_modified_count = 0;
 11var request_token = token();
 12
 13// The script will call report via a uniquely generated ID on the subresource.
 14// If it is a cache hit the ID will be the same and the test will pass.
 15function report(mod) {
 16 if (!last_modified) {
 17 last_modified = mod;
 18 last_modified_count = 1;
 19 } else if (last_modified == mod) {
 20 last_modified_count++;
 21 }
 22}
 23
 24async_test(t => {
 25 window.onload = t.step_func(() => {
 26 step_timeout(() => {
 27 var script = document.createElement("script");
 28 script.src = "resources/stale-script.py?token=" + request_token;
 29 document.body.appendChild(script);
 30 script.onload = t.step_func(() => {
 31 assert_equals(last_modified_count, 2, "last modified");
 32 var checkResult = () => {
 33 // We poll because we don't know when the revalidation will occur.
 34 fetch("resources/stale-script.py?query&token=" + request_token).then(t.step_func((response) => {
 35 var count = response.headers.get("Count");
 36 if (count == '2') {
 37 t.done();
 38 } else {
 39 t.step_timeout(checkResult, 25);
 40 }
 41 }));
 42 };
 43 t.step_timeout(checkResult, 25);
 44 });
 45 }, 0);
 46 });
 47}, 'Cache returns stale resource');
 48
 49var script = document.createElement("script");
 50script.src = "resources/stale-script.py?token=" + request_token;
 51document.body.appendChild(script);
 52</script>
 53</body>

LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/sw-intercept.js

 1async function broadcast(msg) {
 2 for (const client of await clients.matchAll()) {
 3 client.postMessage(msg);
 4 }
 5}
 6
 7self.addEventListener('fetch', event => {
 8 event.waitUntil(broadcast(event.request.url));
 9 event.respondWith(fetch(event.request));
 10});
 11
 12self.addEventListener('activate', event => {
 13 self.clients.claim();
 14});

LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/w3c-import.log

 1The tests in this directory were imported from the W3C repository.
 2Do NOT modify these tests directly in WebKit.
 3Instead, create a pull request on the WPT github:
 4 https://github.com/web-platform-tests/wpt
 5
 6Then run the Tools/Scripts/import-w3c-tests in WebKit to reimport
 7
 8Do NOT modify or remove this file.
 9
 10------------------------------------------------------------------------
 11Properties requiring vendor prefixes:
 12None
 13Property values requiring vendor prefixes:
 14None
 15------------------------------------------------------------------------
 16List of files:
 17/LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/fetch-sw.https.html
 18/LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/fetch.html
 19/LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/stale-css.html
 20/LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/stale-image.html
 21/LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/stale-script.html
 22/LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/sw-intercept.js

LayoutTests/platform/ios-wk1/TestExpectations

@@webkit.org/b/188762 fast/writing-mode/english-rl-text-with-spelling-marker.html
19981998inspector/canvas/recording-html-2d.html
19991999
20002000webkit.org/b/159724 [ Debug ] imported/w3c/web-platform-tests/xhr/send-redirect-post-upload.htm [ Skip ]
 2001
 2002# Stale-while-revalidate is not supported on WK1
 2003imported/w3c/web-platform-tests/fetch/stale-while-revalidate [ Skip ]

LayoutTests/platform/mac-wk1/TestExpectations

@@webkit.org/b/199242 [ Debug ] http/tests/performance/performance-resource-timing
791791[ Mojave+ ] fast/images/animated-heics-verify.html [ Skip ]
792792
793793webkit.org/b/200002 [ Mojave+ Debug ] imported/blink/storage/indexeddb/blob-basics-metadata.html [ Timeout ]
 794
 795# Stale-while-revalidate is not supported on WK1
 796imported/w3c/web-platform-tests/fetch/stale-while-revalidate [ Skip ]