Diff
Modified: trunk/LayoutTests/ChangeLog (266698 => 266699)
--- trunk/LayoutTests/ChangeLog 2020-09-07 12:37:37 UTC (rev 266698)
+++ trunk/LayoutTests/ChangeLog 2020-09-07 15:51:25 UTC (rev 266699)
@@ -1,5 +1,19 @@
2020-09-07 Youenn Fablet <[email protected]>
+ Safari takes too long to fetch images from memory cache
+ https://bugs.webkit.org/show_bug.cgi?id=216048
+ <rdar://problem/68260952>
+
+ Reviewed by Antti Koivisto.
+
+ * http/wpt/html/dom/elements/images/bypass-cache-redirection-revalidation-expected.txt: Added.
+ * http/wpt/html/dom/elements/images/bypass-cache-redirection-revalidation.html: Added.
+ * http/wpt/html/dom/elements/images/hover-image-change-expected.html: Added.
+ * http/wpt/html/dom/elements/images/hover-image-change.html: Added.
+ * http/wpt/html/dom/elements/images/redirect.py: Added.
+
+2020-09-07 Youenn Fablet <[email protected]>
+
Add missing members to RTCIceCandidate
https://bugs.webkit.org/show_bug.cgi?id=216075
Added: trunk/LayoutTests/http/wpt/html/dom/elements/images/bypass-cache-redirection-revalidation-expected.txt (0 => 266699)
--- trunk/LayoutTests/http/wpt/html/dom/elements/images/bypass-cache-redirection-revalidation-expected.txt (rev 0)
+++ trunk/LayoutTests/http/wpt/html/dom/elements/images/bypass-cache-redirection-revalidation-expected.txt 2020-09-07 15:51:25 UTC (rev 266699)
@@ -0,0 +1,5 @@
+
+
+
+PASS Images can bypass no-store redirections
+
Added: trunk/LayoutTests/http/wpt/html/dom/elements/images/bypass-cache-redirection-revalidation.html (0 => 266699)
--- trunk/LayoutTests/http/wpt/html/dom/elements/images/bypass-cache-redirection-revalidation.html (rev 0)
+++ trunk/LayoutTests/http/wpt/html/dom/elements/images/bypass-cache-redirection-revalidation.html 2020-09-07 15:51:25 UTC (rev 266699)
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<title>Cached images can bypass revalidation, including redirections</title>
+<script src=""
+<script src=""
+<script src=""
+<div id="imageDiv1"></div>
+<div id="imageDiv2"></div>
+<div id="imageDiv3"></div>
+<canvas id="canvas"></canvas>
+<script>
+
+function getImagePixel(image)
+{
+ canvas.getContext("2d").drawImage(image, 0, 0, 10, 10);
+ return canvas.getContext("2d").getImageData(0, 0, 1, 1).data;
+}
+
+function computeRedirectionURL(finalURL)
+{
+ return "redirect.py?status=302&location="+ encodeURIComponent(finalURL);
+}
+
+let resolve;
+promise_test(async (t) => {
+ const url = "" + token());
+
+ let promise = new Promise(r => resolve = r);
+ imageDiv1.innerHTML = `<img src="" _onload_="resolve()"></img>`;
+ await promise;
+
+ const url2 = computeRedirectionURL("/html/dom/elements/images/image.py?id=" + token());
+ promise = new Promise(r => resolve = r);
+ imageDiv1.innerHTML = `<img src="" _onload_="resolve()"></img>`;
+ await promise;
+
+ promise = new Promise(r => resolve = r);
+ imageDiv3.innerHTML = `<img id="image3" src="" _onload_="resolve()"></img>`;
+ await promise;
+
+ assert_array_equals(getImagePixel(image3), [0, 255, 0, 255]);
+}, "Images can bypass no-store redirections");
+</script>
+
Added: trunk/LayoutTests/http/wpt/html/dom/elements/images/hover-image-change-expected.html (0 => 266699)
--- trunk/LayoutTests/http/wpt/html/dom/elements/images/hover-image-change-expected.html (rev 0)
+++ trunk/LayoutTests/http/wpt/html/dom/elements/images/hover-image-change-expected.html 2020-09-07 15:51:25 UTC (rev 266699)
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <style>
+ #testDiv {
+ height: 100px;
+ border: 1rem solid blue;
+ background-color: rgba(0, 255, 0, 255);
+ }
+ </style>
+ </head>
+ <body>
+ <div id="testDiv">This is a test</div>
+ </body>
+</html>
Added: trunk/LayoutTests/http/wpt/html/dom/elements/images/hover-image-change.html (0 => 266699)
--- trunk/LayoutTests/http/wpt/html/dom/elements/images/hover-image-change.html (rev 0)
+++ trunk/LayoutTests/http/wpt/html/dom/elements/images/hover-image-change.html 2020-09-07 15:51:25 UTC (rev 266699)
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <link rel="match" href=""
+ <style id="style">
+ #testDiv {
+ height: 100px;
+ background: var(--background);
+ }
+
+ #testDiv:hover {
+ border: 1rem solid blue;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="testDiv" _onmouseover_="checkTestResult()">This is a test</div>
+ <script src=""
+ <script>
+ if (window.testRunner)
+ testRunner.waitUntilDone();
+
+ style.sheet.insertRule('div { --background: url(/html/dom/elements/images/image.py?id='+ token() + '); }');
+
+ async function checkTestResult()
+ {
+ if (window.testRunner)
+ testRunner.notifyDone();
+ }
+
+ window._onload_ = () => {
+ const y = document.getElementById("testDiv").getBoundingClientRect().top;
+ if (window.testRunner)
+ eventSender.mouseMoveTo(10, y + 10);
+ }
+ </script>
+ </body>
+</html>
Added: trunk/LayoutTests/http/wpt/html/dom/elements/images/redirect.py (0 => 266699)
--- trunk/LayoutTests/http/wpt/html/dom/elements/images/redirect.py (rev 0)
+++ trunk/LayoutTests/http/wpt/html/dom/elements/images/redirect.py 2020-09-07 15:51:25 UTC (rev 266699)
@@ -0,0 +1,20 @@
+def main(request, response):
+ """Simple handler that causes not-cacheable redirection.
+
+ The request should typically have two query parameters:
+ status - The status to use for the redirection. Defaults to 302.
+ location - The resource to redirect to.
+ """
+ status = 302
+ if b"status" in request.GET:
+ try:
+ status = int(request.GET.first(b"status"))
+ except ValueError:
+ pass
+
+ response.status = status
+
+ location = request.GET.first(b"location")
+
+ response.headers.set(b"Location", location)
+ response.headers.set(b"Cache-Control", "no-store")
Modified: trunk/LayoutTests/imported/w3c/ChangeLog (266698 => 266699)
--- trunk/LayoutTests/imported/w3c/ChangeLog 2020-09-07 12:37:37 UTC (rev 266698)
+++ trunk/LayoutTests/imported/w3c/ChangeLog 2020-09-07 15:51:25 UTC (rev 266699)
@@ -1,5 +1,19 @@
2020-09-07 Youenn Fablet <[email protected]>
+ Safari takes too long to fetch images from memory cache
+ https://bugs.webkit.org/show_bug.cgi?id=216048
+ <rdar://problem/68260952>
+
+ Reviewed by Antti Koivisto.
+
+ * web-platform-tests/fetch/stale-while-revalidate/stale-image.html:
+ * web-platform-tests/html/dom/elements/images/bypass-cache-revalidation-expected.txt: Added.
+ * web-platform-tests/html/dom/elements/images/bypass-cache-revalidation.html: Added.
+ * web-platform-tests/html/dom/elements/images/image.py: Added.
+ (main):
+
+2020-09-07 Youenn Fablet <[email protected]>
+
Add missing members to RTCIceCandidate
https://bugs.webkit.org/show_bug.cgi?id=216075
Modified: trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/stale-image.html (266698 => 266699)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/stale-image.html 2020-09-07 12:37:37 UTC (rev 266698)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/fetch/stale-while-revalidate/stale-image.html 2020-09-07 15:51:25 UTC (rev 266699)
@@ -10,13 +10,17 @@
an image loaded into the same document will skip cache-control headers.
See: https://html.spec.whatwg.org/#the-list-of-available-images
-->
-<iframe id="child" srcdoc=""></iframe>
+<div id="frames">
+<iframe id="child1" srcdoc=""></iframe>
+<iframe id="child2" srcdoc=""></iframe>
+<iframe id="child3" srcdoc=""></iframe>
+</div>
<script>
var request_token = token();
let image_src = "resources/stale-image.py?token=" + request_token;
-let loadImage = async () => {
+let loadImage = async (document) => {
let img = document.createElement("img");
img.src = ""
let loaded = new Promise(r => img._onload_ = r);
@@ -28,10 +32,10 @@
promise_test(async t => {
await new Promise(r => window._onload_ = r);
- let img1 = await loadImage();
+ let img1 = await loadImage(child1.contentDocument);
assert_equals(img1.width, 16, "(initial version loaded)");
- let img2 = await loadImage();
+ let img2 = await loadImage(child2.contentDocument);
assert_equals(img2.width, 16, "(stale version loaded)");
// Query the server again and again. At some point it must have received the
@@ -45,9 +49,10 @@
break;
}
- let img3 = await loadImage();
+ let img3 = await loadImage(child3.contentDocument);
assert_equals(img3.width, 256, "(revalidated version loaded)");
+ frames.innerHTML = "";
}, 'Cache returns stale resource');
</script>
Added: trunk/LayoutTests/imported/w3c/web-platform-tests/html/dom/elements/images/bypass-cache-revalidation-expected.txt (0 => 266699)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/html/dom/elements/images/bypass-cache-revalidation-expected.txt (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/html/dom/elements/images/bypass-cache-revalidation-expected.txt 2020-09-07 15:51:25 UTC (rev 266699)
@@ -0,0 +1,5 @@
+
+
+
+PASS Images can bypass no-cache
+
Added: trunk/LayoutTests/imported/w3c/web-platform-tests/html/dom/elements/images/bypass-cache-revalidation.html (0 => 266699)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/html/dom/elements/images/bypass-cache-revalidation.html (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/html/dom/elements/images/bypass-cache-revalidation.html 2020-09-07 15:51:25 UTC (rev 266699)
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<title>Cached images can bypass revalidation</title>
+<script src=""
+<script src=""
+<script src=""
+<div id="imageDiv1"></div>
+<div id="imageDiv2"></div>
+<canvas id="canvas"></canvas>
+<script>
+
+function getImagePixel(image)
+{
+ canvas.getContext("2d").drawImage(image, 0, 0, 10, 10);
+ return canvas.getContext("2d").getImageData(0, 0, 1, 1).data;
+}
+
+let resolve;
+promise_test(async (t) => {
+ const url = "" + token();
+
+ let promise = new Promise(r => resolve = r);
+ imageDiv1.innerHTML = `<img src="" _onload_="resolve()"></img>`;
+ await promise;
+
+ const url2 = "image.py?id=" + token();
+ promise = new Promise(r => resolve = r);
+ imageDiv1.innerHTML = `<img src="" _onload_="resolve()"></img>`;
+ await promise;
+
+ promise = new Promise(r => resolve = r);
+ imageDiv2.innerHTML = `<img id="image2" src="" _onload_="resolve()"></img>`;
+ await promise;
+
+ assert_array_equals(getImagePixel(image2), [0, 255, 0, 255]);
+}, "Images can bypass no-cache");
+</script>
+
Added: trunk/LayoutTests/imported/w3c/web-platform-tests/html/dom/elements/images/image.py (0 => 266699)
--- trunk/LayoutTests/imported/w3c/web-platform-tests/html/dom/elements/images/image.py (rev 0)
+++ trunk/LayoutTests/imported/w3c/web-platform-tests/html/dom/elements/images/image.py 2020-09-07 15:51:25 UTC (rev 266699)
@@ -0,0 +1,28 @@
+import os.path
+
+from wptserve.utils import isomorphic_decode
+
+def main(request, response):
+
+ key = request.GET['id']
+ alreadyServedRequest = False
+ try:
+ alreadyServedRequest = request.server.stash.take(key)
+ except (KeyError, ValueError) as e:
+ pass
+
+ if alreadyServedRequest:
+ body = open(os.path.join(os.path.dirname(isomorphic_decode(__file__)), u"../../../../images/red.png"), u"rb").read()
+ else:
+ request.server.stash.put(key, True);
+ body = open(os.path.join(os.path.dirname(isomorphic_decode(__file__)), u"../../../../images/green.png"), u"rb").read()
+ pass
+
+ response.writer.write_status(200)
+ response.writer.write_header(b"etag", "abcdef")
+ response.writer.write_header(b"content-length", len(body))
+ response.writer.write_header(b"content-type", "image/png")
+ response.writer.write_header(b"cache-control", "public, max-age=31536000, no-cache")
+ response.writer.end_headers()
+
+ response.writer.write(body)
Modified: trunk/Source/WebCore/ChangeLog (266698 => 266699)
--- trunk/Source/WebCore/ChangeLog 2020-09-07 12:37:37 UTC (rev 266698)
+++ trunk/Source/WebCore/ChangeLog 2020-09-07 15:51:25 UTC (rev 266699)
@@ -1,5 +1,29 @@
2020-09-07 Youenn Fablet <[email protected]>
+ Safari takes too long to fetch images from memory cache
+ https://bugs.webkit.org/show_bug.cgi?id=216048
+ <rdar://problem/68260952>
+
+ Reviewed by Antti Koivisto.
+
+ In case of image resource, we skip revalidation if the stored image is from the same document.
+ This is inline with https://html.spec.whatwg.org/#updating-the-image-data:list-of-available-images
+ which defines a list of available images for each Document.
+ In case fetch mode is different, we do not skip revalidation as we might otherwise bypass security checks.
+
+ Tests: http/wpt/html/dom/elements/images/bypass-cache-redirection-revalidation.html
+ http/wpt/html/dom/elements/images/hover-image-change.html
+ imported/w3c/web-platform-tests/html/dom/elements/images/bypass-cache-revalidation.html
+
+ * loader/cache/CachedImage.cpp:
+ (WebCore::CachedImage::load):
+ (WebCore::CachedImage::canSkipRevalidation const):
+ * loader/cache/CachedImage.h:
+ * loader/cache/CachedResourceLoader.cpp:
+ (WebCore::CachedResourceLoader::determineRevalidationPolicy const):
+
+2020-09-07 Youenn Fablet <[email protected]>
+
Add missing members to RTCIceCandidate
https://bugs.webkit.org/show_bug.cgi?id=216075
Modified: trunk/Source/WebCore/loader/cache/CachedImage.cpp (266698 => 266699)
--- trunk/Source/WebCore/loader/cache/CachedImage.cpp 2020-09-07 12:37:37 UTC (rev 266698)
+++ trunk/Source/WebCore/loader/cache/CachedImage.cpp 2020-09-07 15:51:25 UTC (rev 266699)
@@ -97,6 +97,7 @@
void CachedImage::load(CachedResourceLoader& loader)
{
+ m_skippingRevalidationDocument = makeWeakPtr(loader.document());
if (loader.shouldPerformImageLoad(url()))
CachedResource::load(loader);
else
@@ -732,4 +733,14 @@
return CachedResource::makeRevalidationDecision(cachePolicy);
}
+bool CachedImage::canSkipRevalidation(const CachedResourceLoader& loader, const CachedResourceRequest& request) const
+{
+ if (options().mode != request.options().mode || options().credentials != request.options().credentials || resourceRequest().allowCookies() != request.resourceRequest().allowCookies())
+ return false;
+
+ // Skip revalidation as per https://html.spec.whatwg.org/#ignore-higher-layer-caching which defines a per-document image list.
+ // This rule is loosely implemented by other browsers, we could relax it and should update it once memory cache is properly specified.
+ return m_skippingRevalidationDocument && loader.document() == m_skippingRevalidationDocument;
+}
+
} // namespace WebCore
Modified: trunk/Source/WebCore/loader/cache/CachedImage.h (266698 => 266699)
--- trunk/Source/WebCore/loader/cache/CachedImage.h 2020-09-07 12:37:37 UTC (rev 266698)
+++ trunk/Source/WebCore/loader/cache/CachedImage.h 2020-09-07 15:51:25 UTC (rev 266699)
@@ -95,6 +95,7 @@
void setForceUpdateImageDataEnabledForTesting(bool enabled) { m_forceUpdateImageDataEnabledForTesting = enabled; }
bool stillNeedsLoad() const override { return !errorOccurred() && status() == Unknown && !isLoading(); }
+ bool canSkipRevalidation(const CachedResourceLoader&, const CachedResourceRequest&) const;
private:
void clear();
@@ -187,6 +188,8 @@
MonotonicTime m_lastUpdateImageDataTime;
+ WeakPtr<Document> m_skippingRevalidationDocument;
+
static constexpr unsigned maxUpdateImageDataCount = 4;
unsigned m_updateImageDataCount : 3;
bool m_isManuallyCached : 1;
Modified: trunk/Source/WebCore/loader/cache/CachedResourceLoader.cpp (266698 => 266699)
--- trunk/Source/WebCore/loader/cache/CachedResourceLoader.cpp 2020-09-07 12:37:37 UTC (rev 266698)
+++ trunk/Source/WebCore/loader/cache/CachedResourceLoader.cpp 2020-09-07 15:51:25 UTC (rev 266699)
@@ -1169,6 +1169,9 @@
return Use;
ASSERT(!existingResource->validationInProgress());
+ if (is<CachedImage>(*existingResource) && downcast<CachedImage>(*existingResource).canSkipRevalidation(*this, cachedResourceRequest))
+ return Use;
+
auto cachePolicy = this->cachePolicy(type, request.url());
// Validate the redirect chain.