Bug 1345094 - make http redirects to file:// uris work under e10s. r?mayhemer draft
authorNicholas Hurley <hurley@mozilla.com>
Wed, 12 Apr 2017 11:43:50 -0700
changeset 563825 d996f83638c1105bd4354d753cb3b97488218aa9
parent 562355 3243c8fc3ce7831dda843b60d6bb2d7e4acf1fd4
child 624589 cf9adb96e458e55e11ce645703930b414bef1423
push id54433
push userbmo:hurley@mozilla.com
push dateMon, 17 Apr 2017 21:52:16 +0000
reviewersmayhemer
bugs1345094
milestone55.0a1
Bug 1345094 - make http redirects to file:// uris work under e10s. r?mayhemer MozReview-Commit-ID: J4IheswoeMZ
netwerk/ipc/NeckoChild.cpp
netwerk/ipc/NeckoChild.h
netwerk/ipc/NeckoParent.cpp
netwerk/ipc/NeckoParent.h
netwerk/ipc/PFileChannel.ipdl
netwerk/ipc/PNecko.ipdl
netwerk/ipc/moz.build
netwerk/protocol/file/FileChannelChild.cpp
netwerk/protocol/file/FileChannelChild.h
netwerk/protocol/file/FileChannelParent.cpp
netwerk/protocol/file/FileChannelParent.h
netwerk/protocol/file/moz.build
netwerk/protocol/file/nsFileProtocolHandler.cpp
--- a/netwerk/ipc/NeckoChild.cpp
+++ b/netwerk/ipc/NeckoChild.cpp
@@ -228,16 +228,30 @@ NeckoChild::AllocPDataChannelChild(const
 
 bool
 NeckoChild::DeallocPDataChannelChild(PDataChannelChild* child)
 {
   // NB: See DataChannelChild::ActorDestroy.
   return true;
 }
 
+PFileChannelChild*
+NeckoChild::AllocPFileChannelChild(const uint32_t& channelId)
+{
+  MOZ_ASSERT_UNREACHABLE("Should never get here");
+  return nullptr;
+}
+
+bool
+NeckoChild::DeallocPFileChannelChild(PFileChannelChild* child)
+{
+  // NB: See FileChannelChild::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
@@ -64,16 +64,18 @@ protected:
   virtual bool DeallocPUDPSocketChild(PUDPSocketChild*) override;
   virtual PDNSRequestChild* AllocPDNSRequestChild(const nsCString& aHost,
                                                   const OriginAttributes& aOriginAttributes,
                                                   const uint32_t& aFlags,
                                                   const nsCString& aNetworkInterface) override;
   virtual bool DeallocPDNSRequestChild(PDNSRequestChild*) override;
   virtual PDataChannelChild* AllocPDataChannelChild(const uint32_t& channelId) override;
   virtual bool DeallocPDataChannelChild(PDataChannelChild* child) override;
+  virtual PFileChannelChild* AllocPFileChannelChild(const uint32_t& channelId) override;
+  virtual bool DeallocPFileChannelChild(PFileChannelChild* 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
@@ -13,16 +13,17 @@
 #include "mozilla/net/CookieServiceParent.h"
 #include "mozilla/net/WyciwygChannelParent.h"
 #include "mozilla/net/FTPChannelParent.h"
 #include "mozilla/net/WebSocketChannelParent.h"
 #include "mozilla/net/WebSocketEventListenerParent.h"
 #include "mozilla/net/DataChannelParent.h"
 #include "mozilla/net/AltDataOutputStreamParent.h"
 #include "mozilla/Unused.h"
+#include "mozilla/net/FileChannelParent.h"
 #ifdef NECKO_PROTOCOL_rtsp
 #include "mozilla/net/RtspControllerParent.h"
 #include "mozilla/net/RtspChannelParent.h"
 #endif
 #include "mozilla/net/DNSRequestParent.h"
 #include "mozilla/net/ChannelDiverterParent.h"
 #include "mozilla/net/IPCTransportProvider.h"
 #ifdef MOZ_WEBRTC
@@ -520,16 +521,40 @@ NeckoParent::RecvPDataChannelConstructor
                                          const uint32_t& channelId)
 {
   DataChannelParent* p = static_cast<DataChannelParent*>(actor);
   DebugOnly<bool> rv = p->Init(channelId);
   MOZ_ASSERT(rv);
   return IPC_OK();
 }
 
+PFileChannelParent*
+NeckoParent::AllocPFileChannelParent(const uint32_t &channelId)
+{
+  RefPtr<FileChannelParent> p = new FileChannelParent();
+  return p.forget().take();
+}
+
+bool
+NeckoParent::DeallocPFileChannelParent(PFileChannelParent* actor)
+{
+  RefPtr<FileChannelParent> p = dont_AddRef(static_cast<FileChannelParent*>(actor));
+  return true;
+}
+
+mozilla::ipc::IPCResult
+NeckoParent::RecvPFileChannelConstructor(PFileChannelParent* actor,
+                                         const uint32_t& channelId)
+{
+  FileChannelParent* p = static_cast<FileChannelParent*>(actor);
+  DebugOnly<bool> rv = p->Init(channelId);
+  MOZ_ASSERT(rv);
+  return IPC_OK();
+}
+
 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
@@ -178,16 +178,23 @@ protected:
 
   virtual PDataChannelParent*
     AllocPDataChannelParent(const uint32_t& channelId) override;
   virtual bool DeallocPDataChannelParent(PDataChannelParent* parent) override;
 
   virtual mozilla::ipc::IPCResult RecvPDataChannelConstructor(PDataChannelParent* aActor,
                                                               const uint32_t& channelId) override;
 
+  virtual PFileChannelParent*
+    AllocPFileChannelParent(const uint32_t& channelId) override;
+  virtual bool DeallocPFileChannelParent(PFileChannelParent* parent) override;
+
+  virtual mozilla::ipc::IPCResult RecvPFileChannelConstructor(PFileChannelParent* aActor,
+                                                              const uint32_t& channelId) override;
+
   virtual PRtspControllerParent* AllocPRtspControllerParent() override;
   virtual bool DeallocPRtspControllerParent(PRtspControllerParent*) override;
 
   virtual PRtspChannelParent*
     AllocPRtspChannelParent(const RtspChannelConnectArgs& aArgs)
                             override;
   virtual mozilla::ipc::IPCResult
     RecvPRtspChannelConstructor(PRtspChannelParent* aActor,
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/PFileChannel.ipdl
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=2 sts=2 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 {
+
+/* Used to facilitate http redirects to file:// - see
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=1345094
+ */
+async protocol PFileChannel
+{
+  manager PNecko;
+
+parent:
+  // Note: channels are opened during construction, so no open method here:
+  // see PNecko.ipdl
+  async __delete__();
+};
+
+} // namespace net
+} // namespace mozilla
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -20,16 +20,17 @@ include protocol PDNSRequest;
 include protocol PChannelDiverter;
 include protocol PBlob; //FIXME: bug #792908
 include protocol PFileDescriptorSet;
 include protocol PDataChannel;
 include protocol PTransportProvider;
 include protocol PChildToParentStream; //FIXME: bug #792908
 include protocol PParentToChildStream; //FIXME: bug #792908
 include protocol PStunAddrsRequest;
+include protocol PFileChannel;
 
 include protocol PRtspController;
 include protocol PRtspChannel;
 include URIParams;
 include NeckoChannelParams;
 include PBrowserOrId;
 include protocol PAltDataOutputStream;
 
@@ -50,16 +51,17 @@ nested(upto inside_cpow) sync protocol P
   manages PFTPChannel;
   manages PWebSocket;
   manages PWebSocketEventListener;
   manages PTCPSocket;
   manages PTCPServerSocket;
   manages PUDPSocket;
   manages PDNSRequest;
   manages PDataChannel;
+  manages PFileChannel;
   manages PRtspController;
   manages PRtspChannel;
   manages PChannelDiverter;
   manages PTransportProvider;
   manages PAltDataOutputStream;
   manages PStunAddrsRequest;
 
 parent:
@@ -98,16 +100,18 @@ parent:
                               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.
    */
   async PDataChannel(uint32_t channelId);
 
+  async PFileChannel(uint32_t channelId);
+
   async PRtspController();
   async PRtspChannel(RtspChannelConnectArgs args);
   async 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
@@ -18,16 +18,17 @@ UNIFIED_SOURCES += [
     'NeckoCommon.cpp',
     'NeckoParent.cpp',
 ]
 
 IPDL_SOURCES = [
     'NeckoChannelParams.ipdlh',
     'PChannelDiverter.ipdl',
     'PDataChannel.ipdl',
+    'PFileChannel.ipdl',
     'PNecko.ipdl',
     'PRtspChannel.ipdl',
     'PRtspController.ipdl',
 ]
 
 # needed so --disable-webrtc builds work (yes, a bit messy)
 if not CONFIG['MOZ_WEBRTC']:
   IPDL_SOURCES += [
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/file/FileChannelChild.cpp
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 sts=2 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 "FileChannelChild.h"
+
+#include "mozilla/Unused.h"
+#include "mozilla/net/NeckoChild.h"
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS_INHERITED(FileChannelChild, nsFileChannel, nsIChildChannel)
+
+FileChannelChild::FileChannelChild(nsIURI *uri)
+  : nsFileChannel(uri)
+  , mIPCOpen(false)
+{
+}
+
+NS_IMETHODIMP
+FileChannelChild::ConnectParent(uint32_t id)
+{
+  if (!gNeckoChild->SendPFileChannelConstructor(this, id)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  AddIPDLReference();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileChannelChild::CompleteRedirectSetup(nsIStreamListener *listener,
+                                        nsISupports *ctx)
+{
+  nsresult rv;
+
+  if (mLoadInfo && mLoadInfo->GetEnforceSecurity()) {
+    MOZ_ASSERT(!ctx, "Context should be null");
+    rv = AsyncOpen2(listener);
+  } else {
+    rv = AsyncOpen(listener, ctx);
+  }
+
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  if (mIPCOpen) {
+    Unused << Send__delete__(this);
+  }
+
+  return NS_OK;
+}
+
+void
+FileChannelChild::AddIPDLReference()
+{
+  AddRef();
+  mIPCOpen = true;
+}
+
+void
+FileChannelChild::ActorDestroy(ActorDestroyReason why)
+{
+  MOZ_ASSERT(mIPCOpen);
+  mIPCOpen = false;
+  Release();
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/file/FileChannelChild.h
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 sts=2 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 mozilla__net__FileChannelChild_h
+#define mozilla__net__FileChannelChild_h
+
+#include "nsFileChannel.h"
+#include "nsIChildChannel.h"
+#include "nsISupportsImpl.h"
+
+#include "mozilla/net/PFileChannelChild.h"
+
+namespace mozilla {
+namespace net {
+
+class FileChannelChild : public nsFileChannel
+                       , public nsIChildChannel
+                       , public PFileChannelChild
+{
+public:
+  explicit FileChannelChild(nsIURI *uri);
+
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSICHILDCHANNEL
+
+protected:
+  virtual void ActorDestroy(ActorDestroyReason why) override;
+
+private:
+  ~FileChannelChild() { };
+
+  void AddIPDLReference();
+
+  bool mIPCOpen;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif /* mozilla__net__FileChannelChild_h */
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/file/FileChannelParent.cpp
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 sts=2 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 "FileChannelParent.h"
+#include "mozilla/Assertions.h"
+#include "nsNetUtil.h"
+#include "nsIChannel.h"
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS(FileChannelParent, nsIParentChannel, nsIStreamListener)
+
+FileChannelParent::~FileChannelParent()
+{
+}
+
+bool
+FileChannelParent::Init(const uint32_t &channelId)
+{
+  nsCOMPtr<nsIChannel> channel;
+  MOZ_ALWAYS_SUCCEEDS(
+      NS_LinkRedirectChannels(channelId, this, getter_AddRefs(channel)));
+
+  return true;
+}
+
+NS_IMETHODIMP
+FileChannelParent::SetParentListener(HttpChannelParentListener* aListener)
+{
+  // Nothing to do.
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileChannelParent::NotifyTrackingProtectionDisabled()
+{
+  // Nothing to do.
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileChannelParent::NotifyTrackingResource()
+{
+  // Nothing to do.
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileChannelParent::SetClassifierMatchedInfo(const nsACString& aList,
+                                            const nsACString& aProvider,
+                                            const nsACString& aPrefix)
+{
+  // nothing to do
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileChannelParent::Delete()
+{
+  // Nothing to do.
+  return NS_OK;
+}
+
+void
+FileChannelParent::ActorDestroy(ActorDestroyReason why)
+{
+}
+
+NS_IMETHODIMP
+FileChannelParent::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
+FileChannelParent::OnStopRequest(nsIRequest *aRequest,
+                                 nsISupports *aContext,
+                                 nsresult aStatusCode)
+{
+  // See above.
+  MOZ_ASSERT(NS_FAILED(aStatusCode));
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileChannelParent::OnDataAvailable(nsIRequest *aRequest,
+                                   nsISupports *aContext,
+                                   nsIInputStream *aInputStream,
+                                   uint64_t aOffset,
+                                   uint32_t aCount)
+{
+  // See above.
+  MOZ_CRASH("Should never be called");
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/file/FileChannelParent.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 mozilla__net__FileChannelParent_h
+#define mozilla__net__FileChannelParent_h
+
+#include "nsIParentChannel.h"
+#include "nsISupportsImpl.h"
+
+#include "mozilla/net/PFileChannelParent.h"
+
+namespace mozilla {
+namespace net {
+
+// In order to support HTTP redirects to file:, we need to implement the HTTP
+// redirection API, which requires a class that implements nsIParentChannel
+// and which calls NS_LinkRedirectChannels.
+class FileChannelParent : public nsIParentChannel
+                        , public PFileChannelParent
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIPARENTCHANNEL
+  NS_DECL_NSIREQUESTOBSERVER
+  NS_DECL_NSISTREAMLISTENER
+
+  MOZ_MUST_USE bool Init(const uint32_t& aArgs);
+
+private:
+  ~FileChannelParent();
+
+  virtual void ActorDestroy(ActorDestroyReason why) override;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif /* mozilla__net__FileChannelParent_h */
--- a/netwerk/protocol/file/moz.build
+++ b/netwerk/protocol/file/moz.build
@@ -3,31 +3,36 @@
 # 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/.
 
 with Files('**'):
     BUG_COMPONENT = ('Core', 'Networking: File')
 
 EXPORTS.mozilla.net += [
+    'FileChannelParent.h',
     'nsFileProtocolHandler.h',
 ]
 
 XPIDL_SOURCES += [
     'nsIFileChannel.idl',
     'nsIFileProtocolHandler.idl',
 ]
 
 XPIDL_MODULE = 'necko_file'
 
 UNIFIED_SOURCES += [
+    'FileChannelChild.cpp',
+    'FileChannelParent.cpp',
     'nsFileChannel.cpp',
     'nsFileProtocolHandler.cpp',
 ]
 
+include('/ipc/chromium/chromium-config.mozbuild')
+
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '/netwerk/base',
 ]
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wno-error=shadow']
--- a/netwerk/protocol/file/nsFileProtocolHandler.cpp
+++ b/netwerk/protocol/file/nsFileProtocolHandler.cpp
@@ -7,16 +7,18 @@
 #include "nsIFile.h"
 #include "nsFileProtocolHandler.h"
 #include "nsFileChannel.h"
 #include "nsStandardURL.h"
 #include "nsURLHelper.h"
 
 #include "nsNetUtil.h"
 
+#include "FileChannelChild.h"
+
 // URL file handling, copied and modified from xpfe/components/bookmarks/src/nsBookmarksService.cpp
 #ifdef XP_WIN
 #include <shlobj.h>
 #include <intshcut.h>
 #include "nsIFileURL.h"
 #ifdef CompareString
 #undef CompareString
 #endif
@@ -183,17 +185,22 @@ nsFileProtocolHandler::NewURI(const nsAC
     return CallQueryInterface(url, result);
 }
 
 NS_IMETHODIMP
 nsFileProtocolHandler::NewChannel2(nsIURI* uri,
                                    nsILoadInfo* aLoadInfo,
                                    nsIChannel** result)
 {
-    nsFileChannel *chan = new nsFileChannel(uri);
+    nsFileChannel *chan;
+    if (IsNeckoChild()) {
+        chan = new mozilla::net::FileChannelChild(uri);
+    } else {
+        chan = new nsFileChannel(uri);
+    }
     if (!chan)
         return NS_ERROR_OUT_OF_MEMORY;
     NS_ADDREF(chan);
 
     nsresult rv = chan->Init();
     if (NS_FAILED(rv)) {
         NS_RELEASE(chan);
         return rv;