Bug 1193911 - Ensure synthetic Responses gets a valid channel info. r=ehsan
authorNikhil Marathe <nsm.nikhil@gmail.com>
Mon, 17 Aug 2015 15:08:58 -0700
changeset 258294 db15edc4cc63f4d9a98f700a9498c52e63659aac
parent 258293 da8fe9823492c0e62bcb99eb9711382f69bc7a0f
child 258295 3de395af1983b46fb6585f5612f78cbb7db19f8c
push id29249
push userryanvm@gmail.com
push dateWed, 19 Aug 2015 11:17:27 +0000
treeherdermozilla-central@706b23a03d1c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan
bugs1193911
milestone43.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 1193911 - Ensure synthetic Responses gets a valid channel info. r=ehsan Right now, synthetic Responses did not have a valid channel info. When these were saved in the Cache, and then restored, the restored Response did have a ChannelInfo, but that ChannelInfo did not have a valid security info. Passing this to respondWith() then caused the interception to fail. This patch modifies Response::Constructor() to initialize its ChannelInfo from the global. ChannelInfo can now initialize itself from a nsIDocument. All workers now store their ChannelInfo on the WorkerLoadInfo.
dom/fetch/ChannelInfo.cpp
dom/fetch/ChannelInfo.h
dom/fetch/Response.cpp
dom/workers/ScriptLoader.cpp
dom/workers/WorkerPrivate.h
dom/workers/test/serviceworkers/fetch/https/https_test.js
dom/workers/test/serviceworkers/fetch/https/register.html
dom/workers/test/serviceworkers/test_https_fetch.html
--- a/dom/fetch/ChannelInfo.cpp
+++ b/dom/fetch/ChannelInfo.cpp
@@ -2,28 +2,49 @@
 /* vim: set ts=8 sts=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 "mozilla/dom/ChannelInfo.h"
 #include "nsCOMPtr.h"
 #include "nsIChannel.h"
+#include "nsIDocument.h"
 #include "nsIHttpChannel.h"
 #include "nsSerializationHelper.h"
 #include "mozilla/net/HttpBaseChannel.h"
 #include "mozilla/ipc/ChannelInfo.h"
 #include "nsIJARChannel.h"
 #include "nsJARChannel.h"
 #include "nsNetUtil.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 void
+ChannelInfo::InitFromDocument(nsIDocument* aDoc)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!mInited, "Cannot initialize the object twice");
+
+  nsCOMPtr<nsISupports> securityInfo = aDoc->GetSecurityInfo();
+  if (securityInfo) {
+    SetSecurityInfo(securityInfo);
+  }
+
+  // mRedirected flag and mRedirectedURISpec are only important for maintaining
+  // the channel's redirected status.  If the ChannelInfo is initialized from
+  // a document, that document has already asked the channel from which it was
+  // loaded about the current channel URI, so it won't matter if a future
+  // ResurrectInfoOnChannel() call misses whether the channel was redirected.
+  mRedirected = false;
+  mInited = true;
+}
+
+void
 ChannelInfo::InitFromChannel(nsIChannel* aChannel)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mInited, "Cannot initialize the object twice");
 
   nsCOMPtr<nsISupports> securityInfo;
   aChannel->GetSecurityInfo(getter_AddRefs(securityInfo));
   if (securityInfo) {
--- a/dom/fetch/ChannelInfo.h
+++ b/dom/fetch/ChannelInfo.h
@@ -6,16 +6,17 @@
 
 #ifndef mozilla_dom_ChannelInfo_h
 #define mozilla_dom_ChannelInfo_h
 
 #include "nsString.h"
 #include "nsCOMPtr.h"
 
 class nsIChannel;
+class nsIDocument;
 class nsIURI;
 
 namespace mozilla {
 namespace ipc {
 class IPCChannelInfo;
 } // namespace ipc
 
 namespace dom {
@@ -61,16 +62,17 @@ public:
   {
     mSecurityInfo = aRHS.mSecurityInfo;
     mRedirectedURISpec = aRHS.mRedirectedURISpec;
     mInited = aRHS.mInited;
     mRedirected = aRHS.mRedirected;
     return *this;
   }
 
+  void InitFromDocument(nsIDocument* aDoc);
   void InitFromChannel(nsIChannel* aChannel);
   void InitFromIPCChannelInfo(const IPCChannelInfo& aChannelInfo);
 
   // This restores every possible information stored from a previous channel
   // object on a new one.
   nsresult ResurrectInfoOnChannel(nsIChannel* aChannel);
 
   bool IsInitialized() const
--- a/dom/fetch/Response.cpp
+++ b/dom/fetch/Response.cpp
@@ -154,16 +154,32 @@ Response::Constructor(const GlobalObject
   } else {
     // Since we don't support default values for ByteString.
     statusText = NS_LITERAL_CSTRING("OK");
   }
 
   nsRefPtr<InternalResponse> internalResponse =
     new InternalResponse(aInit.mStatus, statusText);
 
+  // Grab a valid channel info from the global so this response is 'valid' for
+  // interception.
+  if (NS_IsMainThread()) {
+    nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(global);
+    MOZ_ASSERT(window);
+    nsIDocument* doc = window->GetExtantDoc();
+    MOZ_ASSERT(doc);
+    ChannelInfo info;
+    info.InitFromDocument(doc);
+    internalResponse->InitChannelInfo(info);
+  } else {
+    workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate();
+    MOZ_ASSERT(worker);
+    internalResponse->InitChannelInfo(worker->GetChannelInfo());
+  }
+
   nsRefPtr<Response> r = new Response(global, internalResponse);
 
   if (aInit.mHeaders.WasPassed()) {
     internalResponse->Headers()->Clear();
 
     // Instead of using Fill, create an object to allow the constructor to
     // unwrap the HeadersInit.
     nsRefPtr<Headers> headers =
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -1028,19 +1028,17 @@ private:
 
     // Update the principal of the worker and its base URI if we just loaded the
     // worker's primary script.
     if (IsMainWorkerScript()) {
       // Take care of the base URI first.
       mWorkerPrivate->SetBaseURI(finalURI);
 
       // Store the channel info if needed.
-      if (mWorkerPrivate->IsServiceWorker()) {
-        mWorkerPrivate->InitChannelInfo(channel);
-      }
+      mWorkerPrivate->InitChannelInfo(channel);
 
       // Now to figure out which principal to give this worker.
       WorkerPrivate* parent = mWorkerPrivate->GetParent();
 
       NS_ASSERTION(mWorkerPrivate->GetPrincipal() || parent,
                    "Must have one of these!");
 
       nsCOMPtr<nsIPrincipal> loadPrincipal = mWorkerPrivate->GetPrincipal() ?
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -498,24 +498,22 @@ public:
     MOZ_ASSERT(IsServiceWorker());
     AssertIsOnMainThread();
     return mLoadInfo.mServiceWorkerCacheName;
   }
 
   const ChannelInfo&
   GetChannelInfo() const
   {
-    MOZ_ASSERT(IsServiceWorker());
     return mLoadInfo.mChannelInfo;
   }
 
   void
   SetChannelInfo(const ChannelInfo& aChannelInfo)
   {
-    MOZ_ASSERT(IsServiceWorker());
     AssertIsOnMainThread();
     MOZ_ASSERT(!mLoadInfo.mChannelInfo.IsInitialized());
     MOZ_ASSERT(aChannelInfo.IsInitialized());
     mLoadInfo.mChannelInfo = aChannelInfo;
   }
 
   void
   InitChannelInfo(nsIChannel* aChannel)
--- a/dom/workers/test/serviceworkers/fetch/https/https_test.js
+++ b/dom/workers/test/serviceworkers/fetch/https/https_test.js
@@ -1,14 +1,23 @@
 self.addEventListener("install", function(event) {
   event.waitUntil(caches.open("cache").then(function(cache) {
-    return cache.add("index.html");
+    var synth = new Response('<!DOCTYPE html><script>window.parent.postMessage({status: "done-synth-sw"}, "*");</script>',
+                             {headers:{"Content-Type": "text/html"}});
+    return Promise.all([
+      cache.add("index.html"),
+      cache.put("synth-sw.html", synth),
+    ]);
   }));
 });
 
 self.addEventListener("fetch", function(event) {
   if (event.request.url.indexOf("index.html") >= 0) {
     event.respondWith(caches.match(event.request));
+  } else if (event.request.url.indexOf("synth-sw.html") >= 0) {
+    event.respondWith(caches.match(event.request));
+  } else if (event.request.url.indexOf("synth-window.html") >= 0) {
+    event.respondWith(caches.match(event.request));
   } else if (event.request.url.indexOf("synth.html") >= 0) {
     event.respondWith(new Response('<!DOCTYPE html><script>window.parent.postMessage({status: "done-synth"}, "*");</script>',
                                    {headers:{"Content-Type": "text/html"}}));
   }
 });
--- a/dom/workers/test/serviceworkers/fetch/https/register.html
+++ b/dom/workers/test/serviceworkers/fetch/https/register.html
@@ -4,11 +4,17 @@
     window.parent.postMessage({status: "ok", result: !!v, message: msg}, "*");
   }
 
   function done(reg) {
     ok(reg.active, "The active worker should be available.");
     window.parent.postMessage({status: "registrationdone"}, "*");
   }
 
-  navigator.serviceWorker.ready.then(done);
+  navigator.serviceWorker.ready.then(reg => {
+    return window.caches.open("cache").then(function(cache) {
+      var synth = new Response('<!DOCTYPE html><script>window.parent.postMessage({status: "done-synth-window"}, "*");</scri' + 'pt>',
+                               {headers:{"Content-Type": "text/html"}});
+      return cache.put('synth-window.html', synth).then(_ => done(reg));
+    });
+  });
   navigator.serviceWorker.register("https_test.js", {scope: "."});
 </script>
--- a/dom/workers/test/serviceworkers/test_https_fetch.html
+++ b/dom/workers/test/serviceworkers/test_https_fetch.html
@@ -26,16 +26,20 @@
       if (e.data.status == "ok") {
         ok(e.data.result, e.data.message);
       } else if (e.data.status == "registrationdone") {
         ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
                            .getService(SpecialPowers.Ci.nsIIOService);
         ios.offline = true;
         iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/index.html";
       } else if (e.data.status == "done") {
+        iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/synth-sw.html";
+      } else if (e.data.status == "done-synth-sw") {
+        iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/synth-window.html";
+      } else if (e.data.status == "done-synth-window") {
         iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/synth.html";
       } else if (e.data.status == "done-synth") {
         ios.offline = false;
         iframe.src = "https://example.com/tests/dom/workers/test/serviceworkers/fetch/https/unregister.html";
       } else if (e.data.status == "unregistrationdone") {
         window.onmessage = null;
         ok(true, "Test finished successfully");
         SimpleTest.finish();