Bug 1134325 - Part 2: Correctly reflect the Request's body to the service worker's fetch event; r=jdm
authorEhsan Akhgari <ehsan@mozilla.com>
Mon, 04 May 2015 16:54:22 -0400
changeset 273831 70bd577548bf02809c80fa6e4690a16a27001350
parent 273830 82f7c0c91407a189c137e261f656bb0e96bf4fab
child 273832 346bc200674144500439fa54acc66b41ef14cbbd
push id863
push userraliiev@mozilla.com
push dateMon, 03 Aug 2015 13:22:43 +0000
treeherdermozilla-release@f6321b14228d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdm
bugs1134325
milestone40.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 1134325 - Part 2: Correctly reflect the Request's body to the service worker's fetch event; r=jdm
dom/workers/ServiceWorkerManager.cpp
dom/workers/test/serviceworkers/fetch/fetch_tests.js
dom/workers/test/serviceworkers/fetch_event_worker.js
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -11,16 +11,17 @@
 #include "nsIDocument.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIStreamLoader.h"
 #include "nsIHttpChannel.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsIHttpHeaderVisitor.h"
 #include "nsINetworkInterceptController.h"
 #include "nsIMutableArray.h"
+#include "nsIUploadChannel2.h"
 #include "nsPIDOMWindow.h"
 #include "nsScriptLoader.h"
 #include "nsDebug.h"
 
 #include "jsapi.h"
 
 #include "mozilla/ErrorNames.h"
 #include "mozilla/LoadContext.h"
@@ -2352,16 +2353,17 @@ class FetchEventRunnable : public Worker
   nsTArray<nsCString> mHeaderValues;
   nsAutoPtr<ServiceWorkerClientInfo> mClientInfo;
   nsCString mSpec;
   nsCString mMethod;
   bool mIsReload;
   RequestMode mRequestMode;
   RequestCredentials mRequestCredentials;
   nsContentPolicyType mContentPolicyType;
+  nsCOMPtr<nsIInputStream> mUploadStream;
 public:
   FetchEventRunnable(WorkerPrivate* aWorkerPrivate,
                      nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
                      nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
                      nsAutoPtr<ServiceWorkerClientInfo>& aClientInfo,
                      bool aIsReload)
     : WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
     , mInterceptedChannel(aChannel)
@@ -2446,16 +2448,23 @@ public:
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsILoadInfo> loadInfo;
     rv = channel->GetLoadInfo(getter_AddRefs(loadInfo));
     NS_ENSURE_SUCCESS(rv, rv);
 
     mContentPolicyType = loadInfo->GetContentPolicyType();
 
+    nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(httpChannel);
+    if (uploadChannel) {
+      MOZ_ASSERT(!mUploadStream);
+      rv = uploadChannel->CloneUploadStream(getter_AddRefs(mUploadStream));
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+
     return NS_OK;
   }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
   {
     MOZ_ASSERT(aWorkerPrivate);
     return DispatchFetchEvent(aCx, aWorkerPrivate);
@@ -2505,31 +2514,31 @@ private:
         return false;
       }
     }
 
     nsRefPtr<Headers> headers = new Headers(globalObj.GetAsSupports(), internalHeaders);
     reqInit.mHeaders.Construct();
     reqInit.mHeaders.Value().SetAsHeaders() = headers;
 
-    //TODO(jdm): set request body
-
     reqInit.mMode.Construct(mRequestMode);
     reqInit.mCredentials.Construct(mRequestCredentials);
 
     ErrorResult result;
     nsRefPtr<Request> request = Request::Constructor(globalObj, requestInfo, reqInit, result);
     if (NS_WARN_IF(result.Failed())) {
       return false;
     }
     // For Telemetry, note that this Request object was created by a Fetch event.
     nsRefPtr<InternalRequest> internalReq = request->GetInternalRequest();
     MOZ_ASSERT(internalReq);
     internalReq->SetCreatedByFetchEvent();
 
+    internalReq->SetBody(mUploadStream);
+
     request->SetContentPolicyType(mContentPolicyType);
 
     RootedDictionary<FetchEventInit> init(aCx);
     init.mRequest.Construct();
     init.mRequest.Value() = request;
     init.mBubbles = false;
     init.mCancelable = true;
     init.mIsReload.Construct(mIsReload);
