Bug 1140791 - Run fetch tests on main thread and workers. r=ehsan
authorNikhil Marathe <nsm.nikhil@gmail.com>
Wed, 11 Mar 2015 14:48:58 -0700
changeset 233850 e50a44b381ff26175488a319339e0b1f6af7fd19
parent 233849 84e12f76d30974fc2a1eb1c4b7de57f3a364b26f
child 233851 0b91bd12fb793ad78b1b7c00f1100e40519e8753
push id56976
push usernsm.nikhil@gmail.com
push dateMon, 16 Mar 2015 20:41:53 +0000
treeherdermozilla-inbound@e50a44b381ff [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan
bugs1140791
milestone39.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1140791 - Run fetch tests on main thread and workers. r=ehsan
dom/tests/mochitest/fetch/fetch_test_framework.js
dom/tests/mochitest/fetch/mochitest.ini
dom/tests/mochitest/fetch/test_fetch_basic.html
dom/tests/mochitest/fetch/test_fetch_basic.js
dom/tests/mochitest/fetch/test_fetch_basic_http.html
dom/tests/mochitest/fetch/test_fetch_basic_http.js
dom/tests/mochitest/fetch/test_fetch_basic_worker.html
dom/tests/mochitest/fetch/test_fetch_cors.html
dom/tests/mochitest/fetch/test_fetch_cors.js
dom/tests/mochitest/fetch/test_headers.html
dom/tests/mochitest/fetch/test_headers_common.js
dom/tests/mochitest/fetch/test_headers_mainthread.html
dom/tests/mochitest/fetch/test_headers_mainthread.js
dom/tests/mochitest/fetch/test_request.html
dom/tests/mochitest/fetch/test_request.js
dom/tests/mochitest/fetch/test_response.html
dom/tests/mochitest/fetch/test_response.js
dom/tests/mochitest/fetch/worker_test_fetch_basic.js
dom/tests/mochitest/fetch/worker_test_fetch_basic_http.js
dom/tests/mochitest/fetch/worker_test_fetch_cors.js
dom/tests/mochitest/fetch/worker_wrapper.js
dom/workers/moz.build
dom/workers/test/fetch/mochitest.ini
dom/workers/test/fetch/test_interfaces.html
dom/workers/test/fetch/test_request.html
dom/workers/test/fetch/test_response.html
dom/workers/test/fetch/worker_interfaces.js
dom/workers/test/fetch/worker_test_request.js
dom/workers/test/fetch/worker_test_response.js
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/fetch/fetch_test_framework.js
@@ -0,0 +1,86 @@
+function testScript(script) {
+  function workerTest() {
+    return new Promise(function(resolve, reject) {
+      var worker = new Worker("worker_wrapper.js");
+      worker.onmessage = function(event) {
+        if (event.data.type == 'finish') {
+          resolve();
+        } else if (event.data.type == 'status') {
+          ok(event.data.status, "Worker fetch test: " + event.data.msg);
+        }
+      }
+      worker.onerror = function(event) {
+        reject("Worker error: " + event.message);
+      };
+
+      worker.postMessage({ "script": script });
+    });
+  }
+
+  function windowTest() {
+    return new Promise(function(resolve, reject) {
+      var scriptEl = document.createElement("script");
+      scriptEl.setAttribute("src", script);
+      scriptEl.onload = function() {
+        runTest().then(resolve, reject);
+      };
+      document.body.appendChild(scriptEl);
+    });
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  // We have to run the window and worker tests sequentially since some tests
+  // set and compare cookies and running in parallel can lead to conflicting
+  // values.
+  windowTest()
+    .then(function() {
+      return workerTest();
+    })
+    .catch(function(e) {
+      ok(false, "Some test failed in " + script);
+      info(e);
+      info(e.message);
+      return Promise.resolve();
+    })
+    .then(function() {
+      SimpleTest.finish();
+    });
+}
+
+// Utilities
+// =========
+
+// Helper that uses FileReader or FileReaderSync based on context and returns
+// a Promise that resolves with the text or rejects with error.
+function readAsText(blob) {
+  if (typeof FileReader !== "undefined") {
+    return new Promise(function(resolve, reject) {
+      var fs = new FileReader();
+      fs.onload = function() {
+        resolve(fs.result);
+      }
+      fs.onerror = reject;
+      fs.readAsText(blob);
+    });
+  } else {
+    var fs = new FileReaderSync();
+    return Promise.resolve(fs.readAsText(blob));
+  }
+}
+
+function readAsArrayBuffer(blob) {
+  if (typeof FileReader !== "undefined") {
+    return new Promise(function(resolve, reject) {
+      var fs = new FileReader();
+      fs.onload = function() {
+        resolve(fs.result);
+      }
+      fs.onerror = reject;
+      fs.readAsArrayBuffer(blob);
+    });
+  } else {
+    var fs = new FileReaderSync();
+    return Promise.resolve(fs.readAsArrayBuffer(blob));
+  }
+}
+
--- a/dom/tests/mochitest/fetch/mochitest.ini
+++ b/dom/tests/mochitest/fetch/mochitest.ini
@@ -1,13 +1,18 @@
 [DEFAULT]
 support-files =
+  fetch_test_framework.js
+  test_fetch_basic.js
+  test_fetch_basic_http.js
+  test_fetch_cors.js
   test_headers_common.js
-  test_headers_mainthread.js
-  worker_test_fetch_basic.js
-  worker_test_fetch_basic_http.js
-  worker_test_fetch_cors.js
+  test_request.js
+  test_response.js
   worker_wrapper.js
 
 [test_headers.html]
+[test_headers_mainthread.html]
 [test_fetch_basic.html]
 [test_fetch_basic_http.html]
 [test_fetch_cors.html]
+[test_request.html]
+[test_response.html]
--- a/dom/tests/mochitest/fetch/test_fetch_basic.html
+++ b/dom/tests/mochitest/fetch/test_fetch_basic.html
@@ -8,46 +8,15 @@
   <title>Bug 1039846 - Test fetch() function in worker</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 <pre id="test"></pre>
-<script type="text/javascript" src="worker_test_fetch_basic.js"> </script>
+<script type="text/javascript" src="fetch_test_framework.js"> </script>
 <script class="testbody" type="text/javascript">
-SimpleTest.waitForExplicitFinish();
-
-function testOnWorker(done) {
-  ok(true, "=== Start Worker Tests ===");
-  var worker = new Worker("worker_test_fetch_basic.js");
-  worker.onmessage = function(event) {
-    if (event.data.type == "finish") {
-      ok(true, "=== Finish Worker Tests ===");
-      done();
-    } else if (event.data.type == "status") {
-      ok(event.data.status, event.data.msg);
-    }
-  }
-
-  worker.onerror = function(event) {
-    ok(false, "Worker had an error: " + event.data);
-    ok(true, "=== Finish Worker Tests ===");
-    done();
-  };
-
-  worker.postMessage("start");
-}
-
-//
-// Driver
-//
-
-testOnWorker(function() {
-  SimpleTest.finish();
-});
+testScript("test_fetch_basic.js");
 </script>
-</script>
-</pre>
 </body>
 </html>
 
rename from dom/tests/mochitest/fetch/worker_test_fetch_basic.js
rename to dom/tests/mochitest/fetch/test_fetch_basic.js
--- a/dom/tests/mochitest/fetch/worker_test_fetch_basic.js
+++ b/dom/tests/mochitest/fetch/test_fetch_basic.js
@@ -1,22 +1,8 @@
-if (typeof ok !== "function") {
-  function ok(a, msg) {
-    dump("OK: " + !!a + "  =>  " + a + " " + msg + "\n");
-    postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
-  }
-}
-
-if (typeof is !== "function") {
-  function is(a, b, msg) {
-    dump("IS: " + (a===b) + "  =>  " + a + " | " + b + " " + msg + "\n");
-    postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
-  }
-}
-
 function testAboutURL() {
   var p1 = fetch('about:blank').then(function(res) {
     is(res.status, 200, "about:blank should load a valid Response");
     is(res.headers.get('content-type'), 'text/html;charset=utf-8',
        "about:blank content-type should be text/html;charset=utf-8");
     return res.text().then(function(v) {
       is(v, "", "about:blank body should be empty");
     });
@@ -67,29 +53,14 @@ function testSameOriginBlobURL() {
     is(parseInt(res.headers.get("content-length")), 16, "Content-Length should match Blob's size");
     return res.text().then(function(body) {
       is(body, "english sentence", "Blob fetch body should match");
     });
   });
 }
 
 function runTest() {
-  var done = function() {
-    if (typeof SimpleTest === "object") {
-      SimpleTest.finish();
-    } else {
-      postMessage({ type: 'finish' });
-    }
-  }
-
-  Promise.resolve()
+  return Promise.resolve()
     .then(testAboutURL)
     .then(testDataURL)
     .then(testSameOriginBlobURL)
     // Put more promise based tests here.
-    .then(done)
-    .catch(function(e) {
-      ok(false, "Some Response tests failed " + e);
-      done();
-    })
 }
-
-onmessage = runTest;
--- a/dom/tests/mochitest/fetch/test_fetch_basic_http.html
+++ b/dom/tests/mochitest/fetch/test_fetch_basic_http.html
@@ -8,46 +8,15 @@
   <title>Bug 1039846 - Test fetch() http fetching in worker</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 <pre id="test"></pre>
-<script type="text/javascript" src="worker_test_fetch_basic.js"> </script>
+<script type="text/javascript" src="fetch_test_framework.js"> </script>
 <script class="testbody" type="text/javascript">
-SimpleTest.waitForExplicitFinish();
-
-function testOnWorker(done) {
-  ok(true, "=== Start Worker Tests ===");
-  var worker = new Worker("worker_test_fetch_basic_http.js");
-  worker.onmessage = function(event) {
-    if (event.data.type == "finish") {
-      ok(true, "=== Finish Worker Tests ===");
-      done();
-    } else if (event.data.type == "status") {
-      ok(event.data.status, event.data.msg);
-    }
-  }
-
-  worker.onerror = function(event) {
-    ok(false, "Worker had an error: " + event.message);
-    ok(true, "=== Finish Worker Tests ===");
-    done();
-  };
-
-  worker.postMessage("start");
-}
-
-//
-// Driver
-//
-
-testOnWorker(function() {
-  SimpleTest.finish();
-});
+testScript("test_fetch_basic_http.js");
 </script>
-</script>
-</pre>
 </body>
 </html>
 
rename from dom/tests/mochitest/fetch/worker_test_fetch_basic_http.js
rename to dom/tests/mochitest/fetch/test_fetch_basic_http.js
--- a/dom/tests/mochitest/fetch/worker_test_fetch_basic_http.js
+++ b/dom/tests/mochitest/fetch/test_fetch_basic_http.js
@@ -1,20 +1,8 @@
-if (typeof ok !== "function") {
-  function ok(a, msg) {
-    postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
-  }
-}
-
-if (typeof is !== "function") {
-  function is(a, b, msg) {
-    postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
-  }
-}
-
 var path = "/tests/dom/base/test/";
 
 var passFiles = [['file_XHR_pass1.xml', 'GET', 200, 'OK', 'text/xml'],
                  ['file_XHR_pass2.txt', 'GET', 200, 'OK', 'text/plain'],
                  ['file_XHR_pass3.txt', 'GET', 200, 'OK', 'text/plain'],
                  ];
 
 function testURL() {
@@ -128,45 +116,30 @@ function testResponses() {
   return Promise.all(fetches);
 }
 
 function testBlob() {
   return fetch(path + '/file_XHR_binary2.bin').then((r) => {
     ok(r.status, 200, "status should match");
     return r.blob().then((b) => {
       ok(b.size, 65536, "blob should have size 65536");
-      var frs = new FileReaderSync();
-      var buf = frs.readAsArrayBuffer(b);
-      var u8 = new Uint8Array(buf);
-      for (var i = 0; i < 65536; i++) {
-        if (u8[i] !== (i & 255)) {
-          break;
+      return readAsArrayBuffer(b).then(function(ab) {
+        var u8 = new Uint8Array(ab);
+        for (var i = 0; i < 65536; i++) {
+          if (u8[i] !== (i & 255)) {
+            break;
+          }
         }
-      }
-      is(i, 65536, "wrong value at offset " + i);
+        is(i, 65536, "wrong value at offset " + i);
+      });
     });
   });
 }
 
 function runTest() {
-  var done = function() {
-    if (typeof SimpleTest === "object") {
-      SimpleTest.finish();
-    } else {
-      postMessage({ type: 'finish' });
-    }
-  }
-
-  Promise.resolve()
+  return Promise.resolve()
     .then(testURL)
     .then(testURLFail)
     .then(testRequestGET)
     .then(testResponses)
     .then(testBlob)
     // Put more promise based tests here.
-    .then(done)
-    .catch(function(e) {
-      ok(false, "Some test failed " + e);
-      done();
-    });
 }
-
-onmessage = runTest;
deleted file mode 100644
--- a/dom/tests/mochitest/fetch/test_fetch_basic_worker.html
+++ /dev/null
@@ -1,44 +0,0 @@
-<!--
-  Any copyright is dedicated to the Public Domain.
-  http://creativecommons.org/publicdomain/zero/1.0/
--->
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Bug 1039846 - Test fetch() function in worker</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-<p id="display"></p>
-<div id="content" style="display: none"></div>
-<pre id="test"></pre>
-<script class="testbody" type="text/javascript">
-
-  function runTest() {
-    var worker = new Worker("worker_test_fetch_basic.js");
-    worker.onmessage = function(event) {
-
-      if (event.data.type == 'finish') {
-        SimpleTest.finish();
-      } else if (event.data.type == 'status') {
-        ok(event.data.status, event.data.msg);
-      }
-    }
-
-    worker.onerror = function(event) {
-      ok(false, "Worker had an error: " + event.message + " at " + event.lineno);
-      SimpleTest.finish();
-    };
-
-    worker.postMessage(true);
-  }
-
-  SimpleTest.waitForExplicitFinish();
-
-  runTest();
-</script>
-</pre>
-</body>
-</html>
-
--- a/dom/tests/mochitest/fetch/test_fetch_cors.html
+++ b/dom/tests/mochitest/fetch/test_fetch_cors.html
@@ -8,46 +8,15 @@
   <title>Bug 1039846 - Test fetch() CORS mode</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 <pre id="test"></pre>
+<script type="text/javascript" src="fetch_test_framework.js"> </script>
 <script class="testbody" type="text/javascript">
-SimpleTest.waitForExplicitFinish();
-
-var worker;
-function testOnWorker(done) {
-  ok(true, "=== Start Worker Tests ===");
-  worker = new Worker("worker_test_fetch_cors.js");
-  worker.onmessage = function(event) {
-    if (event.data.type == "finish") {
-      ok(true, "=== Finish Worker Tests ===");
-      done();
-    } else if (event.data.type == "status") {
-      ok(event.data.status, event.data.msg);
-    }
-  }
-
-  worker.onerror = function(event) {
-    ok(false, "Worker had an error: " + event.message);
-    ok(true, "=== Finish Worker Tests ===");
-    done();
-  };
-
-  worker.postMessage("start");
-}
-
-//
-// Driver
-//
-
-testOnWorker(function() {
-  SimpleTest.finish();
-});
+testScript("test_fetch_cors.js");
 </script>
-</script>
-</pre>
 </body>
 </html>
 
rename from dom/tests/mochitest/fetch/worker_test_fetch_cors.js
rename to dom/tests/mochitest/fetch/test_fetch_cors.js
--- a/dom/tests/mochitest/fetch/worker_test_fetch_cors.js
+++ b/dom/tests/mochitest/fetch/test_fetch_cors.js
@@ -1,20 +1,8 @@
-if (typeof ok !== "function") {
-  function ok(a, msg) {
-    postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
-  }
-}
-
-if (typeof is !== "function") {
-  function is(a, b, msg) {
-    postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
-  }
-}
-
 var path = "/tests/dom/base/test/";
 
 function isOpaqueResponse(response) {
   return response.type == "opaque" && response.status === 0 && response.statusText === "";
 }
 
 function testModeSameOrigin() {
   // Fetch spec Section 4, step 4, "request's mode is same-origin".
@@ -1256,34 +1244,19 @@ function testRedirects() {
       });
     })(request, test));
   }
 
   return Promise.all(fetches);
 }
 
 function runTest() {
-  var done = function() {
-    if (typeof SimpleTest === "object") {
-      SimpleTest.finish();
-    } else {
-      postMessage({ type: 'finish' });
-    }
-  }
-
   testNoCorsCtor();
 
-  Promise.resolve()
+  return Promise.resolve()
     .then(testModeSameOrigin)
     .then(testModeNoCors)
     .then(testModeCors)
     .then(testSameOriginCredentials)
     .then(testCrossOriginCredentials)
     .then(testRedirects)
     // Put more promise based tests here.
-    .then(done)
-    .catch(function(e) {
-      ok(false, "Some test failed " + e);
-      done();
-    });
 }
