WebKit export of https://bugs.webkit.org/show_bug.cgi?id=216048 (#25380)

diff --git a/fetch/stale-while-revalidate/stale-image.html b/fetch/stale-while-revalidate/stale-image.html
index f90e293..d86bdfb 100644
--- a/fetch/stale-while-revalidate/stale-image.html
+++ b/fetch/stale-while-revalidate/stale-image.html
@@ -10,13 +10,14 @@
 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>
+<iframe id="child1" srcdoc=""></iframe>
+<iframe id="child2" srcdoc=""></iframe>
 <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 = image_src;
   let loaded = new Promise(r => img.onload = r);
@@ -28,10 +29,10 @@
 promise_test(async t => {
   await new Promise(r => window.onload = r);
 
-  let img1 = await loadImage();
+  let img1 = await loadImage(document);
   assert_equals(img1.width, 16, "(initial version loaded)");
 
-  let img2 = await loadImage();
+  let img2 = await loadImage(child1.contentDocument);
   assert_equals(img2.width, 16, "(stale version loaded)");
 
   // Query the server again and again. At some point it must have received the
@@ -45,7 +46,7 @@
       break;
   }
 
-  let img3 = await loadImage();
+  let img3 = await loadImage(child2.contentDocument);
   assert_equals(img3.width, 256, "(revalidated version loaded)");
 
 }, 'Cache returns stale resource');
diff --git a/html/dom/elements/images/bypass-cache-revalidation.html b/html/dom/elements/images/bypass-cache-revalidation.html
new file mode 100644
index 0000000..38cdd87
--- /dev/null
+++ b/html/dom/elements/images/bypass-cache-revalidation.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<title>Cached images can bypass revalidation</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/utils.js"></script>
+<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 = "image.py?id=" + token();
+
+   let promise = new Promise(r => resolve = r);
+   imageDiv1.innerHTML = `<img src="${url}" onload="resolve()"></img>`;
+   await promise;
+
+   const url2 = "image.py?id=" + token();
+   promise = new Promise(r => resolve = r);
+   imageDiv1.innerHTML = `<img src="${url2}" onload="resolve()"></img>`;
+   await promise;
+
+   promise = new Promise(r => resolve = r);
+   imageDiv2.innerHTML = `<img id="image2" src="${url}" onload="resolve()"></img>`;
+   await promise;
+
+   assert_array_equals(getImagePixel(image2), [0, 255, 0, 255]);
+}, "Images can bypass no-cache");
+</script>
+
diff --git a/html/dom/elements/images/image.py b/html/dom/elements/images/image.py
new file mode 100644
index 0000000..1aec081
--- /dev/null
+++ b/html/dom/elements/images/image.py
@@ -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)