Bug 536289 - Part 2: Make FTP work for fennec. r=jduell a=blocking-fennec
authorAlon Zakai <azakai>
Tue, 10 Aug 2010 14:47:00 -0400
changeset 56142 b2cb926d1ea145b44a08a4558aa147371334b597
parent 56141 290d90eb8ee89dda239ae4ca1c9b331d3d5c625b
child 56143 da156c724d1a0b93e3ff65ace3c4d8e9f6186565
push idunknown
push userunknown
push dateunknown
reviewersjduell, blocking-fennec
bugs536289
milestone2.0b8pre
Bug 536289 - Part 2: Make FTP work for fennec. r=jduell a=blocking-fennec
ipc/ipdl/Makefile.in
netwerk/base/src/nsBaseChannel.h
netwerk/ipc/NeckoChild.cpp
netwerk/ipc/NeckoChild.h
netwerk/ipc/NeckoCommon.h
netwerk/ipc/NeckoParent.cpp
netwerk/ipc/NeckoParent.h
netwerk/ipc/PNecko.ipdl
netwerk/protocol/ftp/FTPChannelChild.cpp
netwerk/protocol/ftp/FTPChannelChild.h
netwerk/protocol/ftp/FTPChannelParent.cpp
netwerk/protocol/ftp/FTPChannelParent.h
netwerk/protocol/ftp/Makefile.in
netwerk/protocol/ftp/PFTPChannel.ipdl
netwerk/protocol/ftp/ipdl.mk
netwerk/protocol/ftp/nsFTPChannel.h
netwerk/protocol/ftp/nsFtpProtocolHandler.cpp
netwerk/protocol/ftp/nsFtpProtocolHandler.h
netwerk/protocol/http/HttpBaseChannel.h
--- a/ipc/ipdl/Makefile.in
+++ b/ipc/ipdl/Makefile.in
@@ -59,16 +59,17 @@ IPDLDIRS =  \
   dom/ipc  \
   gfx/layers/ipc \
   ipc/ipdl/test/cxx  \
   ipc/testshell  \
   js/ipc  \
   js/jetpack \
   layout/ipc \
   netwerk/ipc  \
+  netwerk/protocol/ftp \
   netwerk/protocol/http  \
   netwerk/protocol/wyciwyg \
   netwerk/cookie  \
   $(NULL)
 ##-----------------------------------------------------------------------------
 
 ifdef MOZ_IPDL_TESTS
 DIRS += test
--- a/netwerk/base/src/nsBaseChannel.h
+++ b/netwerk/base/src/nsBaseChannel.h
@@ -276,27 +276,29 @@ private:
   };
   friend class RedirectRunnable;
 
   nsRefPtr<nsInputStreamPump>         mPump;
   nsCOMPtr<nsIInterfaceRequestor>     mCallbacks;
   nsCOMPtr<nsIProgressEventSink>      mProgressSink;
   nsCOMPtr<nsIURI>                    mOriginalURI;
   nsCOMPtr<nsIURI>                    mURI;
-  nsCOMPtr<nsILoadGroup>              mLoadGroup;
   nsCOMPtr<nsISupports>               mOwner;
   nsCOMPtr<nsISupports>               mSecurityInfo;
-  nsCOMPtr<nsIStreamListener>         mListener;
-  nsCOMPtr<nsISupports>               mListenerContext;
   nsCOMPtr<nsIChannel>                mRedirectChannel;
   nsCString                           mContentType;
   nsCString                           mContentCharset;
   PRUint32                            mLoadFlags;
-  nsresult                            mStatus;
   PRPackedBool                        mQueriedProgressSink;
   PRPackedBool                        mSynthProgressEvents;
   PRPackedBool                        mWasOpened;
   PRPackedBool                        mWaitingOnAsyncRedirect;
   PRPackedBool                        mOpenRedirectChannel;
   PRUint32                            mRedirectFlags;
+
+protected:
+  nsCOMPtr<nsILoadGroup>              mLoadGroup;
+  nsCOMPtr<nsIStreamListener>         mListener;
+  nsCOMPtr<nsISupports>               mListenerContext;
+  nsresult                            mStatus;
 };
 
 #endif // !nsBaseChannel_h__
--- a/netwerk/ipc/NeckoChild.cpp
+++ b/netwerk/ipc/NeckoChild.cpp
@@ -40,16 +40,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsHttp.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/net/HttpChannelChild.h"
 #include "mozilla/net/CookieServiceChild.h"
 #include "mozilla/net/WyciwygChannelChild.h"
+#include "mozilla/net/FTPChannelChild.h"
 
 namespace mozilla {
 namespace net {
 
 PNeckoChild *gNeckoChild = nsnull;
 
 // C++ file contents
 NeckoChild::NeckoChild()
@@ -107,16 +108,34 @@ NeckoChild::DeallocPHttpChannel(PHttpCha
 {
   NS_ABORT_IF_FALSE(IsNeckoChild(), "DeallocPHttpChannel called by non-child!");
 
   HttpChannelChild* child = static_cast<HttpChannelChild*>(channel);
   child->ReleaseIPDLReference();
   return true;
 }
 
+PFTPChannelChild*
+NeckoChild::AllocPFTPChannel()
+{
+  // We don't allocate here: see FTPChannelChild::AsyncOpen()
+  NS_RUNTIMEABORT("AllocPFTPChannel should not be called");
+  return nsnull;
+}
+
+bool
+NeckoChild::DeallocPFTPChannel(PFTPChannelChild* channel)
+{
+  NS_ABORT_IF_FALSE(IsNeckoChild(), "DeallocPFTPChannel called by non-child!");
+
+  FTPChannelChild* child = static_cast<FTPChannelChild*>(channel);
+  child->ReleaseIPDLReference();
+  return true;
+}
+
 PCookieServiceChild* 
 NeckoChild::AllocPCookieService()
 {
   // We don't allocate here: see nsCookieService::GetSingleton()
   NS_NOTREACHED("AllocPCookieService should not be called");
   return nsnull;
 }
 
--- a/netwerk/ipc/NeckoChild.h
+++ b/netwerk/ipc/NeckoChild.h
@@ -60,16 +60,18 @@ public:
 
 protected:
   virtual PHttpChannelChild* AllocPHttpChannel(PBrowserChild* iframeEmbedding);
   virtual bool DeallocPHttpChannel(PHttpChannelChild*);
   virtual PCookieServiceChild* AllocPCookieService();
   virtual bool DeallocPCookieService(PCookieServiceChild*);
   virtual PWyciwygChannelChild* AllocPWyciwygChannel();
   virtual bool DeallocPWyciwygChannel(PWyciwygChannelChild*);
+  virtual PFTPChannelChild* AllocPFTPChannel();
+  virtual bool DeallocPFTPChannel(PFTPChannelChild*);
 };
 
 /**
  * Reference to the PNecko Child protocol.
  * Null if this is not a content process.
  */
 extern PNeckoChild *gNeckoChild;
 
--- a/netwerk/ipc/NeckoCommon.h
+++ b/netwerk/ipc/NeckoCommon.h
@@ -68,22 +68,30 @@
       msg.Append(" (set NECKO_ERRORS_ARE_FATAL=1 in your environment to "      \
                       "convert this warning into a fatal error.)");            \
       NS_WARNING(msg.get());                                                   \
     }                                                                          \
   } while (0)
 
 #define DROP_DEAD()                                                            \
   do {                                                                         \
-    nsPrintfCString msg(1000,"FATAL NECKO ERROR: '%s' UNIMPLEMENTED",          \
+    nsPrintfCString msg(1000,"NECKO ERROR: '%s' UNIMPLEMENTED",                \
                         __FUNCTION__);                                         \
     NECKO_MAYBE_ABORT(msg);                                                    \
     return NS_ERROR_NOT_IMPLEMENTED;                                           \
   } while (0)
 
+#define ENSURE_CALLED_BEFORE_ASYNC_OPEN()                                      \
+  if (mIsPending || mWasOpened) {                                              \
+    nsPrintfCString msg(1000, "'%s' called after AsyncOpen: %s +%d",           \
+                        __FUNCTION__, __FILE__, __LINE__);                     \
+    NECKO_MAYBE_ABORT(msg);                                                    \
+  }                                                                            \
+  NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);                           \
+  NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
 
 namespace mozilla {
 namespace net {
 
 inline bool 
 IsNeckoChild() 
 {
   static bool didCheck = false;
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -38,16 +38,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #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 "nsHTMLDNSPrefetch.h"
 
 namespace mozilla {
 namespace net {
 
 // C++ file contents
 NeckoParent::NeckoParent()
@@ -69,16 +70,32 @@ NeckoParent::AllocPHttpChannel(PBrowserP
 bool 
 NeckoParent::DeallocPHttpChannel(PHttpChannelParent* channel)
 {
   HttpChannelParent *p = static_cast<HttpChannelParent *>(channel);
   p->Release();
   return true;
 }
 
+PFTPChannelParent*
+NeckoParent::AllocPFTPChannel()
+{
+  FTPChannelParent *p = new FTPChannelParent();
+  p->AddRef();
+  return p;
+}
+
+bool
+NeckoParent::DeallocPFTPChannel(PFTPChannelParent* channel)
+{
+  FTPChannelParent *p = static_cast<FTPChannelParent *>(channel);
+  p->Release();
+  return true;
+}
+
 PCookieServiceParent* 
 NeckoParent::AllocPCookieService()
 {
   return new CookieServiceParent();
 }
 
 bool 
 NeckoParent::DeallocPCookieService(PCookieServiceParent* cs)
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -57,16 +57,18 @@ public:
 
 protected:
   virtual PHttpChannelParent* AllocPHttpChannel(PBrowserParent* browser);
   virtual bool DeallocPHttpChannel(PHttpChannelParent*);
   virtual PCookieServiceParent* AllocPCookieService();
   virtual bool DeallocPCookieService(PCookieServiceParent*);
   virtual PWyciwygChannelParent* AllocPWyciwygChannel();
   virtual bool DeallocPWyciwygChannel(PWyciwygChannelParent*);
+  virtual PFTPChannelParent* AllocPFTPChannel();
+  virtual bool DeallocPFTPChannel(PFTPChannelParent*);
   virtual bool RecvHTMLDNSPrefetch(const nsString& hostname,
                                    const PRUint16& flags);
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // mozilla_net_NeckoParent_h
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -38,34 +38,37 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 include protocol PContent;
 include protocol PHttpChannel;
 include protocol PCookieService;
 include protocol PBrowser;
 include protocol PWyciwygChannel;
+include protocol PFTPChannel;
 
 namespace mozilla {
 namespace net {
 
 
 //-------------------------------------------------------------------
 sync protocol PNecko
 {
   manager PContent;
   manages PHttpChannel;
   manages PCookieService;
   manages PWyciwygChannel;
+  manages PFTPChannel;
 
 parent:
   __delete__();
 
   PCookieService();
   PWyciwygChannel();
+  PFTPChannel();
 
   HTMLDNSPrefetch(nsString hostname, PRUint16 flags);
 
 both:
   PHttpChannel(nullable PBrowser browser);
 };
 
 
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/ftp/FTPChannelChild.cpp
@@ -0,0 +1,480 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ *  The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Alon Zakai <azakai@mozilla.com>
+ *   Josh Matthews <josh@joshmatthews.net>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "mozilla/net/NeckoChild.h"
+#include "mozilla/net/FTPChannelChild.h"
+#include "nsFtpProtocolHandler.h"
+
+#include "nsStringStream.h"
+#include "nsMimeTypes.h"
+#include "nsNetUtil.h"
+#include "nsIURIFixup.h"
+#include "nsCDefaultURIFixup.h"
+
+#undef LOG
+#define LOG(args) PR_LOG(gFTPLog, PR_LOG_DEBUG, args)
+
+namespace mozilla {
+namespace net {
+
+FTPChannelChild::FTPChannelChild(nsIURI* uri)
+: ChannelEventQueue<FTPChannelChild>(this)
+, mIPCOpen(false)
+, mCanceled(false)
+, mSuspendCount(0)
+, mIsPending(PR_FALSE)
+, mWasOpened(PR_FALSE)
+, mLastModifiedTime(0)
+, mStartPos(0)
+{
+  LOG(("Creating FTPChannelChild @%x\n", this));
+  // grab a reference to the handler to ensure that it doesn't go away.
+  NS_ADDREF(gFtpHandler);
+  SetURI(uri);
+}
+
+FTPChannelChild::~FTPChannelChild()
+{
+  LOG(("Destroying FTPChannelChild @%x\n", this));
+  gFtpHandler->Release();
+}
+
+void
+FTPChannelChild::AddIPDLReference()
+{
+  NS_ABORT_IF_FALSE(!mIPCOpen, "Attempt to retain more than one IPDL reference");
+  mIPCOpen = true;
+  AddRef();
+}
+
+void
+FTPChannelChild::ReleaseIPDLReference()
+{
+  NS_ABORT_IF_FALSE(mIPCOpen, "Attempt to release nonexistent IPDL reference");
+  mIPCOpen = false;
+  Release();
+}
+
+//-----------------------------------------------------------------------------
+// FTPChannelChild::nsISupports
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS_INHERITED4(FTPChannelChild,
+                             nsBaseChannel,
+                             nsIFTPChannel,
+                             nsIUploadChannel,
+                             nsIResumableChannel,
+                             nsIProxiedChannel)
+
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+FTPChannelChild::GetLastModifiedTime(PRTime* lastModifiedTime)
+{
+  *lastModifiedTime = mLastModifiedTime;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::SetLastModifiedTime(PRTime lastModifiedTime)
+{
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::ResumeAt(PRUint64 aStartPos, const nsACString& aEntityID)
+{
+  NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
+  mStartPos = aStartPos;
+  mEntityID = aEntityID;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::GetEntityID(nsACString& entityID)
+{
+  entityID = mEntityID;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::GetProxyInfo(nsIProxyInfo** aProxyInfo)
+{
+  DROP_DEAD();
+}
+
+NS_IMETHODIMP
+FTPChannelChild::SetUploadStream(nsIInputStream* stream,
+                                 const nsACString& contentType,
+                                 PRInt32 contentLength)
+{
+  NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
+  mUploadStream = stream;
+  // NOTE: contentLength is intentionally ignored here.
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::GetUploadStream(nsIInputStream** stream)
+{
+  NS_ENSURE_ARG_POINTER(stream);
+  *stream = mUploadStream;
+  NS_IF_ADDREF(*stream);
+  return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+FTPChannelChild::AsyncOpen(::nsIStreamListener* listener, nsISupports* aContext)
+{
+  LOG(("FTPChannelChild::AsyncOpen [this=%x]\n", this));
+
+  NS_ENSURE_TRUE((gNeckoChild), NS_ERROR_FAILURE);
+  NS_ENSURE_ARG_POINTER(listener);
+  NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
+  NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
+
+  // Port checked in parent, but duplicate here so we can return with error
+  // immediately, as we've done since before e10s.
+  nsresult rv;
+  rv = NS_CheckPortSafety(nsBaseChannel::URI()); // Need to disambiguate,
+                                                 // because in the child ipdl,
+                                                 // a typedef URI is defined...
+  if (NS_FAILED(rv))
+    return rv;
+
+  // FIXME: like bug 558623, merge constructor+SendAsyncOpen into 1 IPC msg
+  gNeckoChild->SendPFTPChannelConstructor(this);
+  mListener = listener;
+  mListenerContext = aContext;
+
+  // add ourselves to the load group. 
+  if (mLoadGroup)
+    mLoadGroup->AddRequest(this, nsnull);
+
+  SendAsyncOpen(nsBaseChannel::URI(), mStartPos, mEntityID,
+                IPC::InputStream(mUploadStream));
+
+  // The socket transport layer in the chrome process now has a logical ref to
+  // us until OnStopRequest is called.
+  AddIPDLReference();
+
+  mIsPending = PR_TRUE;
+  mWasOpened = PR_TRUE;
+
+  return rv;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::IsPending(PRBool* result)
+{
+  *result = mIsPending;
+  return NS_OK;
+}
+
+nsresult
+FTPChannelChild::OpenContentStream(PRBool async,
+                                   nsIInputStream** stream,
+                                   nsIChannel** channel)
+{
+  NS_RUNTIMEABORT("FTPChannel*Child* should never have OpenContentStream called!");
+  return NS_OK;
+}
+  
+//-----------------------------------------------------------------------------
+// FTPChannelChild::PFTPChannelChild
+//-----------------------------------------------------------------------------
+
+class FTPStartRequestEvent : public ChannelEvent
+{
+ public:
+  FTPStartRequestEvent(FTPChannelChild* aChild, const PRInt32& aContentLength,
+                       const nsCString& aContentType, const PRTime& aLastModified,
+                       const nsCString& aEntityID, const IPC::URI& aURI)
+  : mChild(aChild), mContentLength(aContentLength), mContentType(aContentType),
+    mLastModified(aLastModified), mEntityID(aEntityID), mURI(aURI) {}
+  void Run() { mChild->DoOnStartRequest(mContentLength, mContentType,
+                                       mLastModified, mEntityID, mURI); }
+ private:
+  FTPChannelChild* mChild;
+  PRInt32 mContentLength;
+  nsCString mContentType;
+  PRTime mLastModified;
+  nsCString mEntityID;
+  IPC::URI mURI;
+};
+
+bool
+FTPChannelChild::RecvOnStartRequest(const PRInt32& aContentLength,
+                                    const nsCString& aContentType,
+                                    const PRTime& aLastModified,
+                                    const nsCString& aEntityID,
+                                    const IPC::URI& aURI)
+{
+  if (ShouldEnqueue()) {
+    EnqueueEvent(new FTPStartRequestEvent(this, aContentLength, aContentType,
+                                          aLastModified, aEntityID, aURI));
+  } else {
+    DoOnStartRequest(aContentLength, aContentType, aLastModified,
+                     aEntityID, aURI);
+  }
+  return true;
+}
+
+void
+FTPChannelChild::DoOnStartRequest(const PRInt32& aContentLength,
+                                  const nsCString& aContentType,
+                                  const PRTime& aLastModified,
+                                  const nsCString& aEntityID,
+                                  const IPC::URI& aURI)
+{
+  LOG(("FTPChannelChild::RecvOnStartRequest [this=%x]\n", this));
+
+  SetContentLength(aContentLength);
+  SetContentType(aContentType);
+  mLastModifiedTime = aLastModified;
+  mEntityID = aEntityID;
+
+  nsCString spec;
+  nsCOMPtr<nsIURI> uri(aURI);
+  uri->GetSpec(spec);
+  nsBaseChannel::URI()->SetSpec(spec);
+
+  AutoEventEnqueuer ensureSerialDispatch(this);
+  nsresult rv = mListener->OnStartRequest(this, mListenerContext);
+  if (NS_FAILED(rv))
+    Cancel(rv);
+}
+
+class FTPDataAvailableEvent : public ChannelEvent
+{
+ public:
+  FTPDataAvailableEvent(FTPChannelChild* aChild, const nsCString& aData,
+                        const PRUint32& aOffset, const PRUint32& aCount)
+  : mChild(aChild), mData(aData), mOffset(aOffset), mCount(aCount) {}
+  void Run() { mChild->DoOnDataAvailable(mData, mOffset, mCount); }
+ private:
+  FTPChannelChild* mChild;
+  nsCString mData;
+  PRUint32 mOffset, mCount;
+};
+
+bool
+FTPChannelChild::RecvOnDataAvailable(const nsCString& data,
+                                     const PRUint32& offset,
+                                     const PRUint32& count)
+{
+  if (ShouldEnqueue()) {
+    EnqueueEvent(new FTPDataAvailableEvent(this, data, offset, count));
+  } else {
+    DoOnDataAvailable(data, offset, count);
+  }
+  return true;
+}
+
+void
+FTPChannelChild::DoOnDataAvailable(const nsCString& data,
+                                   const PRUint32& offset,
+                                   const PRUint32& count)
+{
+  LOG(("FTPChannelChild::RecvOnDataAvailable [this=%x]\n", this));
+
+  if (mCanceled)
+    return;
+
+  // NOTE: the OnDataAvailable contract requires the client to read all the data
+  // in the inputstream.  This code relies on that ('data' will go away after
+  // this function).  Apparently the previous, non-e10s behavior was to actually
+  // support only reading part of the data, allowing later calls to read the
+  // rest.
+  nsCOMPtr<nsIInputStream> stringStream;
+  nsresult rv = NS_NewByteInputStream(getter_AddRefs(stringStream),
+                                      data.get(),
+                                      count,
+                                      NS_ASSIGNMENT_DEPEND);
+  if (NS_FAILED(rv)) {
+    Cancel(rv);
+    return;
+  }
+
+  AutoEventEnqueuer ensureSerialDispatch(this);
+  rv = mListener->OnDataAvailable(this, mListenerContext,
+                                  stringStream, offset, count);
+  if (NS_FAILED(rv))
+    Cancel(rv);
+  stringStream->Close();
+}
+
+class FTPStopRequestEvent : public ChannelEvent
+{
+ public:
+  FTPStopRequestEvent(FTPChannelChild* aChild, const nsresult& aStatusCode)
+  : mChild(aChild), mStatusCode(aStatusCode) {}
+  void Run() { mChild->DoOnStopRequest(mStatusCode); }
+ private:
+  FTPChannelChild* mChild;
+  nsresult mStatusCode;
+};
+
+bool
+FTPChannelChild::RecvOnStopRequest(const nsresult& statusCode)
+{
+  if (ShouldEnqueue()) {
+    EnqueueEvent(new FTPStopRequestEvent(this, statusCode));
+  } else {
+    DoOnStopRequest(statusCode);
+  }
+  return true;
+}
+
+void
+FTPChannelChild::DoOnStopRequest(const nsresult& statusCode)
+{
+  LOG(("FTPChannelChild::RecvOnStopRequest [this=%x status=%u]\n",
+           this, statusCode));
+
+  if (!mCanceled)
+    mStatus = statusCode;
+
+  { // Ensure that all queued ipdl events are dispatched before
+    // we initiate protocol deletion below.
+    mIsPending = PR_FALSE;
+    AutoEventEnqueuer ensureSerialDispatch(this);
+    (void)mListener->OnStopRequest(this, mListenerContext, statusCode);
+    mListener = nsnull;
+    mListenerContext = nsnull;
+
+    if (mLoadGroup)
+      mLoadGroup->RemoveRequest(this, nsnull, statusCode);
+  }
+
+  // This calls NeckoChild::DeallocPFTPChannel(), which deletes |this| if IPDL
+  // holds the last reference.  Don't rely on |this| existing after here!
+  Send__delete__(this);
+}
+
+class FTPCancelEarlyEvent : public ChannelEvent
+{
+ public:
+  FTPCancelEarlyEvent(FTPChannelChild* aChild, nsresult aStatus)
+  : mChild(aChild), mStatus(aStatus) {}
+  void Run() { mChild->DoCancelEarly(mStatus); }
+ private:
+  FTPChannelChild* mChild;
+  nsresult mStatus;
+};
+
+bool
+FTPChannelChild::RecvCancelEarly(const nsresult& statusCode)
+{
+  if (ShouldEnqueue()) {
+    EnqueueEvent(new FTPCancelEarlyEvent(this, statusCode));
+  } else {
+    DoCancelEarly(statusCode);
+  }
+  return true;
+}
+
+void
+FTPChannelChild::DoCancelEarly(const nsresult& statusCode)
+{
+  if (mCanceled)
+    return;
+
+  mCanceled = true;
+  mStatus = statusCode;
+  mIsPending = PR_FALSE;
+  
+  if (mLoadGroup)
+    mLoadGroup->RemoveRequest(this, nsnull, statusCode);
+
+  if (mListener) {
+    mListener->OnStartRequest(this, mListenerContext);
+    mListener->OnStopRequest(this, mListenerContext, statusCode);
+  }
+
+  mListener = nsnull;
+  mListenerContext = nsnull;
+
+  if (mIPCOpen)
+    Send__delete__(this);
+}
+
+NS_IMETHODIMP
+FTPChannelChild::Cancel(nsresult status)
+{
+  if (mCanceled)
+    return NS_OK;
+
+  mCanceled = true;
+  mStatus = status;
+  if (mIPCOpen)
+    SendCancel(status);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::Suspend()
+{
+  NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE);
+  mSuspendCount++;
+  SendSuspend();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelChild::Resume()
+{
+  NS_ENSURE_TRUE(mIPCOpen, NS_ERROR_NOT_AVAILABLE);
+  SendResume();
+  --mSuspendCount;
+  if (!mSuspendCount) {
+    if (mQueuePhase == PHASE_UNQUEUED)
+      mQueuePhase = PHASE_FINISHED_QUEUEING;
+    FlushEventQueue();
+  }
+  return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/ftp/FTPChannelChild.h
@@ -0,0 +1,151 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ *  The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Alon Zakai <azakai@mozilla.com>
+ *   Josh Matthews <josh@joshmatthews.net>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_net_FTPChannelChild_h
+#define mozilla_net_FTPChannelChild_h
+
+#include "mozilla/net/PFTPChannelChild.h"
+#include "mozilla/net/ChannelEventQueue.h"
+#include "nsBaseChannel.h"
+#include "nsIFTPChannel.h"
+#include "nsIUploadChannel.h"
+#include "nsIProxiedChannel.h"
+#include "nsIResumableChannel.h"
+
+#include "nsIStreamListener.h"
+
+namespace mozilla {
+namespace net {
+
+// This class inherits logic from nsBaseChannel that is not needed for an
+// e10s child channel, but it works.  At some point we could slice up
+// nsBaseChannel and have a new class that has only the common logic for
+// nsFTPChannel/FTPChannelChild.
+
+class FTPChannelChild : public PFTPChannelChild
+                      , public nsBaseChannel
+                      , public nsIFTPChannel
+                      , public nsIUploadChannel
+                      , public nsIResumableChannel
+                      , public nsIProxiedChannel
+                      , public ChannelEventQueue<FTPChannelChild>
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIFTPCHANNEL
+  NS_DECL_NSIUPLOADCHANNEL
+  NS_DECL_NSIRESUMABLECHANNEL
+  NS_DECL_NSIPROXIEDCHANNEL
+
+  NS_IMETHOD Cancel(nsresult status);
+  NS_IMETHOD Suspend();
+  NS_IMETHOD Resume();
+
+  FTPChannelChild(nsIURI* uri);
+  virtual ~FTPChannelChild();
+
+  void AddIPDLReference();
+  void ReleaseIPDLReference();
+
+  NS_IMETHOD AsyncOpen(::nsIStreamListener* listener, nsISupports* aContext);
+
+  // Note that we handle this ourselves, overriding the nsBaseChannel
+  // default behavior, in order to be e10s-friendly.
+  NS_IMETHOD IsPending(PRBool* result);
+
+  NS_OVERRIDE nsresult OpenContentStream(PRBool async,
+                                         nsIInputStream** stream,
+                                         nsIChannel** channel);
+
+  bool IsSuspended();
+
+protected:
+  NS_OVERRIDE bool RecvOnStartRequest(const PRInt32& aContentLength,
+                                      const nsCString& aContentType,
+                                      const PRTime& aLastModified,
+                                      const nsCString& aEntityID,
+                                      const IPC::URI& aURI);
+  NS_OVERRIDE bool RecvOnDataAvailable(const nsCString& data,
+                                       const PRUint32& offset,
+                                       const PRUint32& count);
+  NS_OVERRIDE bool RecvOnStopRequest(const nsresult& statusCode);
+  NS_OVERRIDE bool RecvCancelEarly(const nsresult& statusCode);
+
+  void DoOnStartRequest(const PRInt32& aContentLength,
+                        const nsCString& aContentType,
+                        const PRTime& aLastModified,
+                        const nsCString& aEntityID,
+                        const IPC::URI& aURI);
+  void DoOnDataAvailable(const nsCString& data,
+                         const PRUint32& offset,
+                         const PRUint32& count);
+  void DoOnStopRequest(const nsresult& statusCode);
+  void DoCancelEarly(const nsresult& statusCode);
+
+  friend class FTPStartRequestEvent;
+  friend class FTPDataAvailableEvent;
+  friend class FTPStopRequestEvent;
+  friend class FTPCancelEarlyEvent;
+
+private:
+  nsCOMPtr<nsIInputStream> mUploadStream;
+
+  bool mIPCOpen;
+  bool mCanceled;
+  PRUint32 mSuspendCount;
+  PRPackedBool mIsPending;
+  PRPackedBool mWasOpened;
+  
+  PRTime mLastModifiedTime;
+  PRUint64 mStartPos;
+  nsCString mEntityID;
+};
+
+inline bool
+FTPChannelChild::IsSuspended()
+{
+  return mSuspendCount != 0;
+}
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_FTPChannelChild_h
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/ftp/FTPChannelParent.cpp
@@ -0,0 +1,233 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ *  The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Alon Zakai <azakai@mozilla.com>
+ *   Josh Matthews <josh@joshmatthews.net>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "mozilla/net/FTPChannelParent.h"
+#include "nsFTPChannel.h"
+#include "nsNetUtil.h"
+#include "nsISupportsPriority.h"
+#include "nsFtpProtocolHandler.h"
+
+#undef LOG
+#define LOG(args) PR_LOG(gFTPLog, PR_LOG_DEBUG, args)
+
+namespace mozilla {
+namespace net {
+
+FTPChannelParent::FTPChannelParent()
+: mIPCClosed(false)
+{
+  nsIProtocolHandler* handler;
+  CallGetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "ftp", &handler);
+  NS_ASSERTION(handler, "no ftp handler");
+}
+
+FTPChannelParent::~FTPChannelParent()
+{
+  gFtpHandler->Release();
+}
+
+void
+FTPChannelParent::ActorDestroy(ActorDestroyReason why)
+{
+  // We may still have refcount>0 if the channel hasn't called OnStopRequest
+  // yet, but we must not send any more msgs to child.
+  mIPCClosed = true;
+}
+
+//-----------------------------------------------------------------------------
+// FTPChannelParent::nsISupports
+//-----------------------------------------------------------------------------
+
+NS_IMPL_ISUPPORTS3(FTPChannelParent,
+                   nsIStreamListener,
+                   nsIInterfaceRequestor,
+                   nsIRequestObserver);
+
+//-----------------------------------------------------------------------------
+// FTPChannelParent::PFTPChannelParent
+//-----------------------------------------------------------------------------
+
+bool
+FTPChannelParent::RecvAsyncOpen(const IPC::URI& aURI,
+                                const PRUint64& aStartPos,
+                                const nsCString& aEntityID,
+                                const IPC::InputStream& aUploadStream)
+{
+  nsCOMPtr<nsIURI> uri(aURI);
+
+#ifdef DEBUG
+  nsCString uriSpec;
+  uri->GetSpec(uriSpec);
+  LOG(("FTPChannelParent RecvAsyncOpen [this=%x uri=%s]\n",
+       this, uriSpec.get()));
+#endif
+
+  nsresult rv;
+  nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv));
+  if (NS_FAILED(rv))
+    return SendCancelEarly(rv);
+
+  nsCOMPtr<nsIChannel> chan;
+  rv = NS_NewChannel(getter_AddRefs(chan), uri, ios);
+  if (NS_FAILED(rv))
+    return SendCancelEarly(rv);
+
+  mChannel = static_cast<nsFtpChannel*>(chan.get());
+  
+  nsCOMPtr<nsIInputStream> upload(aUploadStream);
+  if (upload) {
+    // contentType and contentLength are ignored
+    rv = mChannel->SetUploadStream(upload, EmptyCString(), 0);
+    if (NS_FAILED(rv))
+      return SendCancelEarly(rv);
+  }
+
+  rv = mChannel->ResumeAt(aStartPos, aEntityID);
+  if (NS_FAILED(rv))
+    return SendCancelEarly(rv);
+
+  rv = mChannel->AsyncOpen(this, nsnull);
+  if (NS_FAILED(rv))
+    return SendCancelEarly(rv);
+  
+  return true;
+}
+
+bool
+FTPChannelParent::RecvCancel(const nsresult& status)
+{
+  mChannel->Cancel(status);
+  return true;
+}
+
+bool
+FTPChannelParent::RecvSuspend()
+{
+  mChannel->Suspend();
+  return true;
+}
+
+bool
+FTPChannelParent::RecvResume()
+{
+  mChannel->Resume();
+  return true;
+}
+
+//-----------------------------------------------------------------------------
+// FTPChannelParent::nsIRequestObserver
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+FTPChannelParent::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
+{
+  LOG(("FTPChannelParent::OnStartRequest [this=%x]\n", this));
+
+  nsFtpChannel* chan = static_cast<nsFtpChannel*>(aRequest);
+  PRInt32 aContentLength;
+  chan->GetContentLength(&aContentLength);
+  nsCString contentType;
+  chan->GetContentType(contentType);
+  nsCString entityID;
+  chan->GetEntityID(entityID);
+  PRTime lastModified;
+  chan->GetLastModifiedTime(&lastModified);
+
+  if (mIPCClosed || !SendOnStartRequest(aContentLength, contentType,
+                                       lastModified, entityID, chan->URI())) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FTPChannelParent::OnStopRequest(nsIRequest* aRequest,
+                                nsISupports* aContext,
+                                nsresult aStatusCode)
+{
+  LOG(("FTPChannelParent::OnStopRequest: [this=%x status=%ul]\n",
+       this, aStatusCode));
+
+  if (mIPCClosed || !SendOnStopRequest(aStatusCode)) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// FTPChannelParent::nsIStreamListener
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+FTPChannelParent::OnDataAvailable(nsIRequest* aRequest,
+                                  nsISupports* aContext,
+                                  nsIInputStream* aInputStream,
+                                  PRUint32 aOffset,
+                                  PRUint32 aCount)
+{
+  LOG(("FTPChannelParent::OnDataAvailable [this=%x]\n", this));
+  
+  nsCString data;
+  nsresult rv = NS_ReadInputStreamToString(aInputStream, data, aCount);
+  if (NS_FAILED(rv))
+    return rv;
+
+  if (mIPCClosed || !SendOnDataAvailable(data, aOffset, aCount))
+    return NS_ERROR_UNEXPECTED;
+
+  return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// FTPChannelParent::nsIInterfaceRequestor
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+FTPChannelParent::GetInterface(const nsIID& uuid, void** result)
+{
+  DROP_DEAD();
+}
+
+} // namespace net
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/ftp/FTPChannelParent.h
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ *  The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Alon Zakai <azakai@mozilla.com>
+ *   Josh Matthews <josh@joshmatthews.net>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_net_FTPChannelParent_h
+#define mozilla_net_FTPChannelParent_h
+
+#include "mozilla/net/PFTPChannelParent.h"
+#include "mozilla/net/NeckoCommon.h"
+#include "nsIStreamListener.h"
+#include "nsIInterfaceRequestor.h"
+
+class nsFtpChannel;
+
+namespace mozilla {
+namespace net {
+
+class FTPChannelParent : public PFTPChannelParent
+                       , public nsIStreamListener
+                       , public nsIInterfaceRequestor
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIREQUESTOBSERVER
+  NS_DECL_NSISTREAMLISTENER
+  NS_DECL_NSIINTERFACEREQUESTOR
+
+  FTPChannelParent();
+  virtual ~FTPChannelParent();
+
+protected:
+  NS_OVERRIDE virtual bool RecvAsyncOpen(const IPC::URI& uri,
+                                         const PRUint64& startPos,
+                                         const nsCString& entityID,
+                                         const IPC::InputStream& uploadStream);
+  NS_OVERRIDE virtual bool RecvCancel(const nsresult& status);
+  NS_OVERRIDE virtual bool RecvSuspend();
+  NS_OVERRIDE virtual bool RecvResume();
+
+  NS_OVERRIDE virtual void ActorDestroy(ActorDestroyReason why);
+
+  nsRefPtr<nsFtpChannel> mChannel;
+
+  bool mIPCClosed;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_net_FTPChannelParent_h
--- a/netwerk/protocol/ftp/Makefile.in
+++ b/netwerk/protocol/ftp/Makefile.in
@@ -51,35 +51,53 @@ GRE_MODULE     = 1
 FORCE_STATIC_LIB = 1
 
 EXPORTS = ftpCore.h
 
 XPIDLSRCS = \
   nsIFTPChannel.idl \
   $(NULL)
 
+ifdef MOZ_IPC
+EXPORTS_NAMESPACES = mozilla/net
+
+EXPORTS_mozilla/net += \
+  FTPChannelParent.h \
+  FTPChannelChild.h  \
+  $(NULL)
+endif
+
 CPPSRCS = \
   nsFtpProtocolHandler.cpp \
   nsFTPChannel.cpp \
   nsFtpConnectionThread.cpp \
   nsFtpControlConnection.cpp \
   $(NULL)
 
+ifdef MOZ_IPC
+CPPSRCS += \
+  FTPChannelParent.cpp \
+  FTPChannelChild.cpp \
+  $(NULL)
+endif
+
 # Use -g for Irix mipspro builds as workaround for bug 92099
 ifneq (,$(filter IRIX IRIX64,$(OS_ARCH)))
 ifndef GNU_CC
 MODULE_OPTIMIZE_FLAGS=-O -g
 endif
 endif
 
 LOCAL_INCLUDES = \
   -I$(srcdir)/../../base/src \
   -I$(topsrcdir)/xpcom/ds \
   $(NULL)
 
+include $(topsrcdir)/config/config.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
 
 ifeq ($(OS_ARCH),WINNT)
 ifndef MOZ_DEBUG
 ifndef NO_LOGGING
 DEFINES += -DFORCE_PR_LOG
 endif
 endif
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/ftp/PFTPChannel.ipdl
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ *  The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Alon Zakai <azakai@mozilla.com>
+ *   Josh Matthews <josh@joshmatthews.net>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+include protocol PNecko;
+
+include "mozilla/net/NeckoMessageUtils.h";
+
+using IPC::URI;
+using IPC::InputStream;
+using PRTime;
+
+namespace mozilla {
+namespace net {
+
+async protocol PFTPChannel
+{
+  manager PNecko;
+
+parent:
+  __delete__();
+
+  AsyncOpen(URI uri, PRUint64 startPos, nsCString entityID,
+            InputStream uploadStream);
+  Cancel(nsresult status);
+  Suspend();
+  Resume();
+
+child:
+  OnStartRequest(PRInt32 aContentLength, nsCString aContentType,
+                 PRTime aLastModified, nsCString aEntityID, URI aURI);
+  OnDataAvailable(nsCString data, PRUint32 offset, PRUint32 count);
+  OnStopRequest(nsresult statusCode);
+  CancelEarly(nsresult statusCode);
+};
+
+} // namespace net
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/ftp/ipdl.mk
@@ -0,0 +1,40 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Mozilla Firefox.
+#
+# The Initial Developer of the Original Code is
+# The Mozilla Foundation <http://www.mozilla.org/>.
+# Portions created by the Initial Developer are Copyright (C) 2010
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):  Alon Zakai
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+IPDLSRCS =         \
+  PFTPChannel.ipdl \
+  $(NULL)
+
--- a/netwerk/protocol/ftp/nsFTPChannel.h
+++ b/netwerk/protocol/ftp/nsFTPChannel.h
@@ -57,16 +57,17 @@
 #include "nsIStreamListener.h"
 #include "nsAutoLock.h"
 #include "nsIFTPChannel.h"
 #include "nsIUploadChannel.h"
 #include "nsIProxyInfo.h"
 #include "nsIProxiedChannel.h"
 #include "nsIResumableChannel.h"
 #include "nsHashPropertyBag.h"
+#include "nsFtpProtocolHandler.h"
 
 class nsFtpChannel : public nsBaseChannel,
                      public nsIFTPChannel,
                      public nsIUploadChannel,
                      public nsIResumableChannel,
                      public nsIProxiedChannel
 {
 public:
--- a/netwerk/protocol/ftp/nsFtpProtocolHandler.cpp
+++ b/netwerk/protocol/ftp/nsFtpProtocolHandler.cpp
@@ -44,16 +44,22 @@
  * Modifications to Mozilla code or documentation
  * identified per MPL Section 3.3
  *
  * Date         Modified by     Description of modification
  * 03/27/2000   IBM Corp.       Added PR_CALLBACK for Optlink
  *                               use in OS2
  */
 
+#ifdef MOZ_IPC
+#include "mozilla/net/NeckoChild.h"
+#include "mozilla/net/FTPChannelChild.h"
+using namespace mozilla::net;
+#endif
+
 #include "nsFtpProtocolHandler.h"
 #include "nsFTPChannel.h"
 #include "nsIURL.h"
 #include "nsIStandardURL.h"
 #include "nsCRT.h"
 #include "nsIComponentManager.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
@@ -76,16 +82,17 @@
 //    set NSPR_LOG_MODULES=nsFtp:5
 //    set NSPR_LOG_FILE=nspr.log
 //
 // this enables PR_LOG_DEBUG level information and places all output in
 // the file nspr.log
 //
 PRLogModuleInfo* gFTPLog = nsnull;
 #endif
+#undef LOG
 #define LOG(args) PR_LOG(gFTPLog, PR_LOG_DEBUG, args)
 
 //-----------------------------------------------------------------------------
 
 #define IDLE_TIMEOUT_PREF     "network.ftp.idleConnectionTimeout"
 #define IDLE_CONNECTION_LIMIT 8 /* TODO pref me */
 
 #define QOS_DATA_PREF         "network.ftp.data.qos"
@@ -123,16 +130,21 @@ NS_IMPL_THREADSAFE_ISUPPORTS4(nsFtpProto
                               nsIProtocolHandler,
                               nsIProxiedProtocolHandler,
                               nsIObserver,
                               nsISupportsWeakReference)
 
 nsresult
 nsFtpProtocolHandler::Init()
 {
+#ifdef MOZ_IPC
+    if (IsNeckoChild())
+        NeckoChild::InitNeckoChild();
+#endif // MOZ_IPC
+
     if (mIdleTimeout == -1) {
         nsresult rv;
         nsCOMPtr<nsIPrefBranch2> branch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
         if (NS_FAILED(rv)) return rv;
 
         rv = branch->GetIntPref(IDLE_TIMEOUT_PREF, &mIdleTimeout);
         if (NS_FAILED(rv))
             mIdleTimeout = 5*60; // 5 minute default
@@ -236,28 +248,30 @@ nsFtpProtocolHandler::NewChannel(nsIURI*
     return NewProxiedChannel(url, nsnull, result);
 }
 
 NS_IMETHODIMP
 nsFtpProtocolHandler::NewProxiedChannel(nsIURI* uri, nsIProxyInfo* proxyInfo,
                                         nsIChannel* *result)
 {
     NS_ENSURE_ARG_POINTER(uri);
-    nsFtpChannel *channel = new nsFtpChannel(uri, proxyInfo);
-    if (!channel)
-        return NS_ERROR_OUT_OF_MEMORY;
-    NS_ADDREF(channel);
+    nsRefPtr<nsBaseChannel> channel;
+#ifdef MOZ_IPC
+    if (IsNeckoChild())
+        channel = new FTPChannelChild(uri);
+    else
+#endif
+        channel = new nsFtpChannel(uri, proxyInfo);
 
     nsresult rv = channel->Init();
     if (NS_FAILED(rv)) {
-        NS_RELEASE(channel);
         return rv;
     }
     
-    *result = channel;
+    channel.forget(result);
     return rv;
 }
 
 NS_IMETHODIMP 
 nsFtpProtocolHandler::AllowPort(PRInt32 port, const char *scheme, PRBool *_retval)
 {
     *_retval = (port == 21 || port == 22);
     return NS_OK;
--- a/netwerk/protocol/ftp/nsFtpProtocolHandler.h
+++ b/netwerk/protocol/ftp/nsFtpProtocolHandler.h
@@ -118,9 +118,13 @@ private:
     PRUint8 mControlQoSBits;
     PRUint8 mDataQoSBits;
 };
 
 //-----------------------------------------------------------------------------
 
 extern nsFtpProtocolHandler *gFtpHandler;
 
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* gFTPLog;
+#endif
+
 #endif // !nsFtpProtocolHandler_h__
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -55,34 +55,16 @@
 #include "nsIUploadChannel2.h"
 #include "nsIProgressEventSink.h"
 #include "nsIURI.h"
 #include "nsIStringEnumerator.h"
 #include "nsISupportsPriority.h"
 #include "nsIApplicationCache.h"
 #include "nsIResumableChannel.h"
 
-#define DIE_WITH_ASYNC_OPEN_MSG()                                              \
-  do {                                                                         \
-    fprintf(stderr,                                                            \
-            "*&*&*&*&*&*&*&**&*&&*& FATAL ERROR: '%s' "                        \
-            "called after AsyncOpen: %s +%d",                                  \
-            __FUNCTION__, __FILE__, __LINE__);                                 \
-    NS_ABORT();                                                                \
-    return NS_ERROR_NOT_IMPLEMENTED;                                           \
-  } while (0)
-
-#define ENSURE_CALLED_BEFORE_ASYNC_OPEN()                                      \
-  if (mIsPending)                                                              \
-    DIE_WITH_ASYNC_OPEN_MSG();                                                 \
-  if (mWasOpened)                                                              \
-    DIE_WITH_ASYNC_OPEN_MSG();                                                 \
-  NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);                           \
-  NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
-
 namespace mozilla {
 namespace net {
 
 /*
  * This class is a partial implementation of nsIHttpChannel.  It contains code
  * shared by nsHttpChannel and HttpChannelChild. 
  * - Note that this class has nothing to do with nsBaseChannel, which is an
  *   earlier effort at a base class for channels that somehow never made it all