Bug 1373708 - Convert Jar channels to stop using main thread I/O, r=nwgh
authorLiang-Heng Chen <xeonchen@gmail.com>
Thu, 01 Mar 2018 08:12:00 +0000
changeset 462797 8b721870d808f23a4f515e98516e76c3dc9e99b3
parent 462796 797722641610dad674216f190a8f4d7a76af9b8c
child 462798 0052594a7fdaa2df1704b8b134fb585a68de9f59
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnwgh
bugs1373708
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1373708 - Convert Jar channels to stop using main thread I/O, r=nwgh
modules/libjar/nsJARChannel.cpp
modules/libjar/nsJARChannel.h
--- a/modules/libjar/nsJARChannel.cpp
+++ b/modules/libjar/nsJARChannel.cpp
@@ -21,16 +21,17 @@
 #include "nsIPrincipal.h"
 #include "nsIFileURL.h"
 
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/Preferences.h"
 #include "nsITabChild.h"
 #include "private/pprio.h"
 #include "nsInputStreamPump.h"
+#include "nsThreadUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::net;
 
 static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
 
 // the entry for a directory will either be empty (in the case of the
 // top-level directory) or will end with a slash
@@ -193,27 +194,31 @@ nsJARInputThunk::IsNonBlocking(bool *non
 
 nsJARChannel::nsJARChannel()
     : mOpened(false)
     , mContentDisposition(0)
     , mContentLength(-1)
     , mLoadFlags(LOAD_NORMAL)
     , mStatus(NS_OK)
     , mIsPending(false)
+    , mEnableOMT(true)
+    , mPendingEvent()
     , mIsUnsafe(true)
     , mBlockRemoteFiles(false)
 {
+    LOG(("nsJARChannel::nsJARChannel [this=%p]\n", this));
     mBlockRemoteFiles = Preferences::GetBool("network.jar.block-remote-files", false);
 
     // hold an owning reference to the jar handler
     mJarHandler = gJarHandler;
 }
 
 nsJARChannel::~nsJARChannel()
 {
+    LOG(("nsJARChannel::~nsJARChannel [this=%p]\n", this));
     NS_ReleaseOnMainThreadSystemGroup("nsJARChannel::mLoadInfo",
                                       mLoadInfo.forget());
 }
 
 NS_IMPL_ISUPPORTS_INHERITED(nsJARChannel,
                             nsHashPropertyBag,
                             nsIRequest,
                             nsIChannel,
@@ -221,17 +226,24 @@ NS_IMPL_ISUPPORTS_INHERITED(nsJARChannel
                             nsIRequestObserver,
                             nsIThreadRetargetableRequest,
                             nsIThreadRetargetableStreamListener,
                             nsIJARChannel)
 
 nsresult
 nsJARChannel::Init(nsIURI *uri)
 {
+    LOG(("nsJARChannel::Init [this=%p]\n", this));
     nsresult rv;
+
+    mWorker = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
+    if (NS_FAILED(rv)) {
+        return rv;
+    }
+
     mJarURI = do_QueryInterface(uri, &rv);
     if (NS_FAILED(rv))
         return rv;
 
     mOriginalURI = mJarURI;
 
     // Prevent loading jar:javascript URIs (see bug 290982).
     nsCOMPtr<nsIURI> innerURI;
@@ -249,32 +261,33 @@ nsJARChannel::Init(nsIURI *uri)
 
     mJarURI->GetSpec(mSpec);
     return rv;
 }
 
 nsresult
 nsJARChannel::CreateJarInput(nsIZipReaderCache *jarCache, nsJARInputThunk **resultInput)
 {
+    LOG(("nsJARChannel::CreateJarInput [this=%p]\n", this));
     MOZ_ASSERT(resultInput);
     MOZ_ASSERT(mJarFile || mTempMem);
 
     // important to pass a clone of the file since the nsIFile impl is not
     // necessarily MT-safe
     nsCOMPtr<nsIFile> clonedFile;
     nsresult rv = NS_OK;
     if (mJarFile) {
         rv = mJarFile->Clone(getter_AddRefs(clonedFile));
         if (NS_FAILED(rv))
             return rv;
     }
 
     nsCOMPtr<nsIZipReader> reader;
     if (mPreCachedJarReader) {
-      reader = mPreCachedJarReader;
+        reader = mPreCachedJarReader;
     } else if (jarCache) {
         MOZ_ASSERT(mJarFile);
         if (mInnerJarEntry.IsEmpty())
             rv = jarCache->GetZip(clonedFile, getter_AddRefs(reader));
         else
             rv = jarCache->GetInnerZip(clonedFile, mInnerJarEntry,
                                        getter_AddRefs(reader));
     } else {
@@ -301,33 +314,32 @@ nsJARChannel::CreateJarInput(nsIZipReade
 
             rv = reader->OpenInner(outerReader, mInnerJarEntry);
         }
     }
     if (NS_FAILED(rv))
         return rv;
 
     RefPtr<nsJARInputThunk> input = new nsJARInputThunk(reader,
-                                                          mJarURI,
-                                                          mJarEntry,
-                                                          jarCache != nullptr
-                                                          );
+                                                        mJarURI,
+                                                        mJarEntry,
+                                                        jarCache != nullptr);
     rv = input->Init();
     if (NS_FAILED(rv))
         return rv;
 
     // Make GetContentLength meaningful
     mContentLength = input->GetContentLength();
 
     input.forget(resultInput);
     return NS_OK;
 }
 
 nsresult
-nsJARChannel::LookupFile(bool aAllowAsync)
+nsJARChannel::LookupFile()
 {
     LOG(("nsJARChannel::LookupFile [this=%p %s]\n", this, mSpec.get()));
 
     if (mJarFile)
         return NS_OK;
 
     nsresult rv;
 
@@ -373,34 +385,245 @@ nsJARChannel::LookupFile(bool aAllowAsyn
             }
         }
     }
 
     return rv;
 }
 
 nsresult
+CreateLocalJarInput(nsIZipReaderCache* aJarCache,
+                    nsIFile* aFile,
+                    const nsACString& aInnerJarEntry,
+                    nsIJARURI* aJarURI,
+                    const nsACString& aJarEntry,
+                    nsJARInputThunk** aResultInput)
+{
+    LOG(("nsJARChannel::CreateLocalJarInput [aJarCache=%p, %s, %s]\n",
+         aJarCache,
+         PromiseFlatCString(aInnerJarEntry).get(),
+         PromiseFlatCString(aJarEntry).get()));
+
+    MOZ_ASSERT(!NS_IsMainThread());
+    MOZ_ASSERT(aJarCache);
+    MOZ_ASSERT(aResultInput);
+
+    nsresult rv;
+
+    nsCOMPtr<nsIZipReader> reader;
+    if (aInnerJarEntry.IsEmpty()) {
+        rv = aJarCache->GetZip(aFile, getter_AddRefs(reader));
+    } else {
+        rv = aJarCache->GetInnerZip(aFile,
+                                    aInnerJarEntry,
+                                    getter_AddRefs(reader));
+    }
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+    }
+
+    RefPtr<nsJARInputThunk> input = new nsJARInputThunk(reader,
+                                                        aJarURI,
+                                                        aJarEntry,
+                                                        aJarCache != nullptr);
+    rv = input->Init();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+    }
+
+    input.forget(aResultInput);
+    return NS_OK;
+}
+
+nsresult
 nsJARChannel::OpenLocalFile()
 {
+    LOG(("nsJARChannel::OpenLocalFile [this=%p]\n", this));
+
+    MOZ_ASSERT(NS_IsMainThread());
+
+    MOZ_ASSERT(mWorker);
     MOZ_ASSERT(mIsPending);
+    MOZ_ASSERT(mJarFile);
 
     // Local files are always considered safe.
     mIsUnsafe = false;
 
-    RefPtr<nsJARInputThunk> input;
-    nsresult rv = CreateJarInput(gJarHandler->JarCache(),
-                                 getter_AddRefs(input));
-    if (NS_SUCCEEDED(rv)) {
-        // Create input stream pump and call AsyncRead as a block.
-        rv = NS_NewInputStreamPump(getter_AddRefs(mPump), input.forget());
-        if (NS_SUCCEEDED(rv))
-            rv = mPump->AsyncRead(this, nullptr);
+    nsresult rv;
+
+    // Set mLoadGroup and mOpened before AsyncOpen return, and set back if
+    // if failed when callback.
+    if (mLoadGroup) {
+        mLoadGroup->AddRequest(this, nullptr);
+    }
+    mOpened = true;
+
+    if (mPreCachedJarReader || !mEnableOMT) {
+        RefPtr<nsJARInputThunk> input;
+        rv = CreateJarInput(gJarHandler->JarCache(),
+                            getter_AddRefs(input));
+        if (NS_WARN_IF(NS_FAILED(rv))) {
+            return OnOpenLocalFileComplete(rv, true);
+        }
+        return ContinueOpenLocalFile(input, true);
+    }
+
+
+    nsCOMPtr<nsIZipReaderCache> jarCache = gJarHandler->JarCache();
+    if (NS_WARN_IF(!jarCache)) {
+        return NS_ERROR_UNEXPECTED;
+    }
+
+    nsCOMPtr<nsIFile> clonedFile;
+    rv = mJarFile->Clone(getter_AddRefs(clonedFile));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+    }
+
+    // clone mJarURI
+    nsCOMPtr<nsIURI> clonedURI;
+    rv = mJarURI->Clone(getter_AddRefs(clonedURI));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+    }
+    nsCOMPtr<nsIJARURI> clonedJarURI = do_QueryInterface(clonedURI, &rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
     }
 
-    return rv;
+    nsAutoCString jarEntry(mJarEntry);
+    nsAutoCString innerJarEntry(mInnerJarEntry);
+
+    RefPtr<nsJARChannel> self = this;
+    return mWorker->Dispatch(
+            NS_NewRunnableFunction("nsJARChannel::OpenLocalFile",
+                                   [self,
+                                   jarCache,
+                                   clonedFile,
+                                   clonedJarURI,
+                                   jarEntry,
+                                   innerJarEntry] () mutable {
+
+        RefPtr<nsJARInputThunk> input;
+        nsresult rv = CreateLocalJarInput(jarCache,
+                                          clonedFile,
+                                          innerJarEntry,
+                                          clonedJarURI,
+                                          jarEntry,
+                                          getter_AddRefs(input));
+
+        NS_ReleaseOnMainThreadSystemGroup("nsJARChannel::clonedJarURI",
+                                          clonedJarURI.forget());
+
+        nsCOMPtr<nsIRunnable> target;
+        if (NS_SUCCEEDED(rv)) {
+            target = NewRunnableMethod<RefPtr<nsJARInputThunk>, bool>(
+                "nsJARChannel::ContinueOpenLocalFile",
+                self,
+                &nsJARChannel::ContinueOpenLocalFile,
+                input,
+                false);
+        } else {
+            target = NewRunnableMethod<nsresult, bool>(
+                "nsJARChannel::OnOpenLocalFileComplete",
+                self,
+                &nsJARChannel::OnOpenLocalFileComplete,
+                rv,
+                false);
+        }
+
+        // nsJARChannel must be release on main thread, and sometimes
+        // this still hold nsJARChannel after dispatched.
+        self = nullptr;
+
+        NS_DispatchToMainThread(target.forget());
+    }));
+}
+
+nsresult
+nsJARChannel::ContinueOpenLocalFile(nsJARInputThunk* aInput, bool aIsSyncCall)
+{
+    LOG(("nsJARChannel::ContinueOpenLocalFile [this=%p %p]\n", this, aInput));
+
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(mIsPending);
+
+    // Make GetContentLength meaningful
+    mContentLength = aInput->GetContentLength();
+
+    nsresult rv;
+    RefPtr<nsJARInputThunk> input = aInput;
+    // Create input stream pump and call AsyncRead as a block.
+    rv = NS_NewInputStreamPump(getter_AddRefs(mPump), input.forget());
+    if (NS_SUCCEEDED(rv)) {
+        rv = mPump->AsyncRead(this, nullptr);
+    }
+
+    if (NS_SUCCEEDED(rv)) {
+        rv = CheckPendingEvents();
+    }
+
+    return OnOpenLocalFileComplete(rv, aIsSyncCall);
+}
+
+nsresult
+nsJARChannel::OnOpenLocalFileComplete(nsresult aResult, bool aIsSyncCall)
+{
+    LOG(("nsJARChannel::OnOpenLocalFileComplete [this=%p %08x]\n",
+         this,
+         static_cast<uint32_t>(aResult)));
+
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(mIsPending);
+
+    if (NS_FAILED(aResult)) {
+        if (!aIsSyncCall) {
+            NotifyError(aResult);
+        }
+
+        if (mLoadGroup) {
+            mLoadGroup->RemoveRequest(this, nullptr, aResult);
+        }
+
+        mOpened = false;
+        mIsPending = false;
+        mListenerContext = nullptr;
+        mListener = nullptr;
+        mCallbacks = nullptr;
+        mProgressSink = nullptr;
+
+        return aResult;
+    }
+
+    return NS_OK;
+}
+
+nsresult nsJARChannel::CheckPendingEvents()
+{
+    MOZ_ASSERT(NS_IsMainThread());
+    MOZ_ASSERT(mIsPending);
+    MOZ_ASSERT(mPump);
+
+    nsresult rv;
+
+    auto suspendCount = mPendingEvent.suspendCount;
+    while (suspendCount--) {
+        if (NS_WARN_IF(NS_FAILED(rv = mPump->Suspend()))) {
+            return rv;
+        }
+    }
+
+    if (mPendingEvent.isCanceled) {
+        if (NS_WARN_IF(NS_FAILED(rv = mPump->Cancel(mStatus)))) {
+            return rv;
+        }
+        mPendingEvent.isCanceled = false;
+    }
+
+    return NS_OK;
 }
 
 void
 nsJARChannel::NotifyError(nsresult aError)
 {
     MOZ_ASSERT(NS_FAILED(aError));
 
     mStatus = aError;
@@ -444,40 +667,51 @@ nsJARChannel::GetStatus(nsresult *status
         *status = mStatus;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsJARChannel::Cancel(nsresult status)
 {
     mStatus = status;
-    if (mPump)
+    if (mPump) {
         return mPump->Cancel(status);
+    }
 
-    NS_ASSERTION(!mIsPending, "need to implement cancel when downloading");
+    if (mIsPending) {
+        mPendingEvent.isCanceled = true;
+    }
+
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsJARChannel::Suspend()
 {
-    if (mPump)
+    ++mPendingEvent.suspendCount;
+
+    if (mPump) {
         return mPump->Suspend();
+    }
 
-    NS_ASSERTION(!mIsPending, "need to implement suspend when downloading");
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsJARChannel::Resume()
 {
-    if (mPump)
+    if (NS_WARN_IF(mPendingEvent.suspendCount == 0)) {
+        return NS_ERROR_UNEXPECTED;
+    }
+    --mPendingEvent.suspendCount;
+
+    if (mPump) {
         return mPump->Resume();
+    }
 
-    NS_ASSERTION(!mIsPending, "need to implement resume when downloading");
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsJARChannel::GetLoadFlags(nsLoadFlags *aLoadFlags)
 {
     *aLoadFlags = mLoadFlags;
     return NS_OK;
@@ -721,17 +955,17 @@ nsJARChannel::Open(nsIInputStream **stre
     LOG(("nsJARChannel::Open [this=%p]\n", this));
 
     NS_ENSURE_TRUE(!mOpened, NS_ERROR_IN_PROGRESS);
     NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
 
     mJarFile = nullptr;
     mIsUnsafe = true;
 
-    nsresult rv = LookupFile(false);
+    nsresult rv = LookupFile();
     if (NS_FAILED(rv))
         return rv;
 
     // If mJarInput was not set by LookupFile, the JAR is a remote jar.
     if (!mJarFile) {
         NS_NOTREACHED("need sync downloader");
         return NS_ERROR_NOT_IMPLEMENTED;
     }
@@ -746,25 +980,27 @@ nsJARChannel::Open(nsIInputStream **stre
     // local files are always considered safe
     mIsUnsafe = false;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsJARChannel::Open2(nsIInputStream** aStream)
 {
+    LOG(("nsJARChannel::Open2 [this=%p]\n", this));
     nsCOMPtr<nsIStreamListener> listener;
     nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
     NS_ENSURE_SUCCESS(rv, rv);
     return Open(aStream);
 }
 
 NS_IMETHODIMP
 nsJARChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx)
 {
+    LOG(("nsJARChannel::AsyncOpen [this=%p]\n", this));
     MOZ_ASSERT(!mLoadInfo ||
                mLoadInfo->GetSecurityMode() == 0 ||
                mLoadInfo->GetInitialSecurityCheckDone() ||
                (mLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
                 nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())),
                "security flags in loadInfo but asyncOpen2() not called");
 
     LOG(("nsJARChannel::AsyncOpen [this=%p]\n", this));
@@ -778,17 +1014,17 @@ nsJARChannel::AsyncOpen(nsIStreamListene
 
     // Initialize mProgressSink
     NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, mProgressSink);
 
     mListener = listener;
     mListenerContext = ctx;
     mIsPending = true;
 
-    nsresult rv = LookupFile(true);
+    nsresult rv = LookupFile();
     if (NS_FAILED(rv)) {
         mIsPending = false;
         mListenerContext = nullptr;
         mListener = nullptr;
         mCallbacks = nullptr;
         mProgressSink = nullptr;
         return rv;
     }
@@ -829,41 +1065,46 @@ nsJARChannel::AsyncOpen(nsIStreamListene
             return rv;
         }
         if (mLoadInfo && mLoadInfo->GetEnforceSecurity()) {
             rv = channel->AsyncOpen2(downloader);
         }
         else {
             rv = channel->AsyncOpen(downloader, nullptr);
         }
+
     }
     else {
         rv = OpenLocalFile();
+        if (NS_SUCCEEDED(rv)) {
+            return NS_OK;
+        }
     }
 
     if (NS_FAILED(rv)) {
         mIsPending = false;
         mListenerContext = nullptr;
         mListener = nullptr;
         mCallbacks = nullptr;
         mProgressSink = nullptr;
         return rv;
     }
 
     if (mLoadGroup)
         mLoadGroup->AddRequest(this, nullptr);
 
     mOpened = true;
-
+    LOG(("nsJARChannel::AsyncOpen [this=%p] 8\n", this));
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsJARChannel::AsyncOpen2(nsIStreamListener *aListener)
 {
+    LOG(("nsJARChannel::AsyncOpen2 [this=%p]\n", this));
   nsCOMPtr<nsIStreamListener> listener = aListener;
   nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
   if (NS_FAILED(rv)) {
       mIsPending = false;
       mListenerContext = nullptr;
       mListener = nullptr;
       mCallbacks = nullptr;
       mProgressSink = nullptr;
@@ -949,17 +1190,17 @@ nsJARChannel::EnsureCached(bool *aIsCach
 
     *aIsCached = true;
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsJARChannel::GetZipEntry(nsIZipEntry **aZipEntry)
 {
-    nsresult rv = LookupFile(false);
+    nsresult rv = LookupFile();
     if (NS_FAILED(rv))
         return rv;
 
     if (!mJarFile)
         return NS_ERROR_NOT_AVAILABLE;
 
     nsCOMPtr<nsIZipReader> reader;
     rv = gJarHandler->JarCache()->GetZip(mJarFile, getter_AddRefs(reader));
--- a/modules/libjar/nsJARChannel.h
+++ b/modules/libjar/nsJARChannel.h
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsJARChannel_h__
 #define nsJARChannel_h__
 
 #include "mozilla/net/MemoryDownloader.h"
 #include "nsIJARChannel.h"
 #include "nsIJARURI.h"
+#include "nsIEventTarget.h"
 #include "nsIInputStreamPump.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIProgressEventSink.h"
 #include "nsIStreamListener.h"
 #include "nsIZipReader.h"
 #include "nsILoadGroup.h"
 #include "nsILoadInfo.h"
 #include "nsIThreadRetargetableRequest.h"
@@ -53,18 +54,21 @@ public:
     nsresult Init(nsIURI *uri);
 
     void SetFile(nsIFile *file);
 
 private:
     virtual ~nsJARChannel();
 
     nsresult CreateJarInput(nsIZipReaderCache *, nsJARInputThunk **);
-    nsresult LookupFile(bool aAllowAsync);
+    nsresult LookupFile();
     nsresult OpenLocalFile();
+    nsresult ContinueOpenLocalFile(nsJARInputThunk* aInput, bool aIsSyncCall);
+    nsresult OnOpenLocalFileComplete(nsresult aResult, bool aIsSyncCall);
+    nsresult CheckPendingEvents();
     void NotifyError(nsresult aError);
     void FireOnProgress(uint64_t aProgress);
     virtual void OnDownloadComplete(mozilla::net::MemoryDownloader* aDownloader,
                                     nsIRequest* aRequest,
                                     nsISupports* aCtxt,
                                     nsresult aStatus,
                                     mozilla::net::MemoryDownloader::Data aData)
         override;
@@ -88,28 +92,39 @@ private:
     nsCString                       mContentCharset;
     nsCString                       mContentDispositionHeader;
     /* mContentDisposition is uninitialized if mContentDispositionHeader is
      * empty */
     uint32_t                        mContentDisposition;
     int64_t                         mContentLength;
     uint32_t                        mLoadFlags;
     nsresult                        mStatus;
-    bool                            mIsPending;
+    bool                            mIsPending; // the AsyncOpen is in progress.
+
+    bool                            mEnableOMT;
+    // |Cancel()|, |Suspend()|, and |Resume()| might be called during AsyncOpen.
+    struct {
+        bool isCanceled;
+        uint32_t suspendCount;
+    }                               mPendingEvent;
+
     bool                            mIsUnsafe;
 
     mozilla::net::MemoryDownloader::Data mTempMem;
     nsCOMPtr<nsIInputStreamPump>    mPump;
     // mRequest is only non-null during OnStartRequest, so we'll have a pointer
     // to the request if we get called back via RetargetDeliveryTo.
     nsCOMPtr<nsIRequest>            mRequest;
     nsCOMPtr<nsIFile>               mJarFile;
     nsCOMPtr<nsIFile>               mJarFileOverride;
     nsCOMPtr<nsIZipReader>          mPreCachedJarReader;
     nsCOMPtr<nsIURI>                mJarBaseURI;
     nsCString                       mJarEntry;
     nsCString                       mInnerJarEntry;
 
+    // use StreamTransportService as background thread
+    nsCOMPtr<nsIEventTarget>        mWorker;
+
     // True if this channel should not download any remote files.
     bool                            mBlockRemoteFiles;
 };
 
 #endif // nsJARChannel_h__