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

diff --git a/service-workers/service-worker/fetch-error-worker.js b/service-workers/service-worker/fetch-error-worker.js
new file mode 100644
index 0000000..788252c
--- /dev/null
+++ b/service-workers/service-worker/fetch-error-worker.js
@@ -0,0 +1,22 @@
+importScripts("/resources/testharness.js");
+
+function doTest(event)
+{
+    if (!event.request.url.includes("fetch-error-test"))
+        return;
+
+    let counter = 0;
+    const stream = new ReadableStream({ pull: controller => {
+        switch (++counter) {
+        case 1:
+            controller.enqueue(new Uint8Array([1]));
+            return;
+        default:
+            // We asynchronously error the stream so that there is ample time to resolve the fetch promise and call text() on the response.
+            step_timeout(() => controller.error("Sorry"), 50);
+        }
+    }});
+    event.respondWith(new Response(stream));
+}
+
+self.addEventListener("fetch", doTest);
diff --git a/service-workers/service-worker/fetch-error.https.html b/service-workers/service-worker/fetch-error.https.html
new file mode 100644
index 0000000..901c158
--- /dev/null
+++ b/service-workers/service-worker/fetch-error.https.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/service-workers/service-worker/resources/test-helpers.sub.js"></script>
+</head>
+<body>
+<script>
+var scope = "resources";
+var registration;
+
+promise_test(async (test) => {
+    registration = await navigator.serviceWorker.register("fetch-error-worker.js", { scope : scope });
+    var activeWorker = registration.active;
+    if (activeWorker)
+        return;
+    activeWorker = registration.installing;
+    return new Promise(resolve => {
+        activeWorker.addEventListener('statechange', () => {
+            if (activeWorker.state === "activated")
+                resolve();
+        });
+    });
+}, "Setup service worker");
+
+promise_test(async (test) => {
+    const iframe = await with_iframe(scope);
+
+    const response = await iframe.contentWindow.fetch("fetch-error-test");
+    await response.text().then(assert_unreached, (error) => { assert_true(error.message.includes("Sorry")); });
+    iframe.remove();
+}, "Make sure a load that makes progress does not time out");
+
+promise_test(async () => {
+    registration.unregister();
+}, "Unregister service worker");
+</script>
+</body>
+</html>