Bug 1136200 - Verify request type is not no-cors if response is opaque. r=jdm
authorNikhil Marathe <nsm.nikhil@gmail.com>
Tue, 17 Mar 2015 08:47:03 -0700
changeset 264011 2e4fa9ce9708f0538ee535532d28f6945f793d80
parent 264010 6a639c5517e34f0df0b4a7c6b7137a7ddf636ad6
child 264012 84b52c0b3bd16213a0a634b1d1037ccaeeadf367
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdm
bugs1136200
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 1136200 - Verify request type is not no-cors if response is opaque. r=jdm
dom/workers/ServiceWorkerEvents.cpp
dom/workers/test/serviceworkers/fetch/fetch_tests.js
dom/workers/test/serviceworkers/fetch/index.html
dom/workers/test/serviceworkers/fetch_event_worker.js
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ServiceWorkerEvents.h"
 #include "ServiceWorkerClient.h"
 
+#include "nsIHttpChannelInternal.h"
 #include "nsINetworkInterceptController.h"
 #include "nsIOutputStream.h"
 #include "nsContentUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStreamUtils.h"
 #include "nsNetCID.h"
 #include "nsSerializationHelper.h"
@@ -130,21 +131,24 @@ public:
     return rv;
   }
 };
 
 class RespondWithHandler final : public PromiseNativeHandler
 {
   nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel;
   nsMainThreadPtrHandle<ServiceWorker> mServiceWorker;
+  RequestMode mRequestMode;
 public:
   RespondWithHandler(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
-                     nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker)
+                     nsMainThreadPtrHandle<ServiceWorker>& aServiceWorker,
+                     RequestMode aRequestMode)
     : mInterceptedChannel(aChannel)
     , mServiceWorker(aServiceWorker)
+    , mRequestMode(aRequestMode)
   {
   }
 
   void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
 
   void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override;
 
   void CancelRequest();
@@ -208,18 +212,20 @@ RespondWithHandler::ResolvedCallback(JSC
   }
 
   nsRefPtr<Response> response;
   nsresult rv = UNWRAP_OBJECT(Response, &aValue.toObject(), response);
   if (NS_FAILED(rv)) {
     return;
   }
 
-  // FIXME(nsm) Bug 1136200 deal with opaque and no-cors (fetch spec 4.2.2.2).
-  if (response->Type() == ResponseType::Error) {
+  // Section 4.2, step 2.2 "If either response's type is "opaque" and request's
+  // mode is not "no-cors" or response's type is error, return a network error."
+  if (((response->Type() == ResponseType::Opaque) && (mRequestMode != RequestMode::No_cors)) ||
+      response->Type() == ResponseType::Error) {
     return;
   }
 
   if (NS_WARN_IF(response->BodyUsed())) {
     return;
   }
 
   nsRefPtr<InternalResponse> ir = response->GetInternalResponse();
@@ -279,17 +285,18 @@ void
 FetchEvent::RespondWith(Promise& aPromise, ErrorResult& aRv)
 {
   if (mWaitToRespond) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
 
   mWaitToRespond = true;
-  nsRefPtr<RespondWithHandler> handler = new RespondWithHandler(mChannel, mServiceWorker);
+  nsRefPtr<RespondWithHandler> handler =
+    new RespondWithHandler(mChannel, mServiceWorker, mRequest->Mode());
   aPromise.AppendNativeHandler(handler);
 }
 
 void
 FetchEvent::RespondWith(Response& aResponse, ErrorResult& aRv)
 {
   if (mWaitToRespond) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
--- a/dom/workers/test/serviceworkers/fetch/fetch_tests.js
+++ b/dom/workers/test/serviceworkers/fetch/fetch_tests.js
@@ -132,8 +132,38 @@ fetch('http://example.com/tests/dom/base
   res.text().then(function(body) {
     my_ok(body === "", "opaque response body should be empty");
     finish();
   });
 }, function(e) {
   my_ok(false, "no-cors Fetch failed");
   finish();
 });
+
+expectAsyncResult();
+fetch('opaque-on-same-origin')
+.then(function(res) {
+  my_ok(false, "intercepted opaque response for non no-cors request should fail.");
+  finish();
+}, function(e) {
+  my_ok(true, "intercepted opaque response for non no-cors request should fail.");
+  finish();
+});
+
+expectAsyncResult();
+fetch('http://example.com/opaque-no-cors', { mode: "no-cors" })
+.then(function(res) {
+  my_ok(res.type == "opaque", "intercepted opaque response for no-cors request should have type opaque.");
+  finish();
+}, function(e) {
+  my_ok(false, "intercepted opaque response for no-cors request should pass.");
+  finish();
+});
+
+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();
+});
--- a/dom/workers/test/serviceworkers/fetch/index.html
+++ b/dom/workers/test/serviceworkers/fetch/index.html
@@ -130,17 +130,17 @@
   gExpected++;
   var worker = new Worker('fetch_worker_script.js');
   worker.onmessage = function(e) {
     if (e.data == "finish") {
       finish();
     } else if (e.data == "expect") {
       gExpected++;
     } else if (e.data.type == "ok") {
-      my_ok(e.data.value, e.data.msg);
+      my_ok(e.data.value, "Fetch test on worker: " + e.data.msg);
     }
   };
   worker.onerror = function() {
     my_ok(false, "worker should not cause any errors");
   };
 </script>
 </pre>
 </body>
--- a/dom/workers/test/serviceworkers/fetch_event_worker.js
+++ b/dom/workers/test/serviceworkers/fetch_event_worker.js
@@ -113,16 +113,41 @@ onfetch = function(ev) {
   else if (ev.request.url.contains("hello-after-extracting.gz")) {
     ev.respondWith(fetch("fetch/deliver-gzip.sjs").then(function(res) {
       return res.text().then(function(body) {
         return new Response(body, { status: res.status, statusText: res.statusText, headers: res.headers });
       });
     }));
   }
 
+  else if (ev.request.url.contains('opaque-on-same-origin')) {
+    var url = 'http://example.com/tests/dom/base/test/file_CrossSiteXHR_server.sjs?status=200';
+    ev.respondWith(fetch(url, { mode: 'no-cors' }));
+  }
+
+  else if (ev.request.url.contains('opaque-no-cors')) {
+    if (ev.request.mode != "no-cors") {
+      ev.respondWith(Promise.reject());
+      return;
+    }
+
+    var url = 'http://example.com/tests/dom/base/test/file_CrossSiteXHR_server.sjs?status=200';
+    ev.respondWith(fetch(url, { mode: ev.request.mode }));
+  }
+
+  else if (ev.request.url.contains('cors-for-no-cors')) {
+    if (ev.request.mode != "no-cors") {
+      ev.respondWith(Promise.reject());
+      return;
+    }
+
+    var url = 'http://example.com/tests/dom/base/test/file_CrossSiteXHR_server.sjs?status=200&allowOrigin=*';
+    ev.respondWith(fetch(url));
+  }
+
   else if (ev.request.url.contains('example.com')) {
     ev.respondWith(fetch(ev.request));
   }
 
   else if (ev.request.url.contains("index.html")) {
     if (seenIndex) {
         var body = "<script>" +
                      "opener.postMessage({status: 'ok', result: " + ev.isReload + "," +