Bug 1383518 - Part 1: Augment test_formSubmission.html test to include ServiceWorker variants. r=bkelly, a=jcristau FIREFOX_55_0_3_BUILD2 FIREFOX_55_0_3_RELEASE
authorAndrew Sutherland <asutherland@asutherland.org>
Wed, 23 Aug 2017 04:23:13 -0400
changeset 666712 10a244c0f835d286d49a571dab59b698d7404e28
parent 666711 2c5b968aba14f53facca0eb4acc43b3d45260a78
child 666713 f4c7e5518a776c2fdcf0640c2d8fd733b706c105
push id80488
push userbmo:mkelly@mozilla.com
push dateTue, 19 Sep 2017 04:42:30 +0000
reviewersbkelly, jcristau
bugs1383518
milestone55.0.3
Bug 1383518 - Part 1: Augment test_formSubmission.html test to include ServiceWorker variants. r=bkelly, a=jcristau This test causes the expected serviceworker failure messages in the child process and then hangs the test.
dom/html/test/mochitest.ini
dom/html/test/sw_formSubmission.js
dom/html/test/test_formSubmission.html
--- a/dom/html/test/mochitest.ini
+++ b/dom/html/test/mochitest.ini
@@ -183,16 +183,17 @@ support-files =
   nnc_lockup.gif
   reflect.js
   file_ignoreuserfocus.html
   simpleFileOpener.js
   file_bug1166138_1x.png
   file_bug1166138_2x.png
   file_bug1166138_def.png
   script_fakepath.js
+  sw_formSubmission.js
   object_bug287465_o1.html
   object_bug287465_o2.html
   object_bug556645.html
   file.webm
 
 [test_a_text.html]
 [test_anchor_href_cache_invalidation.html]
 [test_applet_attributes_reflection.html]
new file mode 100644
--- /dev/null
+++ b/dom/html/test/sw_formSubmission.js
@@ -0,0 +1,36 @@
+/**
+ * We are used by test_formSubmission.html to immediately activate and start
+ * controlling its page.  We operate in 3 modes, conveyed via ?MODE appended to
+ * our URL.
+ *
+ * - "no-fetch": Don't register a fetch listener so that the optimized fetch
+ *   event bypass happens.
+ * - "reset-fetch": Do register a fetch listener, reset every interception.
+ * - "proxy-fetch": Do register a fetch listener, resolve every interception
+ *   with fetch(event.request).
+ */
+
+const mode = location.search.slice(1);
+
+// Fetch handling.
+if (mode !== "no-fetch") {
+  addEventListener("fetch", function(event) {
+    if (mode === "reset-fetch") {
+      // Don't invoke respondWith, resetting the interception.
+      return;
+    } else if (mode === "proxy-fetch") {
+      // Per the spec, there's an automatic waitUntil() on this too.
+      event.respondWith(fetch(event.request));
+      return;
+    }
+  });
+}
+
+// Go straight to activation, bypassing waiting.
+addEventListener("install", function(event) {
+  event.waitUntil(skipWaiting());
+});
+// Control the test document ASAP.
+addEventListener("activate", function(event) {
+  event.waitUntil(clients.claim());
+});
--- a/dom/html/test/test_formSubmission.html
+++ b/dom/html/test/test_formSubmission.html
@@ -444,17 +444,17 @@ function onFilesOpened(files) {
   SpecialPowers.wrap(input).mozSetFileArray(multiFile);
   myFile1 = input.files[0];
   myFile2 = input.files[1];
   is(myFile1.size, 20, "File1 size");
   is(myFile2.size, 2711, "File2 size");
   emptyFile = { name: "", type: "application/octet-stream" };
 
   // Now, actually run the tests; see below.
-  onFilesSet();
+  runAllTestVariants();
 };
 
 var expectedSub = [
   // Default input
   { name: "n1_1", value: "v1_1" },
   { name: "n1_2", value: "" },
   { name: "n1_3", value: "" },
   { name: "n1_7_\r\n_\r\n_\r\n_ _\"", value: "v1_7____ _\"" },
@@ -673,23 +673,37 @@ function checkPlainSubmission(sub, expec
 }
 
 function setDisabled(list, state) {
   Array.prototype.forEach.call(list, function(e) {
     e.disabled = state;
   });
 }
 
+/*
+ * The below test function uses callbacks that invoke gen.next() rather than
+ * creating and resolving Promises.  I'm trying to minimize churn since these
+ * changes want to be uplifted.  Some kind soul might want to clean this all up
+ * at some point.
+ */
 var gen;
-function onFilesSet() {
-  gen = runTest();
-  gen.next();
+$("target_iframe").onload = function() { gen.next(); };
+// Run the suite of tests for this variant, returning a Promise that will be
+// resolved when the batch completes.  Then and only then runTestVariant may
+// be invoked to run a different variation.
+function runTestVariant(variantLabel) {
+  info("starting test variant: " + variantLabel);
+  return new Promise((resolve) => {
+    // Instantiate the generator.
+    gen = runTestVariantUsingWeirdGenDriver(resolve);
+    // Run the generator to the first yield, at which point it is self-driving.
+    gen.next();
+  });
 }
-
-function* runTest() {
+function* runTestVariantUsingWeirdGenDriver(finishedVariant) {
   // Set up the expectedSub array
   fileReader1 = new FileReader;
   fileReader1.readAsBinaryString(myFile1);
   fileReader2 = new FileReader;
   fileReader2.readAsBinaryString(myFile2);
   fileReader1.onload = fileReader2.onload = function() { gen.next(); };
   yield undefined; // Wait for both FileReaders. We don't care which order they finish.
   yield undefined;
@@ -712,19 +726,21 @@ function* runTest() {
   };
   expectedSub.forEach(fileFixup);
   expectedAugment.forEach(fileFixup);
 
   var form = $("form");
 
   // multipart/form-data
   var iframe = $("target_iframe");
-  iframe.onload = function() { gen.next(); };
 
   // Make normal submission
+  form.action = "form_submit_server.sjs";
+  form.method = "POST";
+  form.enctype = "multipart/form-data";
   form.submit();
   yield undefined; // Wait for iframe to load as a result of the submission
   var submission = JSON.parse(iframe.contentDocument.documentElement.textContent);
   checkMPSubmission(submission, expectedSub, "normal submission");
 
   // Disabled controls
   setDisabled(document.querySelectorAll("input, select, textarea"), true);
   form.submit();
@@ -807,15 +823,80 @@ function* runTest() {
   fd = new FormData(form);
   addToFormData(fd);
   xhr.open("POST", "form_submit_server.sjs");
   xhr.send(fd);
   yield undefined;
   checkMPSubmission(JSON.parse(xhr.responseText),
                     expectedSub.concat(expectedAugment), "send augmented FormData");
 
+  finishedVariant();
+}
+
+/**
+ * Install our service-worker (parameterized by appending "?MODE"), which will
+ * invoke skipWaiting() and clients.claim() to begin controlling us ASAP.  We
+ * wait on the controllerchange event
+ */
+async function installAndBeControlledByServiceWorker(mode) {
+  const scriptURL = "sw_formSubmission.js?" + mode;
+  const controllerChanged = new Promise((resolve) => {
+    navigator.serviceWorker.addEventListener(
+      "controllerchange", () => { resolve(); }, { once: true });
+  });
+
+  info("installing ServiceWorker: " + scriptURL);
+  const swr = await navigator.serviceWorker.register(scriptURL,
+                                                     { scope: "./" });
+  await controllerChanged;
+  ok(navigator.serviceWorker.controller.scriptURL.endsWith(scriptURL),
+     "controlled by the SW we expected");
+  info("became controlled by ServiceWorker.");
+
+  return swr;
+}
+
+async function runAllTestVariants() {
+  // Run the test as it has historically been run, with no ServiceWorkers
+  // anywhere!
+  await runTestVariant("no ServiceWorker");
+
+  // Uncomment the below if something in the test seems broken and you're not
+  // sure whether it's a side-effect of the multiple passes or not.
+  //await runTestVariant("no ServiceWorker second paranoia time");
+
+  // Ensure ServiceWorkers are enabled and that testing mode (which disables
+  // security checks) is on too.
+  await SpecialPowers.pushPrefEnv({"set": [
+    ["dom.serviceWorkers.enabled", true],
+    ["dom.serviceWorkers.testing.enabled", true]
+  ]});
+
+  // Now run the test with a ServiceWorker that covers the scope but has no
+  // fetch handler, so the optimization case will not actually dispatch a
+  // "fetch" event, but some stuff will happen that can change things enough
+  // to break them like in https://bugzilla.mozilla.org/show_bug.cgi?id=1383518.
+  await installAndBeControlledByServiceWorker("no-fetch");
+  await runTestVariant("ServiceWorker that does not listen for fetch events");
+
+  // Now the ServiceWorker does have a "fetch" event listener, but it will reset
+  // interception every time.  This is similar to the prior case but different
+  // enough that it could break things in a different exciting way.
+  await installAndBeControlledByServiceWorker("reset-fetch");
+  await runTestVariant("ServiceWorker that resets all fetches");
+
+  // Now the ServiceWorker resolves the fetch event with `fetch(event.request)`
+  // which makes little sense but is a thing that can happen.
+  const swr = await installAndBeControlledByServiceWorker("proxy-fetch");
+  await runTestVariant("ServiceWorker that proxies all fetches");
+
+  // cleanup.
+  info("unregistering ServiceWorker");
+  await swr.unregister();
+  info("ServiceWorker uninstalled");
+
   SimpleTest.finish();
 }
 
 </script>
 </pre>
 </body>
 </html>