Bug 1314493 simplify xhr webrequest tests for uploads, fix xhr blob send, r=kmag draft
authorShane Caraveo <scaraveo@mozilla.com>
Wed, 02 Nov 2016 09:27:48 -0700
changeset 432726 52e103db1287d122e6b434840affbae7335c74f9
parent 430359 9888f1a23001fde6435e1a9ed7e6d3af8dd988d8
child 432769 7d82d1000473b2530d6144987a28e35815c11999
push id34402
push usermixedpuppy@gmail.com
push dateWed, 02 Nov 2016 16:28:53 +0000
reviewerskmag
bugs1314493
milestone52.0a1
Bug 1314493 simplify xhr webrequest tests for uploads, fix xhr blob send, r=kmag MozReview-Commit-ID: 4sQGgwD6HMC
toolkit/components/extensions/test/mochitest/file_WebRequest_page1.html
toolkit/components/extensions/test/mochitest/mochitest.ini
toolkit/components/extensions/test/mochitest/test_ext_webrequest.html
toolkit/components/extensions/test/mochitest/test_ext_webrequest_upload.html
toolkit/modules/addons/WebRequestUpload.jsm
--- a/toolkit/components/extensions/test/mochitest/file_WebRequest_page1.html
+++ b/toolkit/components/extensions/test/mochitest/file_WebRequest_page1.html
@@ -24,110 +24,20 @@
 <script src="nonexistent_script_url.js"></script>
 
 <iframe src="file_WebRequest_page2.html" width="200" height="200"></iframe>
 <iframe src="redirection.sjs" width="200" height="200"></iframe>
 <iframe src="data:text/plain,webRequestTest" width="200" height="200"></iframe>
 <iframe src="data:text/plain,webRequestTest_bad" width="200" height="200"></iframe>
 <iframe src="https://invalid.localhost/" width="200" height="200"></iframe>
 <a href="file_WebRequest_page3.html?trigger=a" target="webrequest_link">link</a>
-<form method="post"
-  action="file_WebRequest_page3.html?trigger=form"
-  target="_blank"
-  enctype="multipart/form-data"
-  >
-<input type="text" name="&quot;special&quot; chàrs" value="sp€cial">
-<input type="file" name="testFile">
-<input type="file" name="emptyFile">
-<input type="text" name="textInput1" value="value1">
-</form>
-<form method="post"
-  action="file_WebRequest_page3.html?trigger=form"
-  target="_blank"
-  enctype="multipart/form-data"
-  >
-<input type="text" name="textInput2" value="value2">
-<input type="file" name="testFile">
-<input type="file" name="emptyFile">
-
-</form>
-<form method="post"
-  action="file_WebRequest_page3.html?trigger=form"
-  target="_blank"
-  >
-<input type="text" name="textInput" value="value1">
-<input type="text" name="textInput" value="value2">
-</form>
+<form method="post" action="file_WebRequest_page3.html?trigger=form" target="webrequest_form"></form>
 <script>
 "use strict";
 for (let a of document.links) {
   a.click();
 }
-
-SpecialPowers.createFiles([{name: "testFile.pdf", data: "Not really a PDF file :)", "type": "application/x-pdf"}], (files) => {
-  let testFile = files[0];
-  let blob = {
-    name: "blobAsFile",
-    content: new Blob(["A blob sent as a file"], {type: "text/csv"}),
-    fileName: "blobAsFile.csv",
-  };
-  let file = {
-    name: "testFile",
-    fileName: testFile.name,
-  };
-  let uploads = {
-    [blob.name]: blob,
-    [file.name]: file,
-  };
-
-  for (let form of document.forms) {
-    if (file.name in form.elements) {
-      SpecialPowers.wrap(form.elements[file.name]).mozSetFileArray(files);
-    }
-    let action = new URL(form.action);
-    let formData = new FormData(form);
-    let webRequestFD = {};
-
-    let updateActionURL = () => {
-      for (let name of formData.keys()) {
-        webRequestFD[name] = name in uploads ? [uploads[name].fileName] : formData.getAll(name);
-      }
-      action.searchParams.set("upload", JSON.stringify(webRequestFD));
-      action.searchParams.set("enctype", form.enctype);
-    };
-
-    updateActionURL();
-
-    form.action = action;
-    form.submit();
-
-    if (form.enctype === "multipart/form-data") {
-      let post = (data) => {
-        let xhr = new XMLHttpRequest();
-        action.searchParams.set("xhr", "1");
-        xhr.open("POST", action.href);
-        xhr.send(data);
-        action.searchParams.delete("xhr");
-      };
-
-      formData.append(blob.name, blob.content, blob.fileName);
-      formData.append("formDataField", "some value");
-      updateActionURL(true);
-      post(formData);
-
-      action.searchParams.set("upload", JSON.stringify([{file: "<file>"}]));
-      post(testFile);
-
-      let blobReader = new FileReader();
-      blobReader.readAsArrayBuffer(blob.content);
-      blobReader.onload = () => {
-        action.searchParams.set("upload", `${blobReader.result.byteLength} bytes`);
-        post(blob.content);
-      };
-      let byteLength = 16;
-      action.searchParams.set("upload", `${byteLength} bytes`);
-      post(new ArrayBuffer(byteLength));
-    }
-  }
-});
+for (let f of document.forms) {
+  f.submit();
+}
 </script>
 </body>
 </html>
--- a/toolkit/components/extensions/test/mochitest/mochitest.ini
+++ b/toolkit/components/extensions/test/mochitest/mochitest.ini
@@ -86,15 +86,17 @@ skip-if = (os == 'android' || buildapp =
 skip-if = (os == 'android') # Android does not support tabs API. Bug 1260250
 [test_ext_unload_frame.html]
 [test_ext_i18n.html]
 skip-if = (os == 'android') # Bug 1258975 on android.
 [test_ext_web_accessible_resources.html]
 skip-if = (os == 'android') # Bug 1258975 on android.
 [test_ext_webrequest.html]
 skip-if = os == 'android' # webrequest api unsupported (bug 1258975).
+[test_ext_webrequest_upload.html]
+skip-if = os == 'android' # webrequest api unsupported (bug 1258975).
 [test_ext_webnavigation.html]
 skip-if = os == 'android' # port.sender.tab is undefined on Android (bug 1258975).
 [test_ext_webnavigation_filters.html]
 skip-if = os == 'android' # port.sender.tab is undefined on Android (bug 1258975).
 [test_ext_subframes_privileges.html]
 skip-if = os == 'android' # port.sender.tab is undefined on Android (bug 1258975).
 [test_ext_xhr_capabilities.html]
--- a/toolkit/components/extensions/test/mochitest/test_ext_webrequest.html
+++ b/toolkit/components/extensions/test/mochitest/test_ext_webrequest.html
@@ -323,41 +323,16 @@ function backgroundScript() {
       }
     }
     if (details.url.includes("_bad")) {
       return {cancel: true};
     }
     return {};
   }
 
-  function onUpload(details) {
-    let url = new URL(details.url);
-    let upload = url.searchParams.get("upload");
-    if (!upload) {
-      return;
-    }
-    let requestBody = details.requestBody;
-    browser.test.log(`onUpload ${details.url} ${JSON.stringify(details.requestBody)}`);
-    browser.test.assertTrue(!!requestBody, `Intercepted upload ${details.url} #${details.requestId} ${upload} have a requestBody`);
-    if (!requestBody) {
-      return;
-    }
-    let byteLength = parseInt(upload, 10);
-    if (byteLength) {
-      browser.test.assertTrue(!!requestBody.raw, `Binary upload ${details.url} #${details.requestId} ${upload} have a raw attribute`);
-      browser.test.assertEq(byteLength, requestBody.raw && requestBody.raw.map(r => r.bytes && r.bytes.byteLength || 0).reduce((a, b) => a + b), `Binary upload size matches`);
-      return;
-    }
-    if ("raw" in requestBody) {
-      browser.test.assertEq(upload, JSON.stringify(requestBody.raw).replace(/(\bfile: ")[^"]+/, "$1<file>"), `Upload ${details.url} #${details.requestId} matches raw data`);
-    } else {
-      browser.test.assertEq(upload, JSON.stringify(requestBody.formData), `Upload ${details.url} #${details.requestId} matches form data.`);
-    }
-  }
-
   function onBeforeSendHeaders(details) {
     browser.test.log(`onBeforeSendHeaders ${details.url}`);
     checkRequestId(details);
     checkOrigin(details);
     checkResourceType(details.type);
     processHeaders("request", details);
     if (shouldRecord(details.url)) {
       recorded.beforeSendHeaders.push(details.url);
@@ -464,24 +439,16 @@ function backgroundScript() {
   }
 
   function onCompleted(details) {
     checkFromCache("completed", details);
     checkHeaders("response", details);
   }
 
   browser.webRequest.onBeforeRequest.addListener(onBeforeRequest, {urls: ["<all_urls>"]}, ["blocking"]);
-  try {
-    browser.webRequest.onBeforeRequest.addListener(onUpload, {urls: ["http://*/*"]}, ["blocking", "requestBody"]);
-  } catch (e) {
-    // requestBody is disabled in release builds
-    if (!/\brequestBody\b/.test(e.message)) {
-      throw e;
-    }
-  }
   browser.webRequest.onBeforeSendHeaders.addListener(onBeforeSendHeaders, {urls: ["<all_urls>"]}, ["blocking", "requestHeaders"]);
   browser.webRequest.onSendHeaders.addListener(onSendHeaders, {urls: ["<all_urls>"]}, ["requestHeaders"]);
   browser.webRequest.onBeforeRedirect.addListener(onBeforeRedirect, {urls: ["<all_urls>"]});
   browser.webRequest.onHeadersReceived.addListener(onHeadersReceived, {urls: ["<all_urls>"]}, ["blocking", "responseHeaders"]);
   browser.webRequest.onResponseStarted.addListener(checkFromCache.bind(null, "responseStarted"), {urls: ["<all_urls>"]});
   browser.webRequest.onResponseStarted.addListener(checkFromCache.bind(null, "responseStarted2"), {urls: ["<all_urls>"]});
   browser.webRequest.onErrorOccurred.addListener(onErrorOccurred, {urls: ["<all_urls>"]});
   browser.webRequest.onCompleted.addListener(onCompleted, {urls: ["<all_urls>"]}, ["responseHeaders"]);
new file mode 100644
--- /dev/null
+++ b/toolkit/components/extensions/test/mochitest/test_ext_webrequest_upload.html
@@ -0,0 +1,195 @@
+<!DOCTYPE HTML>
+
+<html>
+<head>
+<meta charset="utf-8">
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
+  <script type="text/javascript" src="head.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<form method="post"
+  action="file_WebRequest_page3.html?trigger=form"
+  target="_blank"
+  enctype="multipart/form-data"
+  >
+<input type="text" name="&quot;special&quot; chˆrs" value="spÛcial">
+<input type="file" name="testFile">
+<input type="file" name="emptyFile">
+<input type="text" name="textInput1" value="value1">
+</form>
+
+<form method="post"
+  action="file_WebRequest_page3.html?trigger=form"
+  target="_blank"
+  enctype="multipart/form-data"
+  >
+<input type="text" name="textInput2" value="value2">
+<input type="file" name="testFile">
+<input type="file" name="emptyFile">
+</form>
+
+</form>
+<form method="post"
+  action="file_WebRequest_page3.html?trigger=form"
+  target="_blank"
+  >
+<input type="text" name="textInput" value="value1">
+<input type="text" name="textInput" value="value2">
+</form>
+<script>
+"use strict";
+
+let files, testFile, blob, file, uploads;
+add_task(function* test_setup() {
+  files = yield new Promise(resolve => {
+    SpecialPowers.createFiles([{name: "testFile.pdf", data: "Not really a PDF file :)", "type": "application/x-pdf"}], (files) => {
+      resolve(files);
+    });
+  });
+  testFile = files[0];
+  blob = {
+    name: "blobAsFile",
+    content: new Blob(["A blob sent as a file"], {type: "text/csv"}),
+    fileName: "blobAsFile.csv",
+  };
+  file = {
+    name: "testFile",
+    fileName: testFile.name,
+  };
+  uploads = {
+    [blob.name]: blob,
+    [file.name]: file,
+  };
+});
+
+function background() {
+  let events = {
+    "onBeforeRequest":  [{urls: ["<all_urls>"]}, ["blocking", "requestBody"]],
+    "onCompleted":      [{urls: ["<all_urls>"]}, ["responseHeaders"]],
+  };
+
+  function onUpload(details) {
+    let url = new URL(details.url);
+    let upload = url.searchParams.get("upload");
+    if (!upload) {
+      return;
+    }
+    let requestBody = details.requestBody;
+    browser.test.log(`onUpload ${details.url} ${JSON.stringify(details.requestBody)}`);
+    browser.test.assertTrue(!!requestBody, `Intercepted upload ${details.url} #${details.requestId} ${upload} have a requestBody`);
+    if (!requestBody) {
+      return;
+    }
+    let byteLength = parseInt(upload, 10);
+    if (byteLength) {
+      browser.test.assertTrue(!!requestBody.raw, `Binary upload ${details.url} #${details.requestId} ${upload} have a raw attribute`);
+      browser.test.assertEq(byteLength, requestBody.raw && requestBody.raw.map(r => r.bytes && r.bytes.byteLength || 0).reduce((a, b) => a + b), `Binary upload size matches`);
+      return;
+    }
+    if ("raw" in requestBody) {
+      browser.test.assertEq(upload, JSON.stringify(requestBody.raw).replace(/(\bfile: ")[^"]+/, "$1<file>"), `Upload ${details.url} #${details.requestId} matches raw data`);
+    } else {
+      browser.test.assertEq(upload, JSON.stringify(requestBody.formData), `Upload ${details.url} #${details.requestId} matches form data.`);
+    }
+  }
+
+  function listener(name, details) {
+    browser.test.log(`${name} ${details.requestId} ${details.url}`);
+
+    if (name == "onBeforeRequest") {
+      onUpload(details);
+    } else if (name == "onCompleted") {
+      browser.test.sendMessage("done");
+    }
+  }
+
+  for (let [name, args] of Object.entries(events)) {
+    try {
+      browser.test.log(`adding listener for ${name}`);
+      browser.webRequest[name].addListener(
+        listener.bind(null, name), ...args);
+    } catch (e) {
+      // requestBody is disabled in release builds
+      if (!/\brequestBody\b/.test(e.message)) {
+        args.pop();
+        browser.webRequest[name].addListener(
+          listener.bind(null, name), ...args);
+      }
+    }
+  }
+}
+
+add_task(function* test_xhr_forms() {
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      permissions: [
+        "webRequest",
+        "webRequestBlocking",
+        "<all_urls>",
+      ],
+    },
+    background,
+  });
+
+  yield extension.startup();
+
+  for (let form of document.forms) {
+    if (file.name in form.elements) {
+      SpecialPowers.wrap(form.elements[file.name]).mozSetFileArray(files);
+    }
+    let action = new URL(form.action);
+    let formData = new FormData(form);
+    let webRequestFD = {};
+
+    let updateActionURL = () => {
+      for (let name of formData.keys()) {
+        webRequestFD[name] = name in uploads ? [uploads[name].fileName] : formData.getAll(name);
+      }
+      action.searchParams.set("upload", JSON.stringify(webRequestFD));
+      action.searchParams.set("enctype", form.enctype);
+    };
+
+    updateActionURL();
+
+    form.action = action;
+    form.submit();
+    yield extension.awaitMessage("done");
+
+    if (form.enctype !== "multipart/form-data") {
+      continue;
+    }
+
+    let post = (data) => {
+      let xhr = new XMLHttpRequest();
+      action.searchParams.set("xhr", "1");
+      xhr.open("POST", action.href);
+      xhr.send(data);
+      action.searchParams.delete("xhr");
+      return extension.awaitMessage("done");
+    };
+
+    formData.append(blob.name, blob.content, blob.fileName);
+    formData.append("formDataField", "some value");
+    updateActionURL();
+    yield post(formData);
+
+    action.searchParams.set("upload", JSON.stringify([{file: "<file>"}]));
+    yield post(testFile);
+
+    action.searchParams.set("upload", `${blob.content.size} bytes`);
+    yield post(blob.content);
+
+    let byteLength = 16;
+    action.searchParams.set("upload", `${byteLength} bytes`);
+    yield post(new ArrayBuffer(byteLength));
+  }
+
+  yield extension.unload();
+});
+</script>
+</body>
+</html>
--- a/toolkit/modules/addons/WebRequestUpload.jsm
+++ b/toolkit/modules/addons/WebRequestUpload.jsm
@@ -14,17 +14,19 @@ const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 var WebRequestUpload;
 
 function rewind(stream) {
   try {
-    stream.seek(0, 0);
+    if (stream instanceof Ci.nsISeekableStream) {
+      stream.seek(0, 0);
+    }
   } catch (e) {
     // It might be already closed, e.g. because of a previous error.
   }
 }
 
 function parseFormData(stream, channel, lenient = false) {
   const BUFFER_SIZE = 8192; // Empirically it seemed a good compromise.