Bug 1403393 - Fork nsIStreamTransportService into mailnews. rs=bustage-fix
authorJorg K <jorgk@jorgk.com>
Wed, 27 Sep 2017 07:23:41 +0200
changeset 29052 8747679c08a4937905581672255ab5a68132ea60
parent 29051 3f25545991374d47201720693beefc5f0d52f52f
child 29053 523f36e9ebc57c20886693d71bf936c45fa1f9df
push id2068
push userclokep@gmail.com
push dateMon, 13 Nov 2017 19:02:14 +0000
treeherdercomm-beta@9c7e7ce8672b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbustage-fix
bugs1403393
Bug 1403393 - Fork nsIStreamTransportService into mailnews. rs=bustage-fix
mailnews/base/public/moz.build
mailnews/base/public/nsIStreamTransportService2.idl
mailnews/base/public/nsMsgBaseCID.h
mailnews/base/util/ServiceList.h
mailnews/base/util/Services.cpp
mailnews/base/util/moz.build
mailnews/base/util/nsMsgProtocol.cpp
mailnews/base/util/nsStreamTransportService2.cpp
mailnews/base/util/nsStreamTransportService2.h
mailnews/build/nsMailModule.cpp
mailnews/local/src/nsMailboxProtocol.cpp
--- a/mailnews/base/public/moz.build
+++ b/mailnews/base/public/moz.build
@@ -52,16 +52,17 @@ XPIDL_SOURCES += [
     'nsIMsgStatusFeedback.idl',
     'nsIMsgTagService.idl',
     'nsIMsgThread.idl',
     'nsIMsgUserFeedbackListener.idl',
     'nsIMsgWindow.idl',
     'nsISpamSettings.idl',
     'nsIStatusBarBiffManager.idl',
     'nsIStopwatch.idl',
+    'nsIStreamTransportService2.idl',
     'nsISubscribableServer.idl',
     'nsIUrlListener.idl',
     'nsMsgFolderFlags.idl',
     'nsMsgMessageFlags.idl',
 ]
 
 XPIDL_MODULE = 'msgbase'
 
new file mode 100644
--- /dev/null
+++ b/mailnews/base/public/nsIStreamTransportService2.idl
@@ -0,0 +1,43 @@
+/* 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 "nsISupports.idl"
+
+interface nsITransport;
+interface nsIInputStream;
+
+/**
+ * This service read/writes a stream on a background thread.
+ *
+ * Use this service to transform any blocking stream (e.g., file stream)
+ * into a fully asynchronous stream that can be read/written without 
+ * blocking the main thread.
+ */
+[builtinclass, scriptable, uuid(80f305e1-a04b-4795-97af-72a86681c76c)]
+interface nsIStreamTransportService2 : nsISupports
+{
+    /**
+     * CreateInputTransport
+     *
+     * @param aStream
+     *        The input stream that will be read on a background thread.
+     *        This stream must implement "blocking" stream semantics.
+     * @param aStartOffset
+     *        The input stream will be read starting from this offset.  Pass
+     *        -1 to read from the current stream offset.  NOTE: this parameter
+     *        is ignored if the stream does not support nsISeekableStream.
+     * @param aReadLimit
+     *        This parameter limits the number of bytes that will be read from
+     *        the input stream.  Pass -1 to read everything.
+     * @param aCloseWhenDone
+     *        Specify this flag to have the input stream closed once its
+     *        contents have been completely read.
+     *
+     * @return nsITransport instance.
+     */
+    nsITransport createInputTransport(in nsIInputStream aStream,
+                                      in long long aStartOffset,
+                                      in long long aReadLimit,
+                                      in boolean aCloseWhenDone);
+};
--- a/mailnews/base/public/nsMsgBaseCID.h
+++ b/mailnews/base/public/nsMsgBaseCID.h
@@ -528,9 +528,23 @@
 #define MOZ_NEWMAILNOTIFICATIONSERVICE_CID \
 { /* 740880E6-E299-4165-B82F-DF1DCAB3AE22 */ \
   0x740880E6, 0xE299, 0x4165, \
     { 0xB8, 0x2F, 0xDF, 0x1D, 0xCA, 0xB3, 0xAE, 0x22 }}
 
 #define MOZ_NEWMAILNOTIFICATIONSERVICE_CONTRACTID \
   "@mozilla.org/newMailNotificationService;1"
 
+//
+// Service implementing nsIStreamTransportService2,
+// forked from nsIStreamTransportService.
+//
+#define NS_STREAMTRANSPORTSERVICE2_CONTRACTID \
+    "@mozilla.org/mailnews/stream-transport-service2;1"
+#define NS_STREAMTRANSPORTSERVICE2_CID \
+{ /* 80f305e1-a04b-4795-97af-72a86681c76c */         \
+    0x80f305e1,                                      \
+    0xa04b,                                          \
+    0x4795,                                          \
+    {0x97, 0xaf, 0x72, 0xa8, 0x66, 0x81, 0xc7, 0x6c} \
+}
+
 #endif // nsMessageBaseCID_h__
--- a/mailnews/base/util/ServiceList.h
+++ b/mailnews/base/util/ServiceList.h
@@ -25,16 +25,18 @@ MOZ_SERVICE(ImportService,     nsIImport
 MOZ_SERVICE(MailNotifyService, mozINewMailNotificationService,
             "@mozilla.org/newMailNotificationService;1")
 MOZ_SERVICE(MailSession,       nsIMsgMailSession,
             "@mozilla.org/messenger/services/session;1")
 MOZ_SERVICE(MimeConverter,     nsIMimeConverter,
             "@mozilla.org/messenger/mimeconverter;1")
 MOZ_SERVICE(MFNService,        nsIMsgFolderNotificationService,
             "@mozilla.org/messenger/msgnotificationservice;1")
+MOZ_SERVICE(StreamTransportService2, nsIStreamTransportService2,
+            "@mozilla.org/mailnews/stream-transport-service2;1")
 MOZ_SERVICE(NntpService,       nsINntpService,
             "@mozilla.org/messenger/nntpservice;1")
 MOZ_SERVICE(Pop3Service,       nsIPop3Service,
             "@mozilla.org/messenger/popservice;1")
 MOZ_SERVICE(SmtpService,       nsISmtpService,
             "@mozilla.org/messengercompose/smtp;1")
 MOZ_SERVICE(TagService,        nsIMsgTagService,
             "@mozilla.org/messenger/tagservice;1")
--- a/mailnews/base/util/Services.cpp
+++ b/mailnews/base/util/Services.cpp
@@ -16,16 +16,17 @@
 #include "nsIImportService.h"
 #include "nsIMimeConverter.h"
 #include "nsIMsgAccountManager.h"
 #include "nsIMsgComposeService.h"
 #include "nsIMsgCopyService.h"
 #include "nsIMsgDatabase.h"
 #include "nsIMsgFilterService.h"
 #include "nsIMsgFolderNotificationService.h"
+#include "nsIStreamTransportService2.h"
 #include "nsIMsgHeaderParser.h"
 #include "nsIMsgMailSession.h"
 #include "nsIMsgTagService.h"
 #include "nsINntpService.h"
 #include "nsIPop3Service.h"
 #include "nsISmtpService.h"
 
 namespace mozilla {
--- a/mailnews/base/util/moz.build
+++ b/mailnews/base/util/moz.build
@@ -14,16 +14,17 @@ EXPORTS += [
     'nsMsgKeyArray.h',
     'nsMsgKeySet.h',
     'nsMsgLineBuffer.h',
     'nsMsgMailNewsUrl.h',
     'nsMsgProtocol.h',
     'nsMsgReadStateTxn.h',
     'nsMsgTxn.h',
     'nsMsgUtils.h',
+    'nsStreamTransportService2.h',
 ]
 
 EXPORTS.mozilla.mailnews += [
     'ServiceList.h',
     'Services.h',
 ]
 
 SOURCES += [
@@ -39,16 +40,17 @@ SOURCES += [
     'nsMsgKeySet.cpp',
     'nsMsgLineBuffer.cpp',
     'nsMsgMailNewsUrl.cpp',
     'nsMsgProtocol.cpp',
     'nsMsgReadStateTxn.cpp',
     'nsMsgTxn.cpp',
     'nsMsgUtils.cpp',
     'nsStopwatch.cpp',
+    'nsStreamTransportService2.cpp',
     'Services.cpp',
 ]
 
 EXTRA_JS_MODULES += [
     'ABQueryUtils.jsm',
     'errUtils.js',
     'folderUtils.jsm',
     'hostnameUtils.jsm',
--- a/mailnews/base/util/nsMsgProtocol.cpp
+++ b/mailnews/base/util/nsMsgProtocol.cpp
@@ -4,17 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "msgCore.h"
 #include "nsString.h"
 #include "nsMsgProtocol.h"
 #include "nsIMsgMailNewsUrl.h"
 #include "nsIMsgMailSession.h"
 #include "nsMsgBaseCID.h"
-#include "nsIStreamTransportService.h"
+#include "nsIStreamTransportService2.h"
 #include "nsISocketTransportService.h"
 #include "nsISocketTransport.h"
 #include "nsILoadGroup.h"
 #include "nsILoadInfo.h"
 #include "nsIIOService.h"
 #include "nsNetUtil.h"
 #include "nsIFileURL.h"
 #include "nsIMsgWindow.h"
@@ -203,18 +203,18 @@ nsresult nsMsgProtocol::OpenFileSocket(n
   rv = GetFileFromURL(aURL, getter_AddRefs(file));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIInputStream> stream;
   rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
   if (NS_FAILED(rv)) return rv;
 
   // create input stream transport
-  nsCOMPtr<nsIStreamTransportService> sts =
-      do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
+  nsCOMPtr<nsIStreamTransportService2> sts =
+      do_GetService(NS_STREAMTRANSPORTSERVICE2_CONTRACTID, &rv);
   if (NS_FAILED(rv)) return rv;
 
   rv = sts->CreateInputTransport(stream, int64_t(aStartPosition),
                                  int64_t(aReadCount), true,
                                  getter_AddRefs(m_transport));
 
   m_socketIsOpen = false;
   return rv;
new file mode 100644
--- /dev/null
+++ b/mailnews/base/util/nsStreamTransportService2.cpp
@@ -0,0 +1,562 @@
+/* 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 "nsStreamTransportService2.h"
+#include "nsXPCOMCIDInternal.h"
+#include "nsNetSegmentUtils.h"
+#include "nsTransportUtils.h"
+#include "nsStreamUtils.h"
+#include "nsError.h"
+#include "nsNetCID.h"
+
+#include "nsIAsyncInputStream.h"
+#include "nsIAsyncOutputStream.h"
+#include "nsISeekableStream.h"
+#include "nsIPipe.h"
+#include "nsITransport.h"
+#include "nsIObserverService.h"
+#include "nsIThreadPool.h"
+#include "mozilla/Services.h"
+
+namespace mozilla {
+namespace net {
+
+//-----------------------------------------------------------------------------
+// nsInputStreamTransport2
+//
+// Implements nsIInputStream as a wrapper around the real input stream.  This
+// allows the transport to support seeking, range-limiting, progress reporting,
+// and close-when-done semantics while utilizing NS_AsyncCopy.
+//-----------------------------------------------------------------------------
+
+class nsInputStreamTransport2 : public nsITransport
+                              , public nsIInputStream
+{
+public:
+    NS_DECL_THREADSAFE_ISUPPORTS
+    NS_DECL_NSITRANSPORT
+    NS_DECL_NSIINPUTSTREAM
+
+    nsInputStreamTransport2(nsIInputStream *source,
+                            uint64_t offset,
+                            uint64_t limit,
+                            bool closeWhenDone)
+        : mSource(source)
+        , mOffset(offset)
+        , mLimit(limit)
+        , mCloseWhenDone(closeWhenDone)
+        , mFirstTime(true)
+        , mInProgress(false)
+    {
+    }
+
+private:
+    virtual ~nsInputStreamTransport2()
+    {
+    }
+
+    nsCOMPtr<nsIAsyncInputStream>   mPipeIn;
+
+    // while the copy is active, these members may only be accessed from the
+    // nsIInputStream implementation.
+    nsCOMPtr<nsITransportEventSink> mEventSink;
+    nsCOMPtr<nsIInputStream>        mSource;
+    int64_t                         mOffset;
+    int64_t                         mLimit;
+    bool                            mCloseWhenDone;
+    bool                            mFirstTime;
+
+    // this variable serves as a lock to prevent the state of the transport
+    // from being modified once the copy is in progress.
+    bool                            mInProgress;
+};
+
+NS_IMPL_ISUPPORTS(nsInputStreamTransport2,
+                  nsITransport,
+                  nsIInputStream)
+
+/** nsITransport **/
+
+NS_IMETHODIMP
+nsInputStreamTransport2::OpenInputStream(uint32_t flags,
+                                         uint32_t segsize,
+                                         uint32_t segcount,
+                                         nsIInputStream **result)
+{
+    NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);
+
+    nsresult rv;
+    nsCOMPtr<nsIEventTarget> target =
+            do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
+    if (NS_FAILED(rv)) return rv;
+
+    // XXX if the caller requests an unbuffered stream, then perhaps
+    //     we'd want to simply return mSource; however, then we would
+    //     not be reading mSource on a background thread.  is this ok?
+
+    bool nonblocking = !(flags & OPEN_BLOCKING);
+
+    net_ResolveSegmentParams(segsize, segcount);
+
+    nsCOMPtr<nsIAsyncOutputStream> pipeOut;
+    rv = NS_NewPipe2(getter_AddRefs(mPipeIn),
+                     getter_AddRefs(pipeOut),
+                     nonblocking, true,
+                     segsize, segcount);
+    if (NS_FAILED(rv)) return rv;
+
+    mInProgress = true;
+
+    // startup async copy process...
+    rv = NS_AsyncCopy(this, pipeOut, target,
+                      NS_ASYNCCOPY_VIA_WRITESEGMENTS, segsize);
+    if (NS_SUCCEEDED(rv))
+        NS_ADDREF(*result = mPipeIn);
+
+    return rv;
+}
+
+NS_IMETHODIMP
+nsInputStreamTransport2::OpenOutputStream(uint32_t flags,
+                                          uint32_t segsize,
+                                          uint32_t segcount,
+                                          nsIOutputStream **result)
+{
+    // this transport only supports reading!
+    NS_NOTREACHED("nsInputStreamTransport2::OpenOutputStream");
+    return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMETHODIMP
+nsInputStreamTransport2::Close(nsresult reason)
+{
+    if (NS_SUCCEEDED(reason))
+        reason = NS_BASE_STREAM_CLOSED;
+
+    return mPipeIn->CloseWithStatus(reason);
+}
+
+NS_IMETHODIMP
+nsInputStreamTransport2::SetEventSink(nsITransportEventSink *sink,
+                                      nsIEventTarget *target)
+{
+    NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);
+
+    if (target)
+        return net_NewTransportEventSinkProxy(getter_AddRefs(mEventSink),
+                                              sink, target);
+
+    mEventSink = sink;
+    return NS_OK;
+}
+
+/** nsIInputStream **/
+
+NS_IMETHODIMP
+nsInputStreamTransport2::Close()
+{
+    if (mCloseWhenDone)
+        mSource->Close();
+
+    // make additional reads return early...
+    mOffset = mLimit = 0;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsInputStreamTransport2::Available(uint64_t *result)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsInputStreamTransport2::Read(char *buf, uint32_t count, uint32_t *result)
+{
+    if (mFirstTime) {
+        mFirstTime = false;
+        if (mOffset != 0) {
+            // read from current position if offset equal to max
+            if (mOffset != -1) {
+                nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mSource);
+                if (seekable)
+                    seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
+            }
+            // reset offset to zero so we can use it to enforce limit
+            mOffset = 0;
+        }
+    }
+
+    // limit amount read
+    uint64_t max = count;
+    if (mLimit != -1) {
+        max = mLimit - mOffset;
+        if (max == 0) {
+            *result = 0;
+            return NS_OK;
+        }
+    }
+
+    if (count > max)
+        count = static_cast<uint32_t>(max);
+
+    nsresult rv = mSource->Read(buf, count, result);
+
+    if (NS_SUCCEEDED(rv)) {
+        mOffset += *result;
+        if (mEventSink)
+            mEventSink->OnTransportStatus(this, NS_NET_STATUS_READING, mOffset,
+                                          mLimit);
+    }
+    return rv;
+}
+
+NS_IMETHODIMP
+nsInputStreamTransport2::ReadSegments(nsWriteSegmentFun writer, void *closure,
+                                      uint32_t count, uint32_t *result)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsInputStreamTransport2::IsNonBlocking(bool *result)
+{
+    *result = false;
+    return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsOutputStreamTransport2
+//
+// Implements nsIOutputStream as a wrapper around the real input stream.  This
+// allows the transport to support seeking, range-limiting, progress reporting,
+// and close-when-done semantics while utilizing NS_AsyncCopy.
+//-----------------------------------------------------------------------------
+
+class nsOutputStreamTransport2 : public nsITransport
+                               , public nsIOutputStream
+{
+public:
+    NS_DECL_THREADSAFE_ISUPPORTS
+    NS_DECL_NSITRANSPORT
+    NS_DECL_NSIOUTPUTSTREAM
+
+    nsOutputStreamTransport2(nsIOutputStream *sink,
+                             int64_t offset,
+                             int64_t limit,
+                             bool closeWhenDone)
+        : mSink(sink)
+        , mOffset(offset)
+        , mLimit(limit)
+        , mCloseWhenDone(closeWhenDone)
+        , mFirstTime(true)
+        , mInProgress(false)
+    {
+    }
+
+private:
+    virtual ~nsOutputStreamTransport2()
+    {
+    }
+
+    nsCOMPtr<nsIAsyncOutputStream>  mPipeOut;
+
+    // while the copy is active, these members may only be accessed from the
+    // nsIOutputStream implementation.
+    nsCOMPtr<nsITransportEventSink> mEventSink;
+    nsCOMPtr<nsIOutputStream>       mSink;
+    int64_t                         mOffset;
+    int64_t                         mLimit;
+    bool                            mCloseWhenDone;
+    bool                            mFirstTime;
+
+    // this variable serves as a lock to prevent the state of the transport
+    // from being modified once the copy is in progress.
+    bool                            mInProgress;
+};
+
+NS_IMPL_ISUPPORTS(nsOutputStreamTransport2,
+                  nsITransport,
+                  nsIOutputStream)
+
+/** nsITransport **/
+
+NS_IMETHODIMP
+nsOutputStreamTransport2::OpenInputStream(uint32_t flags,
+                                          uint32_t segsize,
+                                          uint32_t segcount,
+                                          nsIInputStream **result)
+{
+    // this transport only supports writing!
+    NS_NOTREACHED("nsOutputStreamTransport2::OpenInputStream");
+    return NS_ERROR_UNEXPECTED;
+}
+
+NS_IMETHODIMP
+nsOutputStreamTransport2::OpenOutputStream(uint32_t flags,
+                                           uint32_t segsize,
+                                           uint32_t segcount,
+                                           nsIOutputStream **result)
+{
+    NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);
+
+    nsresult rv;
+    nsCOMPtr<nsIEventTarget> target =
+            do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
+    if (NS_FAILED(rv)) return rv;
+
+    // XXX if the caller requests an unbuffered stream, then perhaps
+    //     we'd want to simply return mSink; however, then we would
+    //     not be writing to mSink on a background thread.  is this ok?
+
+    bool nonblocking = !(flags & OPEN_BLOCKING);
+
+    net_ResolveSegmentParams(segsize, segcount);
+
+    nsCOMPtr<nsIAsyncInputStream> pipeIn;
+    rv = NS_NewPipe2(getter_AddRefs(pipeIn),
+                     getter_AddRefs(mPipeOut),
+                     true, nonblocking,
+                     segsize, segcount);
+    if (NS_FAILED(rv)) return rv;
+
+    mInProgress = true;
+
+    // startup async copy process...
+    rv = NS_AsyncCopy(pipeIn, this, target,
+                      NS_ASYNCCOPY_VIA_READSEGMENTS, segsize);
+    if (NS_SUCCEEDED(rv))
+        NS_ADDREF(*result = mPipeOut);
+
+    return rv;
+}
+
+NS_IMETHODIMP
+nsOutputStreamTransport2::Close(nsresult reason)
+{
+    if (NS_SUCCEEDED(reason))
+        reason = NS_BASE_STREAM_CLOSED;
+
+    return mPipeOut->CloseWithStatus(reason);
+}
+
+NS_IMETHODIMP
+nsOutputStreamTransport2::SetEventSink(nsITransportEventSink *sink,
+                                       nsIEventTarget *target)
+{
+    NS_ENSURE_TRUE(!mInProgress, NS_ERROR_IN_PROGRESS);
+
+    if (target)
+        return net_NewTransportEventSinkProxy(getter_AddRefs(mEventSink),
+                                              sink, target);
+
+    mEventSink = sink;
+    return NS_OK;
+}
+
+/** nsIOutputStream **/
+
+NS_IMETHODIMP
+nsOutputStreamTransport2::Close()
+{
+    if (mCloseWhenDone)
+        mSink->Close();
+
+    // make additional writes return early...
+    mOffset = mLimit = 0;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOutputStreamTransport2::Flush()
+{
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsOutputStreamTransport2::Write(const char *buf, uint32_t count, uint32_t *result)
+{
+    if (mFirstTime) {
+        mFirstTime = false;
+        if (mOffset != 0) {
+            // write to current position if offset equal to max
+            if (mOffset != -1) {
+                nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mSink);
+                if (seekable)
+                    seekable->Seek(nsISeekableStream::NS_SEEK_SET, mOffset);
+            }
+            // reset offset to zero so we can use it to enforce limit
+            mOffset = 0;
+        }
+    }
+
+    // limit amount written
+    uint64_t max = count;
+    if (mLimit != -1) {
+        max = mLimit - mOffset;
+        if (max == 0) {
+            *result = 0;
+            return NS_OK;
+        }
+    }
+
+    if (count > max)
+        count = static_cast<uint32_t>(max);
+
+    nsresult rv = mSink->Write(buf, count, result);
+
+    if (NS_SUCCEEDED(rv)) {
+        mOffset += *result;
+        if (mEventSink)
+            mEventSink->OnTransportStatus(this, NS_NET_STATUS_WRITING, mOffset,
+                                          mLimit);
+    }
+    return rv;
+}
+
+NS_IMETHODIMP
+nsOutputStreamTransport2::WriteSegments(nsReadSegmentFun reader, void *closure,
+                                        uint32_t count, uint32_t *result)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsOutputStreamTransport2::WriteFrom(nsIInputStream *in, uint32_t count, uint32_t *result)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsOutputStreamTransport2::IsNonBlocking(bool *result)
+{
+    *result = false;
+    return NS_OK;
+}
+
+//-----------------------------------------------------------------------------
+// nsStreamTransportService2
+//-----------------------------------------------------------------------------
+
+nsStreamTransportService2::~nsStreamTransportService2()
+{
+    NS_ASSERTION(!mPool, "thread pool wasn't shutdown");
+}
+
+nsresult
+nsStreamTransportService2::Init()
+{
+    mPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
+    NS_ENSURE_STATE(mPool);
+
+    // Configure the pool
+    mPool->SetName(NS_LITERAL_CSTRING("StreamTrans"));
+    mPool->SetThreadLimit(25);
+    mPool->SetIdleThreadLimit(1);
+    mPool->SetIdleThreadTimeout(PR_SecondsToInterval(30));
+
+    nsCOMPtr<nsIObserverService> obsSvc =
+        mozilla::services::GetObserverService();
+    if (obsSvc)
+        obsSvc->AddObserver(this, "xpcom-shutdown-threads", false);
+    return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(nsStreamTransportService2,
+                  nsIStreamTransportService2,
+                  nsIEventTarget,
+                  nsIObserver)
+
+NS_IMETHODIMP
+nsStreamTransportService2::DispatchFromScript(nsIRunnable *task, uint32_t flags)
+{
+  nsCOMPtr<nsIRunnable> event(task);
+  return Dispatch(event.forget(), flags);
+}
+
+NS_IMETHODIMP
+nsStreamTransportService2::Dispatch(already_AddRefed<nsIRunnable> task, uint32_t flags)
+{
+    nsCOMPtr<nsIRunnable> event(task); // so it gets released on failure paths
+    nsCOMPtr<nsIThreadPool> pool;
+    {
+        mozilla::MutexAutoLock lock(mShutdownLock);
+        if (mIsShutdown) {
+            return NS_ERROR_NOT_INITIALIZED;
+        }
+        pool = mPool;
+    }
+    NS_ENSURE_TRUE(pool, NS_ERROR_NOT_INITIALIZED);
+    return pool->Dispatch(event.forget(), flags);
+}
+
+NS_IMETHODIMP
+nsStreamTransportService2::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP_(bool)
+nsStreamTransportService2::IsOnCurrentThreadInfallible()
+{
+    nsCOMPtr<nsIThreadPool> pool;
+    {
+        mozilla::MutexAutoLock lock(mShutdownLock);
+        pool = mPool;
+    }
+    if (!pool) {
+      return false;
+    }
+    return pool->IsOnCurrentThread();
+}
+
+NS_IMETHODIMP
+nsStreamTransportService2::IsOnCurrentThread(bool *result)
+{
+    nsCOMPtr<nsIThreadPool> pool;
+    {
+        mozilla::MutexAutoLock lock(mShutdownLock);
+        if (mIsShutdown) {
+            return NS_ERROR_NOT_INITIALIZED;
+        }
+        pool = mPool;
+    }
+    NS_ENSURE_TRUE(pool, NS_ERROR_NOT_INITIALIZED);
+    return pool->IsOnCurrentThread(result);
+}
+
+NS_IMETHODIMP
+nsStreamTransportService2::CreateInputTransport(nsIInputStream *stream,
+                                                int64_t offset,
+                                                int64_t limit,
+                                                bool closeWhenDone,
+                                                nsITransport **result)
+{
+    nsInputStreamTransport2 *trans =
+        new nsInputStreamTransport2(stream, offset, limit, closeWhenDone);
+    if (!trans)
+        return NS_ERROR_OUT_OF_MEMORY;
+    NS_ADDREF(*result = trans);
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsStreamTransportService2::Observe(nsISupports *subject, const char *topic,
+                                   const char16_t *data)
+{
+  NS_ASSERTION(strcmp(topic, "xpcom-shutdown-threads") == 0, "oops");
+
+  {
+    mozilla::MutexAutoLock lock(mShutdownLock);
+    mIsShutdown = true;
+  }
+
+  if (mPool) {
+    mPool->Shutdown();
+    mPool = nullptr;
+  }
+  return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/mailnews/base/util/nsStreamTransportService2.h
@@ -0,0 +1,47 @@
+/* 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 nsStreamTransportService2_h__
+#define nsStreamTransportService2_h__
+
+#include "nsIStreamTransportService2.h"
+#include "nsIEventTarget.h"
+#include "nsIObserver.h"
+#include "nsCOMPtr.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Mutex.h"
+
+class nsIThreadPool;
+
+namespace mozilla {
+namespace net {
+
+class nsStreamTransportService2 final : public nsIStreamTransportService2
+                                      , public nsIEventTarget
+                                      , public nsIObserver
+{
+public:
+    NS_DECL_THREADSAFE_ISUPPORTS
+    NS_DECL_NSISTREAMTRANSPORTSERVICE2
+    NS_DECL_NSIEVENTTARGET_FULL
+    NS_DECL_NSIOBSERVER
+
+    nsresult Init();
+
+    nsStreamTransportService2() : mShutdownLock("nsStreamTransportService2.mShutdownLock"),
+                                 mIsShutdown(false) {}
+
+private:
+    ~nsStreamTransportService2();
+
+    nsCOMPtr<nsIThreadPool> mPool;
+
+    mozilla::Mutex mShutdownLock;
+    bool mIsShutdown;
+};
+
+} // namespace net
+} // namespace mozilla
+#endif
--- a/mailnews/build/nsMailModule.cpp
+++ b/mailnews/build/nsMailModule.cpp
@@ -92,16 +92,19 @@
 #include "nsMsgContentPolicy.h"
 #include "nsCidProtocolHandler.h"
 #include "nsRssIncomingServer.h"
 #include "nsRssService.h"
 #include "nsMsgBrkMBoxStore.h"
 #include "nsMsgMaildirStore.h"
 #include "nsMsgTagService.h"
 #include "nsMsgFolderNotificationService.h"
+#include "nsIThreadPool.h"
+#include "nsStreamTransportService2.h"
+typedef mozilla::net::nsStreamTransportService2 nsStreamTransportService2;
 #include "nsMailDirProvider.h"
 
 #ifdef XP_WIN
 #include "nsMessengerWinIntegration.h"
 #endif
 #ifdef XP_MACOSX
 #include "nsMessengerOSXIntegration.h"
 #endif
@@ -362,16 +365,17 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsMsgSear
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsMsgXFVirtualFolderDBView)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsMsgQuickSearchDBView)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsMsgGroupView)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsMsgOfflineManager)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsMsgProgress)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSpamSettings)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsMsgTagService)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsMsgFolderNotificationService)
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsStreamTransportService2, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsCidProtocolHandler)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsMailDirProvider)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsMsgShutdownService)
 #ifdef XP_WIN
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsMessengerWinIntegration, Init)
 #endif
 #ifdef XP_MACOSX
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsMessengerOSXIntegration, Init)
@@ -425,16 +429,17 @@ NS_DEFINE_NAMED_CID(NS_MSGQUICKSEARCHDBV
 NS_DEFINE_NAMED_CID(NS_MSG_XFVFDBVIEW_CID);
 NS_DEFINE_NAMED_CID(NS_MSG_GROUPDBVIEW_CID);
 NS_DEFINE_NAMED_CID(NS_MSGOFFLINEMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_MSGPROGRESS_CID);
 NS_DEFINE_NAMED_CID(NS_SPAMSETTINGS_CID);
 NS_DEFINE_NAMED_CID(NS_CIDPROTOCOL_CID);
 NS_DEFINE_NAMED_CID(NS_MSGTAGSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_MSGNOTIFICATIONSERVICE_CID);
+NS_DEFINE_NAMED_CID(NS_STREAMTRANSPORTSERVICE2_CID);
 #ifdef XP_WIN
 NS_DEFINE_NAMED_CID(NS_MESSENGERWININTEGRATION_CID);
 #endif
 #ifdef XP_MACOSX
 NS_DEFINE_NAMED_CID(NS_MESSENGEROSXINTEGRATION_CID);
 #endif
 #if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_GTK2)
 NS_DEFINE_NAMED_CID(NS_MESSENGERUNIXINTEGRATION_CID);
@@ -897,16 +902,17 @@ const mozilla::Module::CIDEntry kMailNew
   { &kNS_MSG_XFVFDBVIEW_CID, false, NULL, nsMsgXFVirtualFolderDBViewConstructor},
   { &kNS_MSG_GROUPDBVIEW_CID, false, NULL, nsMsgGroupViewConstructor},
   { &kNS_MSGOFFLINEMANAGER_CID, false, NULL, nsMsgOfflineManagerConstructor},
   { &kNS_MSGPROGRESS_CID, false, NULL, nsMsgProgressConstructor},
   { &kNS_SPAMSETTINGS_CID, false, NULL, nsSpamSettingsConstructor},
   { &kNS_CIDPROTOCOL_CID, false, NULL, nsCidProtocolHandlerConstructor},
   { &kNS_MSGTAGSERVICE_CID, false, NULL, nsMsgTagServiceConstructor},
   { &kNS_MSGNOTIFICATIONSERVICE_CID, false, NULL, nsMsgFolderNotificationServiceConstructor},
+  { &kNS_STREAMTRANSPORTSERVICE2_CID, false, NULL, nsStreamTransportService2Constructor},
 #ifdef XP_WIN
   { &kNS_MESSENGERWININTEGRATION_CID, false, NULL, nsMessengerWinIntegrationConstructor},
 #endif
 #ifdef XP_MACOSX
   { &kNS_MESSENGEROSXINTEGRATION_CID, false, NULL, nsMessengerOSXIntegrationConstructor},
 #endif
 #if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_GTK2)
   { &kNS_MESSENGERUNIXINTEGRATION_CID, false, NULL, nsMessengerUnixIntegrationConstructor},
@@ -1111,16 +1117,17 @@ const mozilla::Module::ContractIDEntry k
   { NS_MSGXFVFDBVIEW_CONTRACTID, &kNS_MSG_XFVFDBVIEW_CID },
   { NS_MSGGROUPDBVIEW_CONTRACTID, &kNS_MSG_GROUPDBVIEW_CID },
   { NS_MSGOFFLINEMANAGER_CONTRACTID, &kNS_MSGOFFLINEMANAGER_CID },
   { NS_MSGPROGRESS_CONTRACTID, &kNS_MSGPROGRESS_CID },
   { NS_SPAMSETTINGS_CONTRACTID, &kNS_SPAMSETTINGS_CID },
   { NS_CIDPROTOCOLHANDLER_CONTRACTID, &kNS_CIDPROTOCOL_CID },
   { NS_MSGTAGSERVICE_CONTRACTID, &kNS_MSGTAGSERVICE_CID },
   { NS_MSGNOTIFICATIONSERVICE_CONTRACTID, &kNS_MSGNOTIFICATIONSERVICE_CID },
+  { NS_STREAMTRANSPORTSERVICE2_CONTRACTID, &kNS_STREAMTRANSPORTSERVICE2_CID },
 #ifdef XP_WIN
   { NS_MESSENGEROSINTEGRATION_CONTRACTID, &kNS_MESSENGERWININTEGRATION_CID },
 #endif
 #ifdef XP_MACOSX
   { NS_MESSENGEROSINTEGRATION_CONTRACTID, &kNS_MESSENGEROSXINTEGRATION_CID },
 #endif
 #if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_GTK2)
   { NS_MESSENGEROSINTEGRATION_CONTRACTID, &kNS_MESSENGERUNIXINTEGRATION_CID },
--- a/mailnews/local/src/nsMailboxProtocol.cpp
+++ b/mailnews/local/src/nsMailboxProtocol.cpp
@@ -7,30 +7,31 @@
 
 #include "nsMailboxProtocol.h"
 #include "nscore.h"
 #include "nsIOutputStream.h"
 #include "nsIInputStreamPump.h"
 #include "nsIMsgDatabase.h"
 #include "nsIMsgHdr.h"
 #include "nsMsgLineBuffer.h"
+#include "nsMsgBaseCID.h"
 #include "nsMsgDBCID.h"
 #include "nsIMsgMailNewsUrl.h"
 #include "nsICopyMsgStreamListener.h"
 #include "nsMsgMessageFlags.h"
 #include "prtime.h"
 #include "mozilla/Logging.h"
 #include "prerror.h"
 #include "prprf.h"
 #include "nspr.h"
 
 static mozilla::LazyLogModule MAILBOX("MAILBOX");
 
 #include "nsIFileStreams.h"
-#include "nsIStreamTransportService.h"
+#include "nsIStreamTransportService2.h"
 #include "nsIStreamConverterService.h"
 #include "nsIIOService.h"
 #include "nsNetUtil.h"
 #include "nsMsgUtils.h"
 #include "nsIMsgWindow.h"
 #include "nsIMimeHeaders.h"
 #include "nsIMsgPluggableStore.h"
 #include "nsISeekableStream.h"
@@ -59,18 +60,18 @@ nsMailboxProtocol::~nsMailboxProtocol()
   // free our local state
   delete m_lineStreamBuffer;
 }
 
 nsresult nsMailboxProtocol::OpenMultipleMsgTransport(uint64_t offset, int32_t size)
 {
   nsresult rv;
 
-  nsCOMPtr<nsIStreamTransportService> serv =
-      do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
+  nsCOMPtr<nsIStreamTransportService2> serv =
+      do_GetService(NS_STREAMTRANSPORTSERVICE2_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // XXX 64-bit
   rv = serv->CreateInputTransport(m_multipleMsgMoveCopyStream, int64_t(offset),
                                   int64_t(size), false,
                                   getter_AddRefs(m_transport));
 
   return rv;
@@ -144,18 +145,18 @@ nsresult nsMailboxProtocol::Initialize(n
               bool reusable = false;
 
               rv = folder->GetMsgInputStream(msgHdr, &reusable, getter_AddRefs(stream));
               NS_ENSURE_SUCCESS(rv, rv);
               nsCOMPtr<nsISeekableStream> seekableStream(do_QueryInterface(stream, &rv));
               NS_ENSURE_SUCCESS(rv, rv);
               seekableStream->Tell(&offset);
               // create input stream transport
-              nsCOMPtr<nsIStreamTransportService> sts =
-                  do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
+              nsCOMPtr<nsIStreamTransportService2> sts =
+                  do_GetService(NS_STREAMTRANSPORTSERVICE2_CONTRACTID, &rv);
               if (NS_FAILED(rv)) return rv;
               m_readCount = aMsgSize;
               // Save the stream for reuse, but only for multiple URLs.
               if (reusable && RunningMultipleMsgUrl())
                 m_multipleMsgMoveCopyStream = stream;
               else
                 reusable = false;
               rv = sts->CreateInputTransport(stream, offset,
@@ -302,18 +303,18 @@ NS_IMETHODIMP nsMailboxProtocol::OnStopR
                   bool reusable = false;
                   rv = msgFolder->GetMsgInputStream(nextMsg, &reusable,
                                                     getter_AddRefs(stream));
                   NS_ASSERTION(!reusable, "We thought streams were not reusable!");
 
                   if (NS_SUCCEEDED(rv))
                   {
                     // create input stream transport
-                    nsCOMPtr<nsIStreamTransportService> sts =
-                        do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
+                    nsCOMPtr<nsIStreamTransportService2> sts =
+                        do_GetService(NS_STREAMTRANSPORTSERVICE2_CONTRACTID, &rv);
 
                     if (NS_SUCCEEDED(rv))
                     {
                       m_readCount = msgSize;
                       rv = sts->CreateInputTransport(stream, msgOffset,
                                                      int64_t(msgSize), true,
                                                      getter_AddRefs(m_transport));
                     }