Bug 1058551 - Support redirects to data: URIs. r=honza
authorBlake Kaplan <mrbkap@gmail.com>
Fri, 27 Mar 2015 13:12:37 -0700
changeset 265143 6fb980e58948e688e2c794e4ec006f2c6ca831c2
parent 265142 20d97f735cde591c6561f1eb727c5d470c4a7080
child 265144 5000b940578b090d416273fe88a10c18deec86ba
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)
reviewershonza
bugs1058551
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 1058551 - Support redirects to data: URIs. r=honza
netwerk/ipc/NeckoChild.cpp
netwerk/ipc/NeckoChild.h
netwerk/ipc/NeckoParent.cpp
netwerk/ipc/NeckoParent.h
netwerk/ipc/PDataChannel.ipdl
netwerk/ipc/PNecko.ipdl
netwerk/ipc/moz.build
netwerk/protocol/data/DataChannelChild.cpp
netwerk/protocol/data/DataChannelChild.h
netwerk/protocol/data/DataChannelParent.cpp
netwerk/protocol/data/DataChannelParent.h
netwerk/protocol/data/moz.build
netwerk/protocol/data/nsDataChannel.h
netwerk/protocol/data/nsDataHandler.cpp
netwerk/test/unit/test_redirect_different-protocol.js
--- a/netwerk/ipc/NeckoChild.cpp
+++ b/netwerk/ipc/NeckoChild.cpp
@@ -162,16 +162,30 @@ NeckoChild::AllocPWebSocketChild(const P
 bool
 NeckoChild::DeallocPWebSocketChild(PWebSocketChild* child)
 {
   WebSocketChannelChild* p = static_cast<WebSocketChannelChild*>(child);
   p->ReleaseIPDLReference();
   return true;
 }
 
+PDataChannelChild*
+NeckoChild::AllocPDataChannelChild(const uint32_t& channelId)
+{
+  MOZ_ASSERT_UNREACHABLE("Should never get here");
+  return nullptr;
+}
+
+bool
+NeckoChild::DeallocPDataChannelChild(PDataChannelChild* child)
+{
+  // NB: See DataChannelChild::ActorDestroy.
+  return true;
+}
+
 PRtspControllerChild*
 NeckoChild::AllocPRtspControllerChild()
 {
   NS_NOTREACHED("AllocPRtspController should not be called");
   return nullptr;
 }
 
 bool
--- a/netwerk/ipc/NeckoChild.h
+++ b/netwerk/ipc/NeckoChild.h
@@ -57,16 +57,18 @@ protected:
                                                   const uint32_t& aFlags,
                                                   const nsCString& aNetworkInterface) override;
   virtual bool DeallocPDNSRequestChild(PDNSRequestChild*) override;
   virtual PRemoteOpenFileChild*
     AllocPRemoteOpenFileChild(const SerializedLoadContext& aSerialized,
                               const URIParams&,
                               const OptionalURIParams&) override;
   virtual bool DeallocPRemoteOpenFileChild(PRemoteOpenFileChild*) override;
+  virtual PDataChannelChild* AllocPDataChannelChild(const uint32_t& channelId) override;
+  virtual bool DeallocPDataChannelChild(PDataChannelChild* child) override;
   virtual PRtspControllerChild* AllocPRtspControllerChild() override;
   virtual bool DeallocPRtspControllerChild(PRtspControllerChild*) override;
   virtual PRtspChannelChild*
     AllocPRtspChannelChild(const RtspChannelConnectArgs& aArgs)
                            override;
   virtual bool DeallocPRtspChannelChild(PRtspChannelChild*) override;
   virtual PChannelDiverterChild*
   AllocPChannelDiverterChild(const ChannelDiverterArgs& channel) override;
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -8,16 +8,17 @@
 #include "necko-config.h"
 #include "nsHttp.h"
 #include "mozilla/net/NeckoParent.h"
 #include "mozilla/net/HttpChannelParent.h"
 #include "mozilla/net/CookieServiceParent.h"
 #include "mozilla/net/WyciwygChannelParent.h"
 #include "mozilla/net/FTPChannelParent.h"
 #include "mozilla/net/WebSocketChannelParent.h"
+#include "mozilla/net/DataChannelParent.h"
 #ifdef NECKO_PROTOCOL_rtsp
 #include "mozilla/net/RtspControllerParent.h"
 #include "mozilla/net/RtspChannelParent.h"
 #endif
 #include "mozilla/net/DNSRequestParent.h"
 #include "mozilla/net/RemoteOpenFileParent.h"
 #include "mozilla/net/ChannelDiverterParent.h"
 #include "mozilla/dom/ContentParent.h"
@@ -344,16 +345,39 @@ NeckoParent::AllocPWebSocketParent(const
 bool
 NeckoParent::DeallocPWebSocketParent(PWebSocketParent* actor)
 {
   WebSocketChannelParent* p = static_cast<WebSocketChannelParent*>(actor);
   p->Release();
   return true;
 }
 
+PDataChannelParent*
+NeckoParent::AllocPDataChannelParent(const uint32_t &channelId)
+{
+  nsRefPtr<DataChannelParent> p = new DataChannelParent();
+  return p.forget().take();
+}
+
+bool
+NeckoParent::DeallocPDataChannelParent(PDataChannelParent* actor)
+{
+  nsRefPtr<DataChannelParent> p = dont_AddRef(static_cast<DataChannelParent*>(actor));
+  return true;
+}
+
+bool
+NeckoParent::RecvPDataChannelConstructor(PDataChannelParent* actor,
+                                         const uint32_t& channelId)
+{
+  DataChannelParent* p = static_cast<DataChannelParent*>(actor);
+  p->Init(channelId);
+  return true;
+}
+
 PRtspControllerParent*
 NeckoParent::AllocPRtspControllerParent()
 {
 #ifdef NECKO_PROTOCOL_rtsp
   RtspControllerParent* p = new RtspControllerParent();
   p->AddRef();
   return p;
 #else
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -165,16 +165,24 @@ protected:
                                    const uint16_t& flags) override;
   virtual bool RecvCancelHTMLDNSPrefetch(const nsString& hostname,
                                          const uint16_t& flags,
                                          const nsresult& reason) override;
 
   virtual mozilla::ipc::IProtocol*
   CloneProtocol(Channel* aChannel,
                 mozilla::ipc::ProtocolCloneContext* aCtx) override;
+
+  virtual PDataChannelParent*
+    AllocPDataChannelParent(const uint32_t& channelId) override;
+  virtual bool DeallocPDataChannelParent(PDataChannelParent* parent) override;
+
+  virtual bool RecvPDataChannelConstructor(PDataChannelParent* aActor,
+                                           const uint32_t& channelId) override;
+
   virtual PRtspControllerParent* AllocPRtspControllerParent() override;
   virtual bool DeallocPRtspControllerParent(PRtspControllerParent*) override;
 
   virtual PRtspChannelParent*
     AllocPRtspChannelParent(const RtspChannelConnectArgs& aArgs)
                             override;
   virtual bool
     RecvPRtspChannelConstructor(PRtspChannelParent* aActor,
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/PDataChannel.ipdl
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+
+/* 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 protocol PNecko;
+include URIParams;
+
+namespace mozilla {
+namespace net {
+
+async protocol PDataChannel
+{
+  manager PNecko;
+
+parent:
+  // Note: channels are opened during construction, so no open method here:
+  // see PNecko.ipdl
+  __delete__();
+};
+
+} // namespace net
+} // namespace mozilla
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -15,16 +15,17 @@ include protocol PWebSocket;
 include protocol PTCPSocket;
 include protocol PTCPServerSocket;
 include protocol PUDPSocket;
 include protocol PRemoteOpenFile;
 include protocol PDNSRequest;
 include protocol PChannelDiverter;
 include protocol PBlob; //FIXME: bug #792908
 include protocol PFileDescriptorSet;
+include protocol PDataChannel;
 
 include protocol PRtspController;
 include protocol PRtspChannel;
 include URIParams;
 include InputStreamParams;
 include NeckoChannelParams;
 include PBrowserOrId;
 
@@ -43,16 +44,17 @@ prio(normal upto urgent) sync protocol P
   manages PWyciwygChannel;
   manages PFTPChannel;
   manages PWebSocket;
   manages PTCPSocket;
   manages PTCPServerSocket;
   manages PUDPSocket;
   manages PDNSRequest;
   manages PRemoteOpenFile;
+  manages PDataChannel;
   manages PRtspController;
   manages PRtspChannel;
   manages PChannelDiverter;
 
 parent:
   __delete__();
 
   prio(urgent) async PCookieService();
@@ -71,16 +73,23 @@ parent:
 
   PRemoteOpenFile(SerializedLoadContext loadContext,
                   URIParams fileuri,
                   OptionalURIParams appuri);
 
   SpeculativeConnect(URIParams uri);
   HTMLDNSPrefetch(nsString hostname, uint16_t flags);
   CancelHTMLDNSPrefetch(nsString hostname, uint16_t flags, nsresult reason);
+
+  /**
+   * channelId is used to establish a connection between redirect channels in
+   * the parent and the child when we're redirecting to a data: URI.
+   */
+  PDataChannel(uint32_t channelId);
+
   PRtspController();
   PRtspChannel(RtspChannelConnectArgs args);
   PChannelDiverter(ChannelDiverterArgs channel);
 
   /**
    * These are called from the child with the results of the auth prompt.
    * callbackId is the id that was passed in PBrowser::AsyncAuthPrompt,
    * corresponding to an nsIAuthPromptCallback
--- a/netwerk/ipc/moz.build
+++ b/netwerk/ipc/moz.build
@@ -27,16 +27,17 @@ UNIFIED_SOURCES += [
     'NeckoParent.cpp',
     'RemoteOpenFileChild.cpp',
     'RemoteOpenFileParent.cpp',
 ]
 
 IPDL_SOURCES = [
     'NeckoChannelParams.ipdlh',
     'PChannelDiverter.ipdl',
+    'PDataChannel.ipdl',
     'PNecko.ipdl',
     'PRemoteOpenFile.ipdl',
     'PRtspChannel.ipdl',
     'PRtspController.ipdl',
 ]
 
 FAIL_ON_WARNINGS = True
 
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/data/DataChannelChild.cpp
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=4 sw=4 sts=4 et 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 "DataChannelChild.h"
+
+#include "mozilla/unused.h"
+#include "mozilla/net/NeckoChild.h"
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS_INHERITED(DataChannelChild, nsDataChannel, nsIChildChannel)
+
+DataChannelChild::DataChannelChild(nsIURI* aURI)
+    : nsDataChannel(aURI)
+    , mIPCOpen(false)
+{
+}
+
+DataChannelChild::~DataChannelChild()
+{
+}
+
+NS_IMETHODIMP
+DataChannelChild::ConnectParent(uint32_t aId)
+{
+    if (!gNeckoChild->SendPDataChannelConstructor(this, aId)) {
+        return NS_ERROR_FAILURE;
+    }
+
+    // IPC now has a ref to us.
+    AddIPDLReference();
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+DataChannelChild::CompleteRedirectSetup(nsIStreamListener *aListener,
+                                        nsISupports *aContext)
+{
+    nsresult rv = AsyncOpen(aListener, aContext);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+    }
+
+    if (mIPCOpen) {
+        unused << Send__delete__(this);
+    }
+    return NS_OK;
+}
+
+void
+DataChannelChild::AddIPDLReference()
+{
+    AddRef();
+    mIPCOpen = true;
+}
+
+void
+DataChannelChild::ActorDestroy(ActorDestroyReason why)
+{
+    MOZ_ASSERT(mIPCOpen);
+    mIPCOpen = false;
+    Release();
+}
+
+} // namespace mozilla
+} // namespace net
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/data/DataChannelChild.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=4 sw=4 sts=4 et 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/. */
+
+#ifndef NS_DATACHANNELCHILD_H
+#define NS_DATACHANNELCHILD_H
+
+#include "nsDataChannel.h"
+#include "nsIChildChannel.h"
+#include "nsISupportsImpl.h"
+
+#include "mozilla/net/PDataChannelChild.h"
+
+namespace mozilla {
+namespace net {
+
+class DataChannelChild : public nsDataChannel
+                       , public nsIChildChannel
+                       , public PDataChannelChild
+{
+public:
+    explicit DataChannelChild(nsIURI *uri);
+
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSICHILDCHANNEL
+
+protected:
+    virtual void ActorDestroy(ActorDestroyReason why) override;
+
+private:
+    ~DataChannelChild();
+
+    void AddIPDLReference();
+
+    bool mIPCOpen;
+};
+
+} // namespace mozilla
+} // namespace net
+
+#endif /* NS_DATACHANNELCHILD_H */
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/data/DataChannelParent.cpp
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=4 sw=4 sts=4 et 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 "DataChannelParent.h"
+#include "mozilla/Assertions.h"
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS(DataChannelParent, nsIParentChannel, nsIStreamListener)
+
+DataChannelParent::~DataChannelParent()
+{
+}
+
+bool
+DataChannelParent::Init(const uint32_t &channelId)
+{
+    nsCOMPtr<nsIChannel> channel;
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+        NS_LinkRedirectChannels(channelId, this, getter_AddRefs(channel))));
+
+    return true;
+}
+
+NS_IMETHODIMP
+DataChannelParent::SetParentListener(HttpChannelParentListener* aListener)
+{
+    // Nothing to do.
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+DataChannelParent::NotifyTrackingProtectionDisabled()
+{
+    // Nothing to do.
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+DataChannelParent::Delete()
+{
+    // Nothing to do.
+    return NS_OK;
+}
+
+void
+DataChannelParent::ActorDestroy(ActorDestroyReason why)
+{
+}
+
+NS_IMETHODIMP
+DataChannelParent::OnStartRequest(nsIRequest *aRequest,
+                                  nsISupports *aContext)
+{
+    // We don't have a way to prevent nsBaseChannel from calling AsyncOpen on
+    // the created nsDataChannel. We don't have anywhere to send the data in the
+    // parent, so abort the binding.
+    return NS_BINDING_ABORTED;
+}
+
+NS_IMETHODIMP
+DataChannelParent::OnStopRequest(nsIRequest *aRequest,
+                                 nsISupports *aContext,
+                                 nsresult aStatusCode)
+{
+    // See above.
+    MOZ_ASSERT(NS_FAILED(aStatusCode));
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+DataChannelParent::OnDataAvailable(nsIRequest *aRequest,
+                                   nsISupports *aContext,
+                                   nsIInputStream *aInputStream,
+                                   uint64_t aOffset,
+                                   uint32_t aCount)
+{
+    // See above.
+    MOZ_CRASH("Should never be called");
+}
+
+} // namespace mozilla
+} // namespace net
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/data/DataChannelParent.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set ts=4 sw=4 sts=4 et 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/. */
+
+#ifndef NS_DATACHANNELPARENT_H
+#define NS_DATACHANNELPARENT_H
+
+#include "nsIParentChannel.h"
+#include "nsISupportsImpl.h"
+
+#include "mozilla/net/PDataChannelParent.h"
+
+namespace mozilla {
+namespace net {
+
+// In order to support HTTP redirects to data:, we need to implement the HTTP
+// redirection API, which requires a class that implements nsIParentChannel
+// and which calls NS_LinkRedirectChannels.
+class DataChannelParent : public nsIParentChannel
+                        , public PDataChannelParent
+{
+public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIPARENTCHANNEL
+    NS_DECL_NSIREQUESTOBSERVER
+    NS_DECL_NSISTREAMLISTENER
+
+    bool Init(const uint32_t& aArgs);
+
+private:
+    ~DataChannelParent();
+
+    virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+} // namespace mozilla
+} // namespace net
+
+#endif /* NS_DATACHANNELPARENT_H */
--- a/netwerk/protocol/data/moz.build
+++ b/netwerk/protocol/data/moz.build
@@ -1,18 +1,26 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+EXPORTS.mozilla.net += [
+    'DataChannelParent.h',
+]
+
 UNIFIED_SOURCES += [
+    'DataChannelChild.cpp',
+    'DataChannelParent.cpp',
     'nsDataChannel.cpp',
     'nsDataHandler.cpp',
 ]
 
+include('/ipc/chromium/chromium-config.mozbuild')
+
 FAIL_ON_WARNINGS = True
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '/netwerk/base',
 ]
 
--- a/netwerk/protocol/data/nsDataChannel.h
+++ b/netwerk/protocol/data/nsDataChannel.h
@@ -7,17 +7,18 @@
 
 #ifndef nsDataChannel_h___
 #define nsDataChannel_h___
 
 #include "nsBaseChannel.h"
 
 class nsIInputStream;
 
-class nsDataChannel : public nsBaseChannel {
+class nsDataChannel : public nsBaseChannel
+{
 public:
     explicit nsDataChannel(nsIURI *uri) {
         SetURI(uri);
     }
 
 protected:
     virtual nsresult OpenContentStream(bool async, nsIInputStream **result,
                                        nsIChannel** channel);
--- a/netwerk/protocol/data/nsDataHandler.cpp
+++ b/netwerk/protocol/data/nsDataHandler.cpp
@@ -2,16 +2,17 @@
 /* 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 "nsDataChannel.h"
 #include "nsDataHandler.h"
 #include "nsNetCID.h"
 #include "nsError.h"
+#include "DataChannelChild.h"
 
 static NS_DEFINE_CID(kSimpleURICID, NS_SIMPLEURI_CID);
 
 ////////////////////////////////////////////////////////////////////////////////
 
 nsDataHandler::nsDataHandler() {
 }
 
@@ -103,19 +104,22 @@ nsDataHandler::NewURI(const nsACString &
 }
 
 NS_IMETHODIMP
 nsDataHandler::NewChannel2(nsIURI* uri,
                            nsILoadInfo* aLoadInfo,
                            nsIChannel** result)
 {
     NS_ENSURE_ARG_POINTER(uri);
-    nsDataChannel* channel = new nsDataChannel(uri);
-    if (!channel)
-        return NS_ERROR_OUT_OF_MEMORY;
+    nsDataChannel* channel;
+    if (XRE_GetProcessType() == GeckoProcessType_Default) {
+        channel = new nsDataChannel(uri);
+    } else {
+        channel = new mozilla::net::DataChannelChild(uri);
+    }
     NS_ADDREF(channel);
 
     nsresult rv = channel->Init();
     if (NS_FAILED(rv)) {
         NS_RELEASE(channel);
         return rv;
     }
 
--- a/netwerk/test/unit/test_redirect_different-protocol.js
+++ b/netwerk/test/unit/test_redirect_different-protocol.js
@@ -39,23 +39,17 @@ function redirectHandler(metadata, respo
 {
   response.setStatusLine(metadata.httpVersion, 301, "Moved");
   response.bodyOutputStream.write(response301Body, response301Body.length);
   response.setHeader("Location", "data:text/plain," + redirectTargetBody, false);
 }
 
 function finish_test(request, buffer)
 {
-  if (inChildProcess()) {
-    // redirects to protocols other than http/ftp will fail until bug 590682 is fixed.
-    do_check_eq(buffer, response301Body);
-  } else {
-    do_check_eq(buffer, redirectTargetBody);
-  }
-
+  do_check_eq(buffer, redirectTargetBody);
   httpServer.stop(do_test_finished);
 }
 
 function run_test()
 {
   httpServer = new HttpServer();
   httpServer.registerPathHandler(randomPath, redirectHandler);
   httpServer.start(-1);