-
-onmessage = runTest;
--- a/dom/tests/mochitest/fetch/test_headers.html
+++ b/dom/tests/mochitest/fetch/test_headers.html
@@ -3,61 +3,14 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Test Fetch Headers - Basic</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
+<script type="text/javascript" src="fetch_test_framework.js"> </script>
 <script class="testbody" type="text/javascript">
-SimpleTest.waitForExplicitFinish();
-
-function testOnWorker(done) {
-  ok(true, "=== Start Worker Headers Tests ===");
-  var worker = new Worker("worker_wrapper.js");
-  worker.onmessage = function(event) {
-    if (event.data.type == "finish") {
-      ok(true, "=== Finish Worker Headers Tests ===");
-      done();
-    } else if (event.data.type == "status") {
-      ok(event.data.status, event.data.msg);
-    }
-  }
-
-  worker.onerror = function(event) {
-    ok(false, "Worker had an error: " + event.data);
-    ok(true, "=== Finish Worker Headers Tests ===");
-    done();
-  };
-
-  worker.postMessage({ script: "test_headers_common.js" });
-}
-
-function testOnMainThread(done) {
-  ok(true, "=== Start Main Thread Headers Tests ===");
-  var commonScript = document.createElement("script");
-  commonScript.setAttribute("src", "test_headers_common.js");
-  commonScript.onload = function() {
-    var mainThreadScript = document.createElement("script");
-    mainThreadScript.setAttribute("src", "test_headers_mainthread.js");
-    mainThreadScript.onload = function() {
-      ok(true, "=== Finish Main Thread Headers Tests ===");
-      done();
-    }
-    document.head.appendChild(mainThreadScript);
-  };
-  document.head.appendChild(commonScript);
-}
-
-
-//
-// Driver
-//
-
-testOnMainThread(function() {
-  testOnWorker(function() {
-    SimpleTest.finish();
-  });
-});
+testScript("test_headers_common.js");
 </script>
 </body>
 </html>
--- a/dom/tests/mochitest/fetch/test_headers_common.js
+++ b/dom/tests/mochitest/fetch/test_headers_common.js
@@ -164,10 +164,13 @@ function TestFilledHeaders() {
   shouldThrow(function() {
     filled = new Headers([
       ["zxy"],
       ["uts", "321"]
     ]);
   }, TypeError, "Fill with non-tuple sequence should throw TypeError.");
 }
 
-TestEmptyHeaders();
-TestFilledHeaders();
+function runTest() {
+  TestEmptyHeaders();
+  TestFilledHeaders();
+  return Promise.resolve();
+}
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/fetch/test_headers_mainthread.html
@@ -0,0 +1,157 @@
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test Fetch Headers - Basic</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script type="text/javascript" src="test_headers_common.js"> </script>
+<script type="text/javascript">
+// Main thread specific tests because they need SpecialPowers.  Expects
+// test_headers_common.js to already be loaded.
+
+function TestRequestHeaders() {
+  is(typeof Headers, "function", "Headers global constructor exists.");
+  var headers = new Headers();
+  ok(headers, "Constructed empty Headers object");
+  SpecialPowers.wrap(headers).guard = "request";
+  TestCoreBehavior(headers, "foo");
+  var forbidden = [
+    "Accept-Charset",
+    "Accept-Encoding",
+    "Access-Control-Request-Headers",
+    "Access-Control-Request-Method",
+    "Connection",
+    "Content-Length",
+    "Cookie",
+    "Cookie2",
+    "Date",
+    "DNT",
+    "Expect",
+    "Host",
+    "Keep-Alive",
+    "Origin",
+    "Referer",
+    "TE",
+    "Trailer",
+    "Transfer-Encoding",
+    "Upgrade",
+    "User-Agent",
+    "Via",
+    "Proxy-Authorization",
+    "Proxy-blarg",
+    "Proxy-",
+    "Sec-foo",
+    "Sec-"
+  ];
+
+  for (var i = 0, n = forbidden.length; i < n; ++i) {
+    var name = forbidden[i];
+    headers.append(name, "hmm");
+    checkNotHas(headers, name, "Should not be able to append " + name + " to request headers");
+    headers.set(name, "hmm");
+    checkNotHas(headers, name, "Should not be able to set " + name + " on request headers");
+  }
+}
+
+function TestRequestNoCorsHeaders() {
+  is(typeof Headers, "function", "Headers global constructor exists.");
+  var headers = new Headers();
+  ok(headers, "Constructed empty Headers object");
+  SpecialPowers.wrap(headers).guard = "request-no-cors";
+
+  headers.append("foo", "bar");
+  checkNotHas(headers, "foo", "Should not be able to append arbitrary headers to request-no-cors headers.");
+  headers.set("foo", "bar");
+  checkNotHas(headers, "foo", "Should not be able to set arbitrary headers on request-no-cors headers.");
+
+  var simpleNames = [
+    "Accept",
+    "Accept-Language",
+    "Content-Language"
+  ];
+
+  var simpleContentTypes = [
+    "application/x-www-form-urlencoded",
+    "multipart/form-data",
+    "text/plain",
+    "application/x-www-form-urlencoded; charset=utf-8",
+    "multipart/form-data; charset=utf-8",
+    "text/plain; charset=utf-8"
+  ];
+
+  for (var i = 0, n = simpleNames.length; i < n; ++i) {
+    var name = simpleNames[i];
+    headers.append(name, "hmm");
+    checkHas(headers, name, "Should be able to append " + name + " to request-no-cors headers");
+    headers.set(name, "hmm");
+    checkHas(headers, name, "Should be able to set " + name + " on request-no-cors headers");
+  }
+
+  for (var i = 0, n = simpleContentTypes.length; i < n; ++i) {
+    var value = simpleContentTypes[i];
+    headers.append("Content-Type", value);
+    checkHas(headers, "Content-Type", "Should be able to append " + value + " Content-Type to request-no-cors headers");
+    headers.delete("Content-Type");
+    headers.set("Content-Type", value);
+    checkHas(headers, "Content-Type", "Should be able to set " + value + " Content-Type on request-no-cors headers");
+  }
+}
+
+function TestResponseHeaders() {
+  is(typeof Headers, "function", "Headers global constructor exists.");
+  var headers = new Headers();
+  ok(headers, "Constructed empty Headers object");
+  SpecialPowers.wrap(headers).guard = "response";
+  TestCoreBehavior(headers, "foo");
+  var forbidden = [
+    "Set-Cookie",
+    "Set-Cookie2"
+  ];
+
+  for (var i = 0, n = forbidden.length; i < n; ++i) {
+    var name = forbidden[i];
+    headers.append(name, "hmm");
+    checkNotHas(headers, name, "Should not be able to append " + name + " to response headers");
+    headers.set(name, "hmm");
+    checkNotHas(headers, name, "Should not be able to set " + name + " on response headers");
+  }
+}
+
+function TestImmutableHeaders() {
+  is(typeof Headers, "function", "Headers global constructor exists.");
+  var headers = new Headers();
+  ok(headers, "Constructed empty Headers object");
+  TestCoreBehavior(headers, "foo");
+  headers.append("foo", "atleastone");
+
+  SpecialPowers.wrap(headers).guard = "immutable";
+
+  shouldThrow(function() {
+    headers.append("foo", "wat");
+  }, TypeError, "Should not be able to append to immutable headers");
+
+  shouldThrow(function() {
+    headers.set("foo", "wat");
+  }, TypeError, "Should not be able to set immutable headers");
+
+  shouldThrow(function() {
+    headers.delete("foo");
+  }, TypeError, "Should not be able to delete immutable headers");
+
+  checkHas(headers, "foo", "Should be able to check immutable headers");
+  ok(headers.get("foo"), "Should be able to get immutable headers");
+  ok(headers.getAll("foo").length, "Should be able to get all immutable headers");
+}
+
+TestRequestHeaders();
+TestRequestNoCorsHeaders();
+TestResponseHeaders();
+TestImmutableHeaders();
+</script>
+</body>
+</html>
+
deleted file mode 100644
--- a/dom/tests/mochitest/fetch/test_headers_mainthread.js
+++ /dev/null
@@ -1,141 +0,0 @@
-// Main thread specific tests because they need SpecialPowers.  Expects
-// test_headers_common.js to already be loaded.
-
-function TestRequestHeaders() {
-  is(typeof Headers, "function", "Headers global constructor exists.");
-  var headers = new Headers();
-  ok(headers, "Constructed empty Headers object");
-  SpecialPowers.wrap(headers).guard = "request";
-  TestCoreBehavior(headers, "foo");
-  var forbidden = [
-    "Accept-Charset",
-    "Accept-Encoding",
-    "Access-Control-Request-Headers",
-    "Access-Control-Request-Method",
-    "Connection",
-    "Content-Length",
-    "Cookie",
-    "Cookie2",
-    "Date",
-    "DNT",
-    "Expect",
-    "Host",
-    "Keep-Alive",
-    "Origin",
-    "Referer",
-    "TE",
-    "Trailer",
-    "Transfer-Encoding",
-    "Upgrade",
-    "User-Agent",
-    "Via",
-    "Proxy-Authorization",
-    "Proxy-blarg",
-    "Proxy-",
-    "Sec-foo",
-    "Sec-"
-  ];
-
-  for (var i = 0, n = forbidden.length; i < n; ++i) {
-    var name = forbidden[i];
-    headers.append(name, "hmm");
-    checkNotHas(headers, name, "Should not be able to append " + name + " to request headers");
-    headers.set(name, "hmm");
-    checkNotHas(headers, name, "Should not be able to set " + name + " on request headers");
-  }
-}
-
-function TestRequestNoCorsHeaders() {
-  is(typeof Headers, "function", "Headers global constructor exists.");
-  var headers = new Headers();
-  ok(headers, "Constructed empty Headers object");
-  SpecialPowers.wrap(headers).guard = "request-no-cors";
-
-  headers.append("foo", "bar");
-  checkNotHas(headers, "foo", "Should not be able to append arbitrary headers to request-no-cors headers.");
-  headers.set("foo", "bar");
-  checkNotHas(headers, "foo", "Should not be able to set arbitrary headers on request-no-cors headers.");
-
-  var simpleNames = [
-    "Accept",
-    "Accept-Language",
-    "Content-Language"
-  ];
-
-  var simpleContentTypes = [
-    "application/x-www-form-urlencoded",
-    "multipart/form-data",
-    "text/plain",
-    "application/x-www-form-urlencoded; charset=utf-8",
-    "multipart/form-data; charset=utf-8",
-    "text/plain; charset=utf-8"
-  ];
-
-  for (var i = 0, n = simpleNames.length; i < n; ++i) {
-    var name = simpleNames[i];
-    headers.append(name, "hmm");
-    checkHas(headers, name, "Should be able to append " + name + " to request-no-cors headers");
-    headers.set(name, "hmm");
-    checkHas(headers, name, "Should be able to set " + name + " on request-no-cors headers");
-  }
-
-  for (var i = 0, n = simpleContentTypes.length; i < n; ++i) {
-    var value = simpleContentTypes[i];
-    headers.append("Content-Type", value);
-    checkHas(headers, "Content-Type", "Should be able to append " + value + " Content-Type to request-no-cors headers");
-    headers.delete("Content-Type");
-    headers.set("Content-Type", value);
-    checkHas(headers, "Content-Type", "Should be able to set " + value + " Content-Type on request-no-cors headers");
-  }
-}
-
-function TestResponseHeaders() {
-  is(typeof Headers, "function", "Headers global constructor exists.");
-  var headers = new Headers();
-  ok(headers, "Constructed empty Headers object");
-  SpecialPowers.wrap(headers).guard = "response";
-  TestCoreBehavior(headers, "foo");
-  var forbidden = [
-    "Set-Cookie",
-    "Set-Cookie2"
-  ];
-
-  for (var i = 0, n = forbidden.length; i < n; ++i) {
-    var name = forbidden[i];
-    headers.append(name, "hmm");
-    checkNotHas(headers, name, "Should not be able to append " + name + " to response headers");
-    headers.set(name, "hmm");
-    checkNotHas(headers, name, "Should not be able to set " + name + " on response headers");
-  }
-}
-
-function TestImmutableHeaders() {
-  is(typeof Headers, "function", "Headers global constructor exists.");
-  var headers = new Headers();
-  ok(headers, "Constructed empty Headers object");
-  TestCoreBehavior(headers, "foo");
-  headers.append("foo", "atleastone");
-
-  SpecialPowers.wrap(headers).guard = "immutable";
-
-  shouldThrow(function() {
-    headers.append("foo", "wat");
-  }, TypeError, "Should not be able to append to immutable headers");
-
-  shouldThrow(function() {
-    headers.set("foo", "wat");
-  }, TypeError, "Should not be able to set immutable headers");
-
-  shouldThrow(function() {
-    headers.delete("foo");
-  }, TypeError, "Should not be able to delete immutable headers");
-
-  checkHas(headers, "foo", "Should be able to check immutable headers");
-  ok(headers.get("foo"), "Should be able to get immutable headers");
-  ok(headers.getAll("foo").length, "Should be able to get all immutable headers");
-}
-
-TestRequestHeaders();
-TestRequestNoCorsHeaders();
-TestResponseHeaders();
-TestImmutableHeaders();
rename from dom/workers/test/fetch/test_request.html
rename to dom/tests/mochitest/fetch/test_request.html
--- a/dom/workers/test/fetch/test_request.html
+++ b/dom/tests/mochitest/fetch/test_request.html
@@ -8,37 +8,15 @@
   <title>Bug XXXXXX - Test Request object in worker</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 <pre id="test"></pre>
+<script type="text/javascript" src="fetch_test_framework.js"> </script>
 <script class="testbody" type="text/javascript">
-
-  function checkEnabled() {
-    var worker = new Worker("worker_test_request.js");
-    worker.onmessage = function(event) {
-
-      if (event.data.type == 'finish') {
-        SimpleTest.finish();
-      } else if (event.data.type == 'status') {
-        ok(event.data.status, event.data.msg);
-      }
-    }
-
-    worker.onerror = function(event) {
-      ok(false, "Worker had an error: " + event.message + " at " + event.lineno);
-      SimpleTest.finish();
-    };
-
-    worker.postMessage(true);
-  }
-
-  SimpleTest.waitForExplicitFinish();
-
-  checkEnabled();
+testScript("test_request.js");
 </script>
-</pre>
 </body>
 </html>
 
rename from dom/workers/test/fetch/worker_test_request.js
rename to dom/tests/mochitest/fetch/test_request.js
--- a/dom/workers/test/fetch/worker_test_request.js
+++ b/dom/tests/mochitest/fetch/test_request.js
@@ -1,16 +1,8 @@
-function ok(a, msg) {
-  postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
-}
-
-function is(a, b, msg) {
-  postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
-}
-
 function testDefaultCtor() {
   var req = new Request("");
   is(req.method, "GET", "Default Request method is GET");
   ok(req.headers instanceof Headers, "Request should have non-null Headers object");
   is(req.url, self.location.href, "URL should be resolved with entry settings object's API base URL");
   is(req.context, "fetch", "Default context is fetch.");
   is(req.referrer, "about:client", "Default referrer is `client` which serializes to about:client.");
   is(req.mode, "cors", "Request mode for string input is cors");
@@ -266,18 +258,19 @@ function testBodyExtraction() {
   var text = "κόσμε";
   var newReq = function() { return new Request("", { method: 'post', body: text }); }
   return newReq().text().then(function(v) {
     ok(typeof v === "string", "Should resolve to string");
     is(text, v, "Extracted string should match");
   }).then(function() {
     return newReq().blob().then(function(v) {
       ok(v instanceof Blob, "Should resolve to Blob");
-      var fs = new FileReaderSync();
-      is(fs.readAsText(v), text, "Decoded Blob should match original");
+      return readAsText(v).then(function(result) {
+        is(result, text, "Decoded Blob should match original");
+      });
     });
   }).then(function() {
     return newReq().json().then(function(v) {
       ok(false, "Invalid json should reject");
     }, function(e) {
       ok(true, "Invalid json should reject");
     })
   }).then(function() {
@@ -306,31 +299,24 @@ function testModeCorsPreflightEnumValue(
       invalidExc = e;
     }
     var expectedMessage = invalidExc.message.replace(invalidMode, 'cors-with-forced-preflight');
     is(e.message, expectedMessage,
        "mode cors-with-forced-preflight should throw same error as invalid RequestMode strings.");
   }
 }
 
-onmessage = function() {
-  var done = function() { postMessage({ type: 'finish' }) }
-
+function runTest() {
   testDefaultCtor();
   testSimpleUrlParse();
   testUrlFragment();
   testMethod();
   testBug1109574();
   testModeCorsPreflightEnumValue();
 
-  Promise.resolve()
+  return Promise.resolve()
     .then(testBodyCreation)
     .then(testBodyUsed)
     .then(testBodyExtraction)
     .then(testUsedRequest)
     .then(testClone())
     // Put more promise based tests here.
-    .then(done)
-    .catch(function(e) {
-      ok(false, "Some Request tests failed " + e);
-      done();
-    })
 }
rename from dom/workers/test/fetch/test_response.html
rename to dom/tests/mochitest/fetch/test_response.html
--- a/dom/workers/test/fetch/test_response.html
+++ b/dom/tests/mochitest/fetch/test_response.html
@@ -8,37 +8,15 @@
   <title>Bug 1039846 - Test Response object in worker</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 <pre id="test"></pre>
+<script type="text/javascript" src="fetch_test_framework.js"> </script>
 <script class="testbody" type="text/javascript">
-
-  function runTest() {
-    var worker = new Worker("worker_test_response.js");
-    worker.onmessage = function(event) {
-
-      if (event.data.type == 'finish') {
-        SimpleTest.finish();
-      } else if (event.data.type == 'status') {
-        ok(event.data.status, event.data.msg);
-      }
-    }
-
-    worker.onerror = function(event) {
-      ok(false, "Worker had an error: " + event.message + " at " + event.lineno);
-      SimpleTest.finish();
-    };
-
-    worker.postMessage(true);
-  }
-
-  SimpleTest.waitForExplicitFinish();
-
-  runTest();
+testScript("test_response.js");
 </script>
-</pre>
 </body>
 </html>
 
rename from dom/workers/test/fetch/worker_test_response.js
rename to dom/tests/mochitest/fetch/test_response.js
--- a/dom/workers/test/fetch/worker_test_response.js
+++ b/dom/tests/mochitest/fetch/test_response.js
@@ -1,18 +1,8 @@
-function ok(a, msg) {
-  dump("OK: " + !!a + "  =>  " + a + " " + msg + "\n");
-  postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
-}
-
-function is(a, b, msg) {
-  dump("IS: " + (a===b) + "  =>  " + a + " | " + b + " " + msg + "\n");
-  postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
-}
-
 function testDefaultCtor() {
   var res = new Response();
   is(res.type, "default", "Default Response type is default");
   ok(res.headers instanceof Headers, "Response should have non-null Headers object");
   is(res.url, "", "URL should be empty string");
   is(res.status, 200, "Default status is 200");
   is(res.statusText, "OK", "Default statusText is OK");
 }
@@ -207,47 +197,41 @@ function testBodyExtraction() {
   var text = "κόσμε";
   var newRes = function() { return new Response(text); }
   return newRes().text().then(function(v) {
     ok(typeof v === "string", "Should resolve to string");
     is(text, v, "Extracted string should match");
   }).then(function() {
     return newRes().blob().then(function(v) {
       ok(v instanceof Blob, "Should resolve to Blob");
-      var fs = new FileReaderSync();
-      is(fs.readAsText(v), text, "Decoded Blob should match original");
+      return readAsText(v).then(function(result) {
+        is(result, text, "Decoded Blob should match original");
+      });
     });
   }).then(function() {
     return newRes().json().then(function(v) {
       ok(false, "Invalid json should reject");
     }, function(e) {
       ok(true, "Invalid json should reject");
     })
   }).then(function() {
     return newRes().arrayBuffer().then(function(v) {
       ok(v instanceof ArrayBuffer, "Should resolve to ArrayBuffer");
       var dec = new TextDecoder();
       is(dec.decode(new Uint8Array(v)), text, "UTF-8 decoded ArrayBuffer should match original");
     });
   })
 }
 
-onmessage = function() {
-  var done = function() { postMessage({ type: 'finish' }) }
-
+function runTest() {
   testDefaultCtor();
   testError();
   testRedirect();
   testOk();
   testFinalURL();
 
-  Promise.resolve()
+  return Promise.resolve()
     .then(testBodyCreation)
     .then(testBodyUsed)
     .then(testBodyExtraction)
     .then(testClone)
     // Put more promise based tests here.
-    .then(done)
-    .catch(function(e) {
-      ok(false, "Some Response tests failed " + e);
-      done();
-    })
 }
--- a/dom/tests/mochitest/fetch/worker_wrapper.js
+++ b/dom/tests/mochitest/fetch/worker_wrapper.js
@@ -1,24 +1,29 @@
 function ok(a, msg) {
-  dump("OK: " + !!a + "  =>  " + a + " " + msg + "\n");
   postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
 }
 
 function is(a, b, msg) {
-  dump("IS: " + (a===b) + "  =>  " + a + " | " + b + " " + msg + "\n");
   postMessage({type: 'status', status: a === b, msg: a + " === " + b + ": " + msg });
 }
 
 addEventListener('message', function workerWrapperOnMessage(e) {
   removeEventListener('message', workerWrapperOnMessage);
   var data = e.data;
+
+  var done = function() {
+    postMessage({ type: 'finish' });
+  }
+
   try {
     importScripts(data.script);
+    // runTest() is provided by the test.
+    runTest().then(done, done);
   } catch(e) {
     postMessage({
       type: 'status',
       status: false,
       msg: 'worker failed to import ' + data.script + "; error: " + e.message
     });
+    done();
   }
-  postMessage({ type: 'finish' });
 });
--- a/dom/workers/moz.build
+++ b/dom/workers/moz.build
@@ -103,17 +103,16 @@ include('/ipc/chromium/chromium-config.m
 FINAL_LIBRARY = 'xul'
 
 TEST_DIRS += [
     'test/extensions/bootstrap',
     'test/extensions/traditional',
 ]
 
 MOCHITEST_MANIFESTS += [
-    'test/fetch/mochitest.ini',
     'test/mochitest.ini',
     'test/serviceworkers/mochitest.ini',
 ]
 
 MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
 
 XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell/xpcshell.ini']
 
deleted file mode 100644
--- a/dom/workers/test/fetch/mochitest.ini
+++ /dev/null
@@ -1,9 +0,0 @@
-[DEFAULT]
-support-files =
-  worker_interfaces.js
-  worker_test_request.js
-  worker_test_response.js
-
-[test_interfaces.html]
-[test_request.html]
-[test_response.html]
deleted file mode 100644
--- a/dom/workers/test/fetch/test_interfaces.html
+++ /dev/null
@@ -1,44 +0,0 @@
-<!--
-  Any copyright is dedicated to the Public Domain.
-  http://creativecommons.org/publicdomain/zero/1.0/
--->
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Bug 1017613 - Test fetch API interfaces</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
-</head>
-<body>
-<p id="display"></p>
-<div id="content" style="display: none"></div>
-<pre id="test"></pre>
-<script class="testbody" type="text/javascript">
-
-  function checkEnabled() {
-    var worker = new Worker("worker_interfaces.js");
-    worker.onmessage = function(event) {
-
-      if (event.data.type == 'finish') {
-        SimpleTest.finish();
-      } else if (event.data.type == 'status') {
-        ok(event.data.status, event.data.msg);
-      }
-    }
-
-    worker.onerror = function(event) {
-      ok(false, "Worker had an error: " + event.data);
-      SimpleTest.finish();
-    };
-
-    worker.postMessage(true);
-  }
-
-  SimpleTest.waitForExplicitFinish();
-
-  checkEnabled();
-</script>
-</pre>
-</body>
-</html>
-
deleted file mode 100644
--- a/dom/workers/test/fetch/worker_interfaces.js
+++ /dev/null
@@ -1,12 +0,0 @@
-function ok(a, msg) {
-  dump("OK: " + !!a + "  =>  " + a + " " + msg + "\n");
-  postMessage({type: 'status', status: !!a, msg: a + ": " + msg });
-}
-
-onmessage = function() {
-  ok(typeof Headers === "function", "Headers should be defined");
-  ok(typeof Request === "function", "Request should be defined");
-  ok(typeof Response === "function", "Response should be defined");
-  ok(typeof fetch === "function", "fetch() should be defined");
-  postMessage({ type: 'finish' });
-}