[beacon] Fix ArrayBuffer and URLSearchParams data

`navigator.sendBeacon()` was not handling DOMArrayBuffer and
URLSearchParams inputs properly, resulting in failing WPTs related to
the sent Content-Type, as well as in the wrong data sent in the case
of DOMArrayBuffers.
This CL fixes that.

Bug: 876671
Change-Id: I17674b3041aa0f0bdbd1a570ab34be48b0dd98b4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2489986
Commit-Queue: Yoav Weiss <yoavweiss@chromium.org>
Reviewed-by: Adam Rice <ricea@chromium.org>
Reviewed-by: Yutaka Hirano <yhirano@chromium.org>
Cr-Commit-Position: refs/heads/master@{#820134}
diff --git a/beacon/headers/header-content-type.html b/beacon/headers/header-content-type-and-body.html
similarity index 61%
rename from beacon/headers/header-content-type.html
rename to beacon/headers/header-content-type-and-body.html
index e2f2705..0369cff 100644
--- a/beacon/headers/header-content-type.html
+++ b/beacon/headers/header-content-type-and-body.html
@@ -12,13 +12,13 @@
     <script>
 const RESOURCES_DIR = "/beacon/resources/";
 
-function testContentTypeHeader(what, contentType, title) {
+function testContentTypeAndBody(what, expected, title) {
   function wait(ms) {
     return new Promise(resolve => step_timeout(resolve, ms));
   }
   promise_test(async t => {
     const id = self.token();
-    const testUrl = new Request(RESOURCES_DIR + "content-type.py?cmd=put&id=" + id).url;
+    const testUrl = new Request(RESOURCES_DIR + "content-type-and-body.py?cmd=put&id=" + id).url;
     assert_equals(performance.getEntriesByName(testUrl).length, 0);
     assert_true(navigator.sendBeacon(testUrl, what), "SendBeacon Succeeded");
 
@@ -26,13 +26,17 @@
       await wait(50);
     } while (performance.getEntriesByName(testUrl).length === 0);
     assert_equals(performance.getEntriesByName(testUrl).length, 1);
-    const checkUrl = RESOURCES_DIR + "content-type.py?cmd=get&id=" + id;
+    const checkUrl = RESOURCES_DIR + "content-type-and-body.py?cmd=get&id=" + id;
     const response = await fetch(checkUrl);
     const text = await response.text();
-    if (contentType === "multipart/form-data") {
+    if (expected.startsWith("multipart/form-data")) {
+      const split = expected.split(":");
+      const contentType = split[0];
+      const contentDisposition = "Content-Disposition: form-data; name=\"" + split[1] + "\"; filename=\"blob\"";
       assert_true(text.startsWith(contentType), "Correct Content-Type header result");
+      assert_true(text.includes(contentDisposition), "Body included value");
     } else {
-      assert_equals(text, contentType, "Correct Content-Type header result");
+      assert_equals(text, expected, "Correct Content-Type header result");
     }
   }, "Test content-type header for a body " + title);
 }
@@ -74,12 +78,12 @@
   return new URLSearchParams(input);
 }
 
-testContentTypeHeader("hi!", "text/plain;charset=UTF-8", "string");
-testContentTypeHeader(stringToArrayBufferView("123"), "", "ArrayBufferView");
-testContentTypeHeader(stringToArrayBuffer("123"), "", "ArrayBuffer");
-testContentTypeHeader(stringToBlob("123"), "text/plain", "Blob");
-testContentTypeHeader(stringToFormData("qwerty"), "multipart/form-data", "FormData");
-testContentTypeHeader(stringToURLSearchParams("key1=value1&key2=value2"), "application/x-www-form-urlencoded;charset=UTF-8", "URLSearchParams");
+testContentTypeAndBody("hi!", "text/plain;charset=UTF-8: hi!", "string");
+testContentTypeAndBody(stringToArrayBufferView("123"), ": 1\0" + "2\0" + "3\0", "ArrayBufferView");
+testContentTypeAndBody(stringToArrayBuffer("123"), ": 1\0" + "2\0" + "3\0", "ArrayBuffer");
+testContentTypeAndBody(stringToBlob("123"), "text/plain: 123", "Blob");
+testContentTypeAndBody(stringToFormData("qwerty"), "multipart/form-data:qwerty", "FormData");
+testContentTypeAndBody(stringToURLSearchParams("key1=value1&key2=value2"), "application/x-www-form-urlencoded;charset=UTF-8: key1=value1&key2=value2", "URLSearchParams");
     </script>
   </body>
 </html>
diff --git a/beacon/resources/content-type.py b/beacon/resources/content-type-and-body.py
similarity index 92%
rename from beacon/resources/content-type.py
rename to beacon/resources/content-type-and-body.py
index d3be7d4..1c8c4c7 100644
--- a/beacon/resources/content-type.py
+++ b/beacon/resources/content-type-and-body.py
@@ -2,7 +2,7 @@
     command = request.GET.first(b"cmd").lower()
     test_id = request.GET.first(b"id")
     if command == b"put":
-        request.server.stash.put(test_id, request.headers.get(b"Content-Type", b""))
+        request.server.stash.put(test_id, request.headers.get(b"Content-Type", b"") + ": " + request.body)
         return [(b"Content-Type", b"text/plain")], u""
 
     if command == b"get":