--- a/dom/workers/test/serviceworkers/fetch/fetch_tests.js
+++ b/dom/workers/test/serviceworkers/fetch/fetch_tests.js
@@ -175,8 +175,78 @@ expectAsyncResult();
 fetch('http://example.com/cors-for-no-cors', { mode: "no-cors" })
 .then(function(res) {
   my_ok(res.type == "opaque", "intercepted non-opaque response for no-cors request should resolve to opaque response.");
   finish();
 }, function(e) {
   my_ok(false, "intercepted non-opaque response for no-cors request should resolve to opaque response. It should not fail.");
   finish();
 });
+
+function arrayBufferFromString(str) {
+  var arr = new Uint8Array(str.length);
+  for (var i = 0; i < str.length; ++i) {
+    arr[i] = str.charCodeAt(i);
+  }
+  return arr;
+}
+
+expectAsyncResult();
+fetch(new Request('body-simple', {method: 'POST', body: 'my body'}))
+.then(function(res) {
+  return res.text();
+}).then(function(body) {
+  my_ok(body == 'my bodymy body', "the body of the intercepted fetch should be visible in the SW");
+  finish();
+});
+
+expectAsyncResult();
+fetch(new Request('body-arraybufferview', {method: 'POST', body: arrayBufferFromString('my body')}))
+.then(function(res) {
+  return res.text();
+}).then(function(body) {
+  my_ok(body == 'my bodymy body', "the ArrayBufferView body of the intercepted fetch should be visible in the SW");
+  finish();
+});
+
+expectAsyncResult();
+fetch(new Request('body-arraybuffer', {method: 'POST', body: arrayBufferFromString('my body').buffer}))
+.then(function(res) {
+  return res.text();
+}).then(function(body) {
+  my_ok(body == 'my bodymy body', "the ArrayBuffer body of the intercepted fetch should be visible in the SW");
+  finish();
+});
+
+expectAsyncResult();
+var usp = new URLSearchParams();
+usp.set("foo", "bar");
+usp.set("baz", "qux");
+fetch(new Request('body-urlsearchparams', {method: 'POST', body: usp}))
+.then(function(res) {
+  return res.text();
+}).then(function(body) {
+  my_ok(body == 'foo=bar&baz=quxfoo=bar&baz=qux', "the URLSearchParams body of the intercepted fetch should be visible in the SW");
+  finish();
+});
+
+expectAsyncResult();
+var fd = new FormData();
+fd.set("foo", "bar");
+fd.set("baz", "qux");
+fetch(new Request('body-formdata', {method: 'POST', body: fd}))
+.then(function(res) {
+  return res.text();
+}).then(function(body) {
+  my_ok(body.indexOf("Content-Disposition: form-data; name=\"foo\"\r\n\r\nbar") <
+        body.indexOf("Content-Disposition: form-data; name=\"baz\"\r\n\r\nqux"),
+        "the FormData body of the intercepted fetch should be visible in the SW");
+  finish();
+});
+
+expectAsyncResult();
+fetch(new Request('body-blob', {method: 'POST', body: new Blob(new String('my body'))}))
+.then(function(res) {
+  return res.text();
+}).then(function(body) {
+  my_ok(body == 'my bodymy body', "the Blob body of the intercepted fetch should be visible in the SW");
+  finish();
+});
--- a/dom/workers/test/serviceworkers/fetch_event_worker.js
+++ b/dom/workers/test/serviceworkers/fetch_event_worker.js
@@ -167,9 +167,15 @@ onfetch = function(ev) {
                      "opener.postMessage({status: 'done'}, '*');" +
                    "</script>";
         ev.respondWith(new Response(body, {headers: {'Content-Type': 'text/html'}}));
     } else {
       seenIndex = true;
       ev.respondWith(fetch(ev.request.url));
     }
   }
+
+  else if (ev.request.url.includes("body-")) {
+    ev.respondWith(ev.request.text().then(function (body) {
+      return new Response(body + body);
+    }));
+  }
 }