tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsJARChannel.cpp (40499B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set sw=2 ts=8 et tw=80 : */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/dom/BrowsingContext.h"
      8 #include "nsJAR.h"
      9 #include "nsJARChannel.h"
     10 #include "nsJARProtocolHandler.h"
     11 #include "nsMimeTypes.h"
     12 #include "nsNetUtil.h"
     13 #include "nsEscape.h"
     14 #include "nsContentUtils.h"
     15 #include "nsProxyRelease.h"
     16 #include "nsContentSecurityManager.h"
     17 #include "nsComponentManagerUtils.h"
     18 
     19 #include "nsIFileURL.h"
     20 #include "nsIURIMutator.h"
     21 
     22 #include "mozilla/BasePrincipal.h"
     23 #include "mozilla/ErrorNames.h"
     24 #include "mozilla/IntegerPrintfMacros.h"
     25 #include "mozilla/Preferences.h"
     26 #include "mozilla/ScopeExit.h"
     27 #include "mozilla/StaticPrefs_network.h"
     28 #include "mozilla/glean/LibjarMetrics.h"
     29 #include "private/pprio.h"
     30 #include "nsInputStreamPump.h"
     31 #include "nsThreadUtils.h"
     32 #include "nsJARProtocolHandler.h"
     33 
     34 using namespace mozilla;
     35 using namespace mozilla::net;
     36 
     37 static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
     38 
     39 // the entry for a directory will either be empty (in the case of the
     40 // top-level directory) or will end with a slash
     41 #define ENTRY_IS_DIRECTORY(_entry) \
     42  ((_entry).IsEmpty() || '/' == (_entry).Last())
     43 
     44 //-----------------------------------------------------------------------------
     45 
     46 //
     47 // set MOZ_LOG=nsJarProtocol:5
     48 //
     49 static LazyLogModule gJarProtocolLog("nsJarProtocol");
     50 
     51 // Ignore any LOG macro that we inherit from arbitrary headers. (We define our
     52 // own LOG macro below.)
     53 #ifdef LOG
     54 #  undef LOG
     55 #endif
     56 #ifdef LOG_ENABLED
     57 #  undef LOG_ENABLED
     58 #endif
     59 
     60 #define LOG(args) MOZ_LOG(gJarProtocolLog, mozilla::LogLevel::Debug, args)
     61 #define LOG_ENABLED() MOZ_LOG_TEST(gJarProtocolLog, mozilla::LogLevel::Debug)
     62 
     63 //-----------------------------------------------------------------------------
     64 // nsJARInputThunk
     65 //
     66 // this class allows us to do some extra work on the stream transport thread.
     67 //-----------------------------------------------------------------------------
     68 
     69 class nsJARInputThunk : public nsIInputStream {
     70 public:
     71  NS_DECL_THREADSAFE_ISUPPORTS
     72  NS_DECL_NSIINPUTSTREAM
     73 
     74  nsJARInputThunk(nsIZipReader* zipReader, const nsACString& jarEntry,
     75                  bool usingJarCache)
     76      : mUsingJarCache(usingJarCache),
     77        mJarReader(zipReader),
     78        mJarEntry(jarEntry),
     79        mContentLength(-1) {
     80    MOZ_DIAGNOSTIC_ASSERT(zipReader, "zipReader must not be null");
     81  }
     82 
     83  int64_t GetContentLength() { return mContentLength; }
     84 
     85  nsresult Init();
     86 
     87 private:
     88  virtual ~nsJARInputThunk() { Close(); }
     89 
     90  bool mUsingJarCache;
     91  nsCOMPtr<nsIZipReader> mJarReader;
     92  nsCOMPtr<nsIInputStream> mJarStream;
     93  nsCString mJarEntry;
     94  int64_t mContentLength;
     95 };
     96 
     97 NS_IMPL_ISUPPORTS(nsJARInputThunk, nsIInputStream)
     98 
     99 nsresult nsJARInputThunk::Init() {
    100  if (!mJarReader) {
    101    return NS_ERROR_INVALID_ARG;
    102  }
    103  nsresult rv =
    104      mJarReader->GetInputStream(mJarEntry, getter_AddRefs(mJarStream));
    105  if (NS_FAILED(rv)) {
    106    return rv;
    107  }
    108 
    109  // ask the JarStream for the content length
    110  uint64_t avail;
    111  rv = mJarStream->Available((uint64_t*)&avail);
    112  if (NS_FAILED(rv)) return rv;
    113 
    114  mContentLength = avail < INT64_MAX ? (int64_t)avail : -1;
    115 
    116  return NS_OK;
    117 }
    118 
    119 NS_IMETHODIMP
    120 nsJARInputThunk::Close() {
    121  nsresult rv = NS_OK;
    122 
    123  if (mJarStream) rv = mJarStream->Close();
    124 
    125  if (!mUsingJarCache && mJarReader) mJarReader->Close();
    126 
    127  mJarReader = nullptr;
    128 
    129  return rv;
    130 }
    131 
    132 NS_IMETHODIMP
    133 nsJARInputThunk::Available(uint64_t* avail) {
    134  return mJarStream->Available(avail);
    135 }
    136 
    137 NS_IMETHODIMP
    138 nsJARInputThunk::StreamStatus() { return mJarStream->StreamStatus(); }
    139 
    140 NS_IMETHODIMP
    141 nsJARInputThunk::Read(char* buf, uint32_t count, uint32_t* countRead) {
    142  return mJarStream->Read(buf, count, countRead);
    143 }
    144 
    145 NS_IMETHODIMP
    146 nsJARInputThunk::ReadSegments(nsWriteSegmentFun writer, void* closure,
    147                              uint32_t count, uint32_t* countRead) {
    148  // stream transport does only calls Read()
    149  return NS_ERROR_NOT_IMPLEMENTED;
    150 }
    151 
    152 NS_IMETHODIMP
    153 nsJARInputThunk::IsNonBlocking(bool* nonBlocking) {
    154  *nonBlocking = false;
    155  return NS_OK;
    156 }
    157 
    158 //-----------------------------------------------------------------------------
    159 // nsJARChannel
    160 //-----------------------------------------------------------------------------
    161 
    162 nsJARChannel::nsJARChannel() {
    163  LOG(("nsJARChannel::nsJARChannel [this=%p]\n", this));
    164  // hold an owning reference to the jar handler
    165  mJarHandler = gJarHandler;
    166 }
    167 
    168 nsJARChannel::~nsJARChannel() {
    169  LOG(("nsJARChannel::~nsJARChannel [this=%p]\n", this));
    170  if (NS_IsMainThread()) {
    171    return;
    172  }
    173 
    174  // Proxy release the following members to main thread.
    175  NS_ReleaseOnMainThread("nsJARChannel::mLoadInfo", mLoadInfo.forget());
    176  NS_ReleaseOnMainThread("nsJARChannel::mCallbacks", mCallbacks.forget());
    177  NS_ReleaseOnMainThread("nsJARChannel::mProgressSink", mProgressSink.forget());
    178  NS_ReleaseOnMainThread("nsJARChannel::mLoadGroup", mLoadGroup.forget());
    179  NS_ReleaseOnMainThread("nsJARChannel::mListener", mListener.forget());
    180 }
    181 
    182 NS_IMPL_ISUPPORTS_INHERITED(nsJARChannel, nsHashPropertyBag, nsIRequest,
    183                            nsIChannel, nsIStreamListener, nsIRequestObserver,
    184                            nsIThreadRetargetableRequest,
    185                            nsIThreadRetargetableStreamListener, nsIJARChannel)
    186 
    187 nsresult nsJARChannel::Init(nsIURI* uri) {
    188  LOG(("nsJARChannel::Init [this=%p]\n", this));
    189  nsresult rv;
    190 
    191  mWorker = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
    192  if (NS_FAILED(rv)) {
    193    return rv;
    194  }
    195 
    196  mJarURI = do_QueryInterface(uri, &rv);
    197  if (NS_FAILED(rv)) return rv;
    198 
    199  mOriginalURI = mJarURI;
    200 
    201  // Prevent loading jar:javascript URIs (see bug 290982).
    202  nsCOMPtr<nsIURI> innerURI;
    203  rv = mJarURI->GetJARFile(getter_AddRefs(innerURI));
    204  if (NS_FAILED(rv)) {
    205    return rv;
    206  }
    207 
    208  if (innerURI->SchemeIs("javascript")) {
    209    NS_WARNING("blocking jar:javascript:");
    210    return NS_ERROR_INVALID_ARG;
    211  }
    212 
    213  mJarURI->GetSpec(mSpec);
    214  return rv;
    215 }
    216 
    217 nsresult nsJARChannel::CreateJarInput(nsIZipReaderCache* jarCache,
    218                                      nsJARInputThunk** resultInput) {
    219  LOG(("nsJARChannel::CreateJarInput [this=%p]\n", this));
    220  MOZ_ASSERT(resultInput);
    221  MOZ_ASSERT(mJarFile);
    222 
    223  // important to pass a clone of the file since the nsIFile impl is not
    224  // necessarily MT-safe
    225  nsCOMPtr<nsIFile> clonedFile;
    226  nsresult rv = NS_OK;
    227  if (mJarFile) {
    228    rv = mJarFile->Clone(getter_AddRefs(clonedFile));
    229    if (NS_FAILED(rv)) return rv;
    230  }
    231 
    232  nsCOMPtr<nsIZipReader> reader;
    233  if (mPreCachedJarReader) {
    234    reader = mPreCachedJarReader;
    235  } else if (jarCache) {
    236    if (mInnerJarEntry.IsEmpty())
    237      rv = jarCache->GetZip(clonedFile, getter_AddRefs(reader));
    238    else
    239      rv = jarCache->GetInnerZip(clonedFile, mInnerJarEntry,
    240                                 getter_AddRefs(reader));
    241  } else {
    242    // create an uncached jar reader
    243    nsCOMPtr<nsIZipReader> outerReader = do_CreateInstance(kZipReaderCID, &rv);
    244    if (NS_FAILED(rv)) return rv;
    245 
    246    rv = outerReader->Open(clonedFile);
    247    if (NS_FAILED(rv)) return rv;
    248 
    249    if (mInnerJarEntry.IsEmpty())
    250      reader = outerReader;
    251    else {
    252      reader = do_CreateInstance(kZipReaderCID, &rv);
    253      if (NS_FAILED(rv)) return rv;
    254 
    255      rv = reader->OpenInner(outerReader, mInnerJarEntry);
    256    }
    257  }
    258  if (NS_FAILED(rv)) return rv;
    259 
    260  RefPtr<nsJARInputThunk> input =
    261      new nsJARInputThunk(reader, mJarEntry, jarCache != nullptr);
    262  rv = input->Init();
    263  if (NS_FAILED(rv)) {
    264    return rv;
    265  }
    266 
    267  // Make GetContentLength meaningful
    268  mContentLength = input->GetContentLength();
    269 
    270  input.forget(resultInput);
    271  return NS_OK;
    272 }
    273 
    274 nsresult nsJARChannel::LookupFile() {
    275  LOG(("nsJARChannel::LookupFile [this=%p %s]\n", this, mSpec.get()));
    276 
    277  if (mJarFile) return NS_OK;
    278 
    279  nsresult rv;
    280 
    281  rv = mJarURI->GetJARFile(getter_AddRefs(mJarBaseURI));
    282  if (NS_FAILED(rv)) return rv;
    283 
    284  rv = mJarURI->GetJAREntry(mJarEntry);
    285  if (NS_FAILED(rv)) return rv;
    286 
    287  // The name of the JAR entry must not contain URL-escaped characters:
    288  // we're moving from URL domain to a filename domain here. nsStandardURL
    289  // does basic escaping by default, which breaks reading zipped files which
    290  // have e.g. spaces in their filenames.
    291  NS_UnescapeURL(mJarEntry);
    292 
    293  if (mJarFileOverride) {
    294    mJarFile = mJarFileOverride;
    295    LOG(("nsJARChannel::LookupFile [this=%p] Overriding mJarFile\n", this));
    296    return NS_OK;
    297  }
    298 
    299  // try to get a nsIFile directly from the url, which will often succeed.
    300  {
    301    nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(mJarBaseURI);
    302    if (fileURL) fileURL->GetFile(getter_AddRefs(mJarFile));
    303  }
    304 
    305  // try to handle a nested jar
    306  if (!mJarFile) {
    307    nsCOMPtr<nsIJARURI> jarURI = do_QueryInterface(mJarBaseURI);
    308    if (jarURI) {
    309      nsCOMPtr<nsIFileURL> fileURL;
    310      nsCOMPtr<nsIURI> innerJarURI;
    311      rv = jarURI->GetJARFile(getter_AddRefs(innerJarURI));
    312      if (NS_SUCCEEDED(rv)) fileURL = do_QueryInterface(innerJarURI);
    313      if (fileURL) {
    314        fileURL->GetFile(getter_AddRefs(mJarFile));
    315        jarURI->GetJAREntry(mInnerJarEntry);
    316      }
    317    }
    318  }
    319 
    320  return rv;
    321 }
    322 
    323 nsresult CreateLocalJarInput(nsIZipReaderCache* aJarCache, nsIFile* aFile,
    324                             const nsACString& aInnerJarEntry,
    325                             const nsACString& aJarEntry,
    326                             nsJARInputThunk** aResultInput) {
    327  LOG(("nsJARChannel::CreateLocalJarInput [aJarCache=%p, %s, %s]\n", aJarCache,
    328       PromiseFlatCString(aInnerJarEntry).get(),
    329       PromiseFlatCString(aJarEntry).get()));
    330 
    331  MOZ_ASSERT(!NS_IsMainThread());
    332  MOZ_ASSERT(aJarCache);
    333  MOZ_ASSERT(aResultInput);
    334 
    335  nsresult rv;
    336 
    337  nsCOMPtr<nsIZipReader> reader;
    338  if (aInnerJarEntry.IsEmpty()) {
    339    rv = aJarCache->GetZip(aFile, getter_AddRefs(reader));
    340  } else {
    341    rv = aJarCache->GetInnerZip(aFile, aInnerJarEntry, getter_AddRefs(reader));
    342  }
    343  if (NS_WARN_IF(NS_FAILED(rv))) {
    344    return rv;
    345  }
    346 
    347  RefPtr<nsJARInputThunk> input =
    348      new nsJARInputThunk(reader, aJarEntry, aJarCache != nullptr);
    349  rv = input->Init();
    350  if (NS_FAILED(rv)) {
    351    return rv;
    352  }
    353 
    354  input.forget(aResultInput);
    355  return NS_OK;
    356 }
    357 
    358 nsresult nsJARChannel::OpenLocalFile() {
    359  LOG(("nsJARChannel::OpenLocalFile [this=%p]\n", this));
    360 
    361  MOZ_ASSERT(NS_IsMainThread());
    362 
    363  MOZ_ASSERT(mWorker);
    364  MOZ_ASSERT(mIsPending);
    365  MOZ_ASSERT(mJarFile);
    366 
    367  nsresult rv;
    368 
    369  // Set mLoadGroup and mOpened before AsyncOpen return, and set back if
    370  // if failed when callback.
    371  if (mLoadGroup) {
    372    mLoadGroup->AddRequest(this, nullptr);
    373  }
    374  SetOpened();
    375 
    376  if (mPreCachedJarReader || !mEnableOMT) {
    377    RefPtr<nsJARInputThunk> input;
    378    rv = CreateJarInput(gJarHandler->JarCache(), getter_AddRefs(input));
    379    if (NS_WARN_IF(NS_FAILED(rv))) {
    380      return OnOpenLocalFileComplete(rv, true);
    381    }
    382    return ContinueOpenLocalFile(input, true);
    383  }
    384 
    385  nsCOMPtr<nsIZipReaderCache> jarCache = gJarHandler->JarCache();
    386  if (NS_WARN_IF(!jarCache)) {
    387    return NS_ERROR_UNEXPECTED;
    388  }
    389 
    390  nsCOMPtr<nsIFile> clonedFile;
    391  rv = mJarFile->Clone(getter_AddRefs(clonedFile));
    392  if (NS_WARN_IF(NS_FAILED(rv))) {
    393    return rv;
    394  }
    395 
    396  nsAutoCString jarEntry(mJarEntry);
    397  nsAutoCString innerJarEntry(mInnerJarEntry);
    398 
    399  RefPtr<nsJARChannel> self = this;
    400  return mWorker->Dispatch(NS_NewRunnableFunction(
    401      "nsJARChannel::OpenLocalFile",
    402      [self, jarCache, clonedFile, jarEntry, innerJarEntry]() mutable {
    403        RefPtr<nsJARInputThunk> input;
    404        nsresult rv = CreateLocalJarInput(jarCache, clonedFile, innerJarEntry,
    405                                          jarEntry, getter_AddRefs(input));
    406 
    407        nsCOMPtr<nsIRunnable> target;
    408        if (NS_SUCCEEDED(rv)) {
    409          target = NewRunnableMethod<RefPtr<nsJARInputThunk>, bool>(
    410              "nsJARChannel::ContinueOpenLocalFile", self,
    411              &nsJARChannel::ContinueOpenLocalFile, input, false);
    412        } else {
    413          target = NewRunnableMethod<nsresult, bool>(
    414              "nsJARChannel::OnOpenLocalFileComplete", self,
    415              &nsJARChannel::OnOpenLocalFileComplete, rv, false);
    416        }
    417 
    418        // nsJARChannel must be release on main thread, and sometimes
    419        // this still hold nsJARChannel after dispatched.
    420        self = nullptr;
    421 
    422        NS_DispatchToMainThread(target.forget());
    423      }));
    424 }
    425 
    426 nsresult nsJARChannel::ContinueOpenLocalFile(nsJARInputThunk* aInput,
    427                                             bool aIsSyncCall) {
    428  LOG(("nsJARChannel::ContinueOpenLocalFile [this=%p %p]\n", this, aInput));
    429 
    430  MOZ_ASSERT(NS_IsMainThread());
    431  MOZ_ASSERT(mIsPending);
    432 
    433  // Make GetContentLength meaningful
    434  mContentLength = aInput->GetContentLength();
    435 
    436  nsresult rv;
    437  RefPtr<nsJARInputThunk> input = aInput;
    438  // Create input stream pump and call AsyncRead as a block.
    439  rv = NS_NewInputStreamPump(getter_AddRefs(mPump), input.forget());
    440  if (NS_SUCCEEDED(rv)) {
    441    rv = mPump->AsyncRead(this);
    442  }
    443 
    444  if (NS_SUCCEEDED(rv)) {
    445    rv = CheckPendingEvents();
    446  }
    447 
    448  return OnOpenLocalFileComplete(rv, aIsSyncCall);
    449 }
    450 
    451 nsresult nsJARChannel::OnOpenLocalFileComplete(nsresult aResult,
    452                                               bool aIsSyncCall) {
    453  LOG(("nsJARChannel::OnOpenLocalFileComplete [this=%p %08x]\n", this,
    454       static_cast<uint32_t>(aResult)));
    455 
    456  MOZ_ASSERT(NS_IsMainThread());
    457  MOZ_ASSERT(mIsPending);
    458 
    459  if (NS_FAILED(aResult)) {
    460    if (aResult == NS_ERROR_FILE_NOT_FOUND) {
    461      CheckForBrokenChromeURL(mLoadInfo, mOriginalURI);
    462    }
    463    if (!aIsSyncCall) {
    464      NotifyError(aResult);
    465    }
    466 
    467    if (mLoadGroup) {
    468      mLoadGroup->RemoveRequest(this, nullptr, aResult);
    469    }
    470 
    471    mOpened = false;
    472    mIsPending = false;
    473    mListener = nullptr;
    474    mCallbacks = nullptr;
    475    mProgressSink = nullptr;
    476 
    477    return aResult;
    478  }
    479 
    480  return NS_OK;
    481 }
    482 
    483 nsresult nsJARChannel::CheckPendingEvents() {
    484  MOZ_ASSERT(NS_IsMainThread());
    485  MOZ_ASSERT(mIsPending);
    486  MOZ_ASSERT(mPump);
    487 
    488  nsresult rv;
    489 
    490  uint32_t suspendCount = mPendingEvent.suspendCount;
    491  while (suspendCount--) {
    492    if (NS_WARN_IF(NS_FAILED(rv = mPump->Suspend()))) {
    493      return rv;
    494    }
    495  }
    496 
    497  if (mPendingEvent.isCanceled) {
    498    if (NS_WARN_IF(NS_FAILED(rv = mPump->Cancel(mStatus)))) {
    499      return rv;
    500    }
    501    mPendingEvent.isCanceled = false;
    502  }
    503 
    504  return NS_OK;
    505 }
    506 
    507 void nsJARChannel::NotifyError(nsresult aError) {
    508  MOZ_ASSERT(NS_FAILED(aError));
    509 
    510  mStatus = aError;
    511 
    512  OnStartRequest(nullptr);
    513  OnStopRequest(nullptr, aError);
    514 }
    515 
    516 void nsJARChannel::FireOnProgress(uint64_t aProgress) {
    517  MOZ_ASSERT(NS_IsMainThread());
    518  MOZ_ASSERT(mProgressSink);
    519 
    520  mProgressSink->OnProgress(this, aProgress, mContentLength);
    521 }
    522 
    523 //-----------------------------------------------------------------------------
    524 // nsIRequest
    525 //-----------------------------------------------------------------------------
    526 
    527 NS_IMETHODIMP
    528 nsJARChannel::GetName(nsACString& result) { return mJarURI->GetSpec(result); }
    529 
    530 NS_IMETHODIMP
    531 nsJARChannel::IsPending(bool* result) {
    532  *result = mIsPending;
    533  return NS_OK;
    534 }
    535 
    536 NS_IMETHODIMP
    537 nsJARChannel::GetStatus(nsresult* status) {
    538  if (mPump && NS_SUCCEEDED(mStatus))
    539    mPump->GetStatus(status);
    540  else
    541    *status = mStatus;
    542  return NS_OK;
    543 }
    544 
    545 NS_IMETHODIMP nsJARChannel::SetCanceledReason(const nsACString& aReason) {
    546  return SetCanceledReasonImpl(aReason);
    547 }
    548 
    549 NS_IMETHODIMP nsJARChannel::GetCanceledReason(nsACString& aReason) {
    550  return GetCanceledReasonImpl(aReason);
    551 }
    552 
    553 NS_IMETHODIMP nsJARChannel::CancelWithReason(nsresult aStatus,
    554                                             const nsACString& aReason) {
    555  return CancelWithReasonImpl(aStatus, aReason);
    556 }
    557 
    558 NS_IMETHODIMP
    559 nsJARChannel::Cancel(nsresult status) {
    560  mCanceled = true;
    561  mStatus = status;
    562  if (mPump) {
    563    return mPump->Cancel(status);
    564  }
    565 
    566  if (mIsPending) {
    567    mPendingEvent.isCanceled = true;
    568  }
    569 
    570  return NS_OK;
    571 }
    572 
    573 NS_IMETHODIMP
    574 nsJARChannel::GetCanceled(bool* aCanceled) {
    575  *aCanceled = mCanceled;
    576  return NS_OK;
    577 }
    578 
    579 NS_IMETHODIMP
    580 nsJARChannel::Suspend() {
    581  ++mPendingEvent.suspendCount;
    582 
    583  if (mPump) {
    584    return mPump->Suspend();
    585  }
    586 
    587  return NS_OK;
    588 }
    589 
    590 NS_IMETHODIMP
    591 nsJARChannel::Resume() {
    592  if (NS_WARN_IF(mPendingEvent.suspendCount == 0)) {
    593    return NS_ERROR_UNEXPECTED;
    594  }
    595  --mPendingEvent.suspendCount;
    596 
    597  if (mPump) {
    598    return mPump->Resume();
    599  }
    600 
    601  return NS_OK;
    602 }
    603 
    604 NS_IMETHODIMP
    605 nsJARChannel::GetLoadFlags(nsLoadFlags* aLoadFlags) {
    606  *aLoadFlags = mLoadFlags;
    607  return NS_OK;
    608 }
    609 
    610 NS_IMETHODIMP
    611 nsJARChannel::SetLoadFlags(nsLoadFlags aLoadFlags) {
    612  mLoadFlags = aLoadFlags;
    613  return NS_OK;
    614 }
    615 
    616 NS_IMETHODIMP
    617 nsJARChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
    618  return GetTRRModeImpl(aTRRMode);
    619 }
    620 
    621 NS_IMETHODIMP
    622 nsJARChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
    623  return SetTRRModeImpl(aTRRMode);
    624 }
    625 
    626 NS_IMETHODIMP
    627 nsJARChannel::GetIsDocument(bool* aIsDocument) {
    628  return NS_GetIsDocumentChannel(this, aIsDocument);
    629 }
    630 
    631 NS_IMETHODIMP
    632 nsJARChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) {
    633  NS_IF_ADDREF(*aLoadGroup = mLoadGroup);
    634  return NS_OK;
    635 }
    636 
    637 NS_IMETHODIMP
    638 nsJARChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) {
    639  mLoadGroup = aLoadGroup;
    640  return NS_OK;
    641 }
    642 
    643 //-----------------------------------------------------------------------------
    644 // nsIChannel
    645 //-----------------------------------------------------------------------------
    646 
    647 NS_IMETHODIMP
    648 nsJARChannel::GetOriginalURI(nsIURI** aURI) {
    649  *aURI = mOriginalURI;
    650  NS_ADDREF(*aURI);
    651  return NS_OK;
    652 }
    653 
    654 NS_IMETHODIMP
    655 nsJARChannel::SetOriginalURI(nsIURI* aURI) {
    656  NS_ENSURE_ARG_POINTER(aURI);
    657  mOriginalURI = aURI;
    658  return NS_OK;
    659 }
    660 
    661 NS_IMETHODIMP
    662 nsJARChannel::GetURI(nsIURI** aURI) {
    663  NS_IF_ADDREF(*aURI = mJarURI);
    664 
    665  return NS_OK;
    666 }
    667 
    668 NS_IMETHODIMP
    669 nsJARChannel::GetOwner(nsISupports** aOwner) {
    670  // JAR signatures are not processed to avoid main-thread network I/O (bug
    671  // 726125)
    672  *aOwner = mOwner;
    673  NS_IF_ADDREF(*aOwner);
    674  return NS_OK;
    675 }
    676 
    677 NS_IMETHODIMP
    678 nsJARChannel::SetOwner(nsISupports* aOwner) {
    679  mOwner = aOwner;
    680  return NS_OK;
    681 }
    682 
    683 NS_IMETHODIMP
    684 nsJARChannel::GetLoadInfo(nsILoadInfo** aLoadInfo) {
    685  NS_IF_ADDREF(*aLoadInfo = mLoadInfo);
    686  return NS_OK;
    687 }
    688 
    689 NS_IMETHODIMP
    690 nsJARChannel::SetLoadInfo(nsILoadInfo* aLoadInfo) {
    691  MOZ_RELEASE_ASSERT(aLoadInfo, "loadinfo can't be null");
    692  mLoadInfo = aLoadInfo;
    693  return NS_OK;
    694 }
    695 
    696 NS_IMETHODIMP
    697 nsJARChannel::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) {
    698  NS_IF_ADDREF(*aCallbacks = mCallbacks);
    699  return NS_OK;
    700 }
    701 
    702 NS_IMETHODIMP
    703 nsJARChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) {
    704  mCallbacks = aCallbacks;
    705  return NS_OK;
    706 }
    707 
    708 NS_IMETHODIMP
    709 nsJARChannel::GetSecurityInfo(nsITransportSecurityInfo** aSecurityInfo) {
    710  MOZ_ASSERT(aSecurityInfo, "Null out param");
    711  *aSecurityInfo = nullptr;
    712  return NS_OK;
    713 }
    714 
    715 bool nsJARChannel::GetContentTypeGuess(nsACString& aResult) const {
    716  const char *ext = nullptr, *fileName = mJarEntry.get();
    717  int32_t len = mJarEntry.Length();
    718 
    719  // check if we're displaying a directory
    720  // mJarEntry will be empty if we're trying to display
    721  // the topmost directory in a zip, e.g. jar:foo.zip!/
    722  if (ENTRY_IS_DIRECTORY(mJarEntry)) {
    723    aResult.AssignLiteral(APPLICATION_HTTP_INDEX_FORMAT);
    724    return true;
    725  }
    726 
    727  // Not a directory, take a guess by its extension
    728  for (int32_t i = len - 1; i >= 0; i--) {
    729    if (fileName[i] == '.') {
    730      ext = &fileName[i + 1];
    731      break;
    732    }
    733  }
    734  if (!ext) {
    735    return false;
    736  }
    737  nsIMIMEService* mimeServ = gJarHandler->MimeService();
    738  if (!mimeServ) {
    739    return false;
    740  }
    741  mimeServ->GetTypeFromExtension(nsDependentCString(ext), aResult);
    742  return !aResult.IsEmpty();
    743 }
    744 
    745 NS_IMETHODIMP
    746 nsJARChannel::GetContentType(nsACString& aResult) {
    747  // If the Jar file has not been open yet,
    748  // We return application/x-unknown-content-type
    749  if (!mOpened) {
    750    aResult.AssignLiteral(UNKNOWN_CONTENT_TYPE);
    751    return NS_OK;
    752  }
    753 
    754  aResult = mContentType;
    755  return NS_OK;
    756 }
    757 
    758 NS_IMETHODIMP
    759 nsJARChannel::SetContentType(const nsACString& aContentType) {
    760  // We behave like HTTP channels (treat this as a hint if called before open,
    761  // and override the charset if called after open).
    762  // mContentCharset is unchanged if not parsed
    763  NS_ParseResponseContentType(aContentType, mContentType, mContentCharset);
    764  return NS_OK;
    765 }
    766 
    767 NS_IMETHODIMP
    768 nsJARChannel::GetContentCharset(nsACString& aContentCharset) {
    769  // If someone gives us a charset hint we should just use that charset.
    770  // So we don't care when this is being called.
    771  aContentCharset = mContentCharset;
    772  if (mContentCharset.IsEmpty() && (mOriginalURI->SchemeIs("chrome") ||
    773                                    mOriginalURI->SchemeIs("resource"))) {
    774    aContentCharset.AssignLiteral("UTF-8");
    775  }
    776  return NS_OK;
    777 }
    778 
    779 NS_IMETHODIMP
    780 nsJARChannel::SetContentCharset(const nsACString& aContentCharset) {
    781  mContentCharset = aContentCharset;
    782  return NS_OK;
    783 }
    784 
    785 NS_IMETHODIMP
    786 nsJARChannel::GetContentDisposition(uint32_t* aContentDisposition) {
    787  return NS_ERROR_NOT_AVAILABLE;
    788 }
    789 
    790 NS_IMETHODIMP
    791 nsJARChannel::SetContentDisposition(uint32_t aContentDisposition) {
    792  return NS_ERROR_NOT_AVAILABLE;
    793 }
    794 
    795 NS_IMETHODIMP
    796 nsJARChannel::GetContentDispositionFilename(
    797    nsAString& aContentDispositionFilename) {
    798  return NS_ERROR_NOT_AVAILABLE;
    799 }
    800 
    801 NS_IMETHODIMP
    802 nsJARChannel::SetContentDispositionFilename(
    803    const nsAString& aContentDispositionFilename) {
    804  return NS_ERROR_NOT_AVAILABLE;
    805 }
    806 
    807 NS_IMETHODIMP
    808 nsJARChannel::GetContentDispositionHeader(
    809    nsACString& aContentDispositionHeader) {
    810  return NS_ERROR_NOT_AVAILABLE;
    811 }
    812 
    813 NS_IMETHODIMP
    814 nsJARChannel::GetContentLength(int64_t* result) {
    815  *result = mContentLength;
    816  return NS_OK;
    817 }
    818 
    819 NS_IMETHODIMP
    820 nsJARChannel::SetContentLength(int64_t aContentLength) {
    821  // XXX does this really make any sense at all?
    822  mContentLength = aContentLength;
    823  return NS_OK;
    824 }
    825 
    826 static void RecordZeroLengthEvent(bool aIsSync, const nsCString& aSpec,
    827                                  nsresult aStatus, bool aCanceled,
    828                                  const nsCString& aCanceledReason,
    829                                  nsILoadInfo* aLoadInfo) {
    830  if (!StaticPrefs::network_jar_record_failure_reason()) {
    831    return;
    832  }
    833 
    834  // If the BrowsingContext performing the load has already been discarded, and
    835  // we're getting a zero-length event due to the channel being canceled, this
    836  // event isn't interesting for YSOD analysis, so can be skipped.
    837  if (RefPtr<mozilla::dom::BrowsingContext> targetBC =
    838          aLoadInfo->GetTargetBrowsingContext()) {
    839    if (targetBC->IsDiscarded() && aCanceled) {
    840      return;
    841    }
    842  }
    843 
    844  // The Legacy Telemetry event can only hold 80 characters.
    845  // We only save the file name and path inside the jar.
    846  auto findFilenameStart = [](const nsCString& aSpec) -> uint32_t {
    847    int32_t pos = aSpec.Find("!/");
    848    if (pos == kNotFound) {
    849      MOZ_ASSERT(false, "This should not happen");
    850      return 0;
    851    }
    852    int32_t from = aSpec.RFindChar('/', pos);
    853    if (from == kNotFound) {
    854      MOZ_ASSERT(false, "This should not happen");
    855      return 0;
    856    }
    857    // Skip over the slash
    858    from++;
    859    return from;
    860  };
    861 
    862  // If for some reason we are unable to extract the filename we report the
    863  // entire string, or 80 characters of it, to make sure we don't miss any
    864  // events.
    865  uint32_t from = findFilenameStart(aSpec);
    866  const auto fileName = Substring(aSpec, from);
    867 
    868  nsAutoCString errorCString;
    869  mozilla::GetErrorName(aStatus, errorCString);
    870 
    871  // To test this telemetry we use a zip file and we want to make
    872  // sure don't filter it out.
    873  bool isTest = fileName.Find("test_empty_file.zip!") != -1;
    874  bool isOmniJa = StringBeginsWith(fileName, "omni.ja!"_ns);
    875 
    876  if (StringEndsWith(fileName, ".ftl"_ns)) {
    877    // FTL uses I/O to test for file presence, so we get
    878    // a high volume of events from it, but it is not erronous.
    879    // Also, Fluent is resilient to empty loads, so even if any
    880    // of the errors are real errors, they don't cause YSOD.
    881    // We can investigate them separately.
    882    if (!isTest && aStatus == NS_ERROR_FILE_NOT_FOUND) {
    883      return;
    884    }
    885 
    886    glean::zero_byte_load::LoadFtlExtra extra = {
    887        .cancelReason = Some(aCanceledReason),
    888        .cancelled = Some(aCanceled),
    889        .fileName = Some(fileName),
    890        .status = Some(errorCString),
    891        .sync = Some(aIsSync),
    892    };
    893    glean::zero_byte_load::load_ftl.Record(Some(extra));
    894  } else if (StringEndsWith(fileName, ".dtd"_ns)) {
    895    // We're going to skip reporting telemetry on res DTDs.
    896    // See Bug 1693711 for investigation into those empty loads.
    897    if (!isTest && StringBeginsWith(fileName, "omni.ja!/res/dtd"_ns)) {
    898      return;
    899    }
    900 
    901    glean::zero_byte_load::LoadDtdExtra extra = {
    902        .cancelReason = Some(aCanceledReason),
    903        .cancelled = Some(aCanceled),
    904        .fileName = Some(fileName),
    905        .status = Some(errorCString),
    906        .sync = Some(aIsSync),
    907    };
    908    glean::zero_byte_load::load_dtd.Record(Some(extra));
    909  } else if (StringEndsWith(fileName, ".properties"_ns)) {
    910    glean::zero_byte_load::LoadPropertiesExtra extra = {
    911        .cancelReason = Some(aCanceledReason),
    912        .cancelled = Some(aCanceled),
    913        .fileName = Some(fileName),
    914        .status = Some(errorCString),
    915        .sync = Some(aIsSync),
    916    };
    917    glean::zero_byte_load::load_properties.Record(Some(extra));
    918  } else if (StringEndsWith(fileName, ".js"_ns) ||
    919             StringEndsWith(fileName, ".jsm"_ns) ||
    920             StringEndsWith(fileName, ".mjs"_ns)) {
    921    // We're going to skip reporting telemetry on JS loads
    922    // coming not from omni.ja.
    923    // See Bug 1693711 for investigation into those empty loads.
    924    if (!isTest && !isOmniJa) {
    925      return;
    926    }
    927 
    928    glean::zero_byte_load::LoadJsExtra extra = {
    929        .cancelReason = Some(aCanceledReason),
    930        .cancelled = Some(aCanceled),
    931        .fileName = Some(fileName),
    932        .status = Some(errorCString),
    933        .sync = Some(aIsSync),
    934    };
    935    glean::zero_byte_load::load_js.Record(Some(extra));
    936  } else if (StringEndsWith(fileName, ".xml"_ns)) {
    937    glean::zero_byte_load::LoadXmlExtra extra = {
    938        .cancelReason = Some(aCanceledReason),
    939        .cancelled = Some(aCanceled),
    940        .fileName = Some(fileName),
    941        .status = Some(errorCString),
    942        .sync = Some(aIsSync),
    943    };
    944    glean::zero_byte_load::load_xml.Record(Some(extra));
    945  } else if (StringEndsWith(fileName, ".xhtml"_ns)) {
    946    // This error seems to be very common and is not strongly
    947    // correlated to YSOD.
    948    if (aStatus == NS_ERROR_PARSED_DATA_CACHED) {
    949      return;
    950    }
    951 
    952    // We're not investigating YSODs from extensions for now.
    953    if (!isOmniJa) {
    954      return;
    955    }
    956 
    957    glean::zero_byte_load::LoadXhtmlExtra extra = {
    958        .cancelReason = Some(aCanceledReason),
    959        .cancelled = Some(aCanceled),
    960        .fileName = Some(fileName),
    961        .status = Some(errorCString),
    962        .sync = Some(aIsSync),
    963    };
    964    glean::zero_byte_load::load_xhtml.Record(Some(extra));
    965  } else if (StringEndsWith(fileName, ".css"_ns)) {
    966    // Bug 1702937: Filter out svg+'css'+'png'/NS_BINDING_ABORTED combo.
    967    if (aStatus == NS_BINDING_ABORTED) {
    968      return;
    969    }
    970 
    971    // Bug 1702937: Filter css/NS_ERROR_CORRUPTED_CONTENT that is coming from
    972    // outside of omni.ja.
    973    if (!isOmniJa && aStatus == NS_ERROR_CORRUPTED_CONTENT) {
    974      return;
    975    }
    976 
    977    glean::zero_byte_load::LoadCssExtra extra = {
    978        .cancelReason = Some(aCanceledReason),
    979        .cancelled = Some(aCanceled),
    980        .fileName = Some(fileName),
    981        .status = Some(errorCString),
    982        .sync = Some(aIsSync),
    983    };
    984    glean::zero_byte_load::load_css.Record(Some(extra));
    985  } else if (StringEndsWith(fileName, ".json"_ns)) {
    986    // FTL uses I/O to test for file presence, so we get
    987    // a high volume of events from it, but it is not erronous.
    988    // Also, Fluent is resilient to empty loads, so even if any
    989    // of the errors are real errors, they don't cause YSOD.
    990    // We can investigate them separately.
    991    if (!isTest && aStatus == NS_ERROR_FILE_NOT_FOUND) {
    992      return;
    993    }
    994 
    995    glean::zero_byte_load::LoadJsonExtra extra = {
    996        .cancelReason = Some(aCanceledReason),
    997        .cancelled = Some(aCanceled),
    998        .fileName = Some(fileName),
    999        .status = Some(errorCString),
   1000        .sync = Some(aIsSync),
   1001    };
   1002    glean::zero_byte_load::load_json.Record(Some(extra));
   1003  } else if (StringEndsWith(fileName, ".html"_ns)) {
   1004    // See bug 1695560. Filter out non-omni.ja HTML.
   1005    if (!isOmniJa) {
   1006      return;
   1007    }
   1008 
   1009    // See bug 1695560. "activity-stream-noscripts.html" with NS_ERROR_FAILURE
   1010    // is filtered out.
   1011    if (fileName.EqualsLiteral("omni.ja!/chrome/browser/res/newtab/"
   1012                               "prerendered/activity-stream-noscripts.html") &&
   1013        aStatus == NS_ERROR_FAILURE) {
   1014      return;
   1015    }
   1016 
   1017    glean::zero_byte_load::LoadHtmlExtra extra = {
   1018        .cancelReason = Some(aCanceledReason),
   1019        .cancelled = Some(aCanceled),
   1020        .fileName = Some(fileName),
   1021        .status = Some(errorCString),
   1022        .sync = Some(aIsSync),
   1023    };
   1024    glean::zero_byte_load::load_html.Record(Some(extra));
   1025  } else if (StringEndsWith(fileName, ".png"_ns)) {
   1026    // See bug 1695560.
   1027    // Bug 1702937: Filter out svg+'css'+'png'/NS_BINDING_ABORTED combo.
   1028    if (!isOmniJa || aStatus == NS_BINDING_ABORTED) {
   1029      return;
   1030    }
   1031 
   1032    glean::zero_byte_load::LoadPngExtra extra = {
   1033        .cancelReason = Some(aCanceledReason),
   1034        .cancelled = Some(aCanceled),
   1035        .fileName = Some(fileName),
   1036        .status = Some(errorCString),
   1037        .sync = Some(aIsSync),
   1038    };
   1039    glean::zero_byte_load::load_png.Record(Some(extra));
   1040  } else if (StringEndsWith(fileName, ".svg"_ns)) {
   1041    // See bug 1695560.
   1042    // Bug 1702937: Filter out svg+'css'+'png'/NS_BINDING_ABORTED combo.
   1043    if (!isOmniJa || aStatus == NS_BINDING_ABORTED) {
   1044      return;
   1045    }
   1046    glean::zero_byte_load::LoadSvgExtra extra = {
   1047        .cancelReason = Some(aCanceledReason),
   1048        .cancelled = Some(aCanceled),
   1049        .fileName = Some(fileName),
   1050        .status = Some(errorCString),
   1051        .sync = Some(aIsSync),
   1052    };
   1053    glean::zero_byte_load::load_svg.Record(Some(extra));
   1054  } else {  // All others
   1055    // We're going to, for now, filter out `other` category.
   1056    // See Bug 1693711 for investigation into those empty loads.
   1057    // Bug 1702937: Filter other/*.ico/NS_BINDING_ABORTED.
   1058    if (!isTest && (!isOmniJa || (aStatus == NS_BINDING_ABORTED &&
   1059                                  StringEndsWith(fileName, ".ico"_ns)))) {
   1060      return;
   1061    }
   1062 
   1063    // See bug 1695560. "search-extensions/google/favicon.ico" with
   1064    // NS_BINDING_ABORTED is filtered out.
   1065    if (fileName.EqualsLiteral(
   1066            "omni.ja!/chrome/browser/search-extensions/google/favicon.ico") &&
   1067        aStatus == NS_BINDING_ABORTED) {
   1068      return;
   1069    }
   1070 
   1071    glean::zero_byte_load::LoadOthersExtra extra = {
   1072        .cancelReason = Some(aCanceledReason),
   1073        .cancelled = Some(aCanceled),
   1074        .fileName = Some(fileName),
   1075        .status = Some(errorCString),
   1076        .sync = Some(aIsSync),
   1077    };
   1078    glean::zero_byte_load::load_others.Record(Some(extra));
   1079  }
   1080 }
   1081 
   1082 NS_IMETHODIMP
   1083 nsJARChannel::Open(nsIInputStream** aStream) {
   1084  LOG(("nsJARChannel::Open [this=%p]\n", this));
   1085  nsCOMPtr<nsIStreamListener> listener;
   1086  nsresult rv =
   1087      nsContentSecurityManager::doContentSecurityCheck(this, listener);
   1088  NS_ENSURE_SUCCESS(rv, rv);
   1089 
   1090  auto recordEvent = MakeScopeExit([&] {
   1091    if (mContentLength <= 0 || NS_FAILED(rv)) {
   1092      RecordZeroLengthEvent(true, mSpec, rv, mCanceled, mCanceledReason,
   1093                            mLoadInfo);
   1094    }
   1095  });
   1096 
   1097  LOG(("nsJARChannel::Open [this=%p]\n", this));
   1098 
   1099  NS_ENSURE_TRUE(!mOpened, NS_ERROR_IN_PROGRESS);
   1100  NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
   1101 
   1102  mJarFile = nullptr;
   1103 
   1104  rv = LookupFile();
   1105  if (NS_FAILED(rv)) return rv;
   1106 
   1107  // If mJarFile was not set by LookupFile, we can't open a channel.
   1108  if (!mJarFile) {
   1109    MOZ_ASSERT_UNREACHABLE("only file-backed jars are supported");
   1110    return NS_ERROR_NOT_IMPLEMENTED;
   1111  }
   1112 
   1113  RefPtr<nsJARInputThunk> input;
   1114  rv = CreateJarInput(gJarHandler->JarCache(), getter_AddRefs(input));
   1115  if (NS_FAILED(rv)) return rv;
   1116 
   1117  input.forget(aStream);
   1118  SetOpened();
   1119 
   1120  return NS_OK;
   1121 }
   1122 
   1123 void nsJARChannel::SetOpened() {
   1124  MOZ_ASSERT(!mOpened, "Opening channel twice?");
   1125  mOpened = true;
   1126  // Compute the content type now.
   1127  if (!GetContentTypeGuess(mContentType)) {
   1128    mContentType.Assign(UNKNOWN_CONTENT_TYPE);
   1129  }
   1130 }
   1131 
   1132 NS_IMETHODIMP
   1133 nsJARChannel::AsyncOpen(nsIStreamListener* aListener) {
   1134  LOG(("nsJARChannel::AsyncOpen [this=%p]\n", this));
   1135  nsCOMPtr<nsIStreamListener> listener = aListener;
   1136  nsresult rv =
   1137      nsContentSecurityManager::doContentSecurityCheck(this, listener);
   1138  if (NS_FAILED(rv)) {
   1139    mIsPending = false;
   1140    mListener = nullptr;
   1141    mCallbacks = nullptr;
   1142    mProgressSink = nullptr;
   1143    return rv;
   1144  }
   1145 
   1146  LOG(("nsJARChannel::AsyncOpen [this=%p]\n", this));
   1147  MOZ_ASSERT(
   1148      mLoadInfo->GetSecurityMode() == 0 ||
   1149          mLoadInfo->GetInitialSecurityCheckDone() ||
   1150          (mLoadInfo->GetSecurityMode() ==
   1151               nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL &&
   1152           mLoadInfo->GetLoadingPrincipal() &&
   1153           mLoadInfo->GetLoadingPrincipal()->IsSystemPrincipal()),
   1154      "security flags in loadInfo but doContentSecurityCheck() not called");
   1155 
   1156  NS_ENSURE_ARG_POINTER(listener);
   1157  NS_ENSURE_TRUE(!mOpened, NS_ERROR_IN_PROGRESS);
   1158  NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
   1159 
   1160  mJarFile = nullptr;
   1161 
   1162  // Initialize mProgressSink
   1163  NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup, mProgressSink);
   1164 
   1165  mListener = listener;
   1166  mIsPending = true;
   1167 
   1168  rv = LookupFile();
   1169  if (NS_FAILED(rv) || !mJarFile) {
   1170    // Not a local file...
   1171    mIsPending = false;
   1172    mListener = nullptr;
   1173    mCallbacks = nullptr;
   1174    mProgressSink = nullptr;
   1175    return mJarFile ? rv : NS_ERROR_UNSAFE_CONTENT_TYPE;
   1176  }
   1177 
   1178  rv = OpenLocalFile();
   1179  if (NS_FAILED(rv)) {
   1180    mIsPending = false;
   1181    mListener = nullptr;
   1182    mCallbacks = nullptr;
   1183    mProgressSink = nullptr;
   1184    return rv;
   1185  }
   1186 
   1187  return NS_OK;
   1188 }
   1189 
   1190 //-----------------------------------------------------------------------------
   1191 // nsIJARChannel
   1192 //-----------------------------------------------------------------------------
   1193 NS_IMETHODIMP
   1194 nsJARChannel::GetJarFile(nsIFile** aFile) {
   1195  NS_IF_ADDREF(*aFile = mJarFile);
   1196  return NS_OK;
   1197 }
   1198 
   1199 NS_IMETHODIMP
   1200 nsJARChannel::SetJarFile(nsIFile* aFile) {
   1201  if (mOpened) {
   1202    return NS_ERROR_IN_PROGRESS;
   1203  }
   1204  mJarFileOverride = aFile;
   1205  return NS_OK;
   1206 }
   1207 
   1208 NS_IMETHODIMP
   1209 nsJARChannel::EnsureCached(bool* aIsCached) {
   1210  nsresult rv;
   1211  *aIsCached = false;
   1212 
   1213  if (mOpened) {
   1214    return NS_ERROR_ALREADY_OPENED;
   1215  }
   1216 
   1217  if (mPreCachedJarReader) {
   1218    // We've already been called and found the JAR is cached
   1219    *aIsCached = true;
   1220    return NS_OK;
   1221  }
   1222 
   1223  nsCOMPtr<nsIURI> innerFileURI;
   1224  rv = mJarURI->GetJARFile(getter_AddRefs(innerFileURI));
   1225  NS_ENSURE_SUCCESS(rv, rv);
   1226 
   1227  nsCOMPtr<nsIFileURL> innerFileURL = do_QueryInterface(innerFileURI, &rv);
   1228  NS_ENSURE_SUCCESS(rv, rv);
   1229 
   1230  nsCOMPtr<nsIFile> jarFile;
   1231  rv = innerFileURL->GetFile(getter_AddRefs(jarFile));
   1232  NS_ENSURE_SUCCESS(rv, rv);
   1233 
   1234  nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
   1235  NS_ENSURE_SUCCESS(rv, rv);
   1236 
   1237  nsCOMPtr<nsIProtocolHandler> handler;
   1238  rv = ioService->GetProtocolHandler("jar", getter_AddRefs(handler));
   1239  NS_ENSURE_SUCCESS(rv, rv);
   1240 
   1241  auto jarHandler = static_cast<nsJARProtocolHandler*>(handler.get());
   1242  MOZ_ASSERT(jarHandler);
   1243 
   1244  nsIZipReaderCache* jarCache = jarHandler->JarCache();
   1245 
   1246  rv = jarCache->GetZipIfCached(jarFile, getter_AddRefs(mPreCachedJarReader));
   1247  if (rv == NS_ERROR_CACHE_KEY_NOT_FOUND) {
   1248    return NS_OK;
   1249  }
   1250  NS_ENSURE_SUCCESS(rv, rv);
   1251 
   1252  *aIsCached = true;
   1253  return NS_OK;
   1254 }
   1255 
   1256 NS_IMETHODIMP
   1257 nsJARChannel::GetZipEntry(nsIZipEntry** aZipEntry) {
   1258  nsresult rv = LookupFile();
   1259  if (NS_FAILED(rv)) return rv;
   1260 
   1261  if (!mJarFile) return NS_ERROR_NOT_AVAILABLE;
   1262 
   1263  nsCOMPtr<nsIZipReader> reader;
   1264  rv = gJarHandler->JarCache()->GetZip(mJarFile, getter_AddRefs(reader));
   1265  if (NS_FAILED(rv)) return rv;
   1266 
   1267  return reader->GetEntry(mJarEntry, aZipEntry);
   1268 }
   1269 
   1270 //-----------------------------------------------------------------------------
   1271 // nsIStreamListener
   1272 //-----------------------------------------------------------------------------
   1273 
   1274 NS_IMETHODIMP
   1275 nsJARChannel::OnStartRequest(nsIRequest* req) {
   1276  LOG(("nsJARChannel::OnStartRequest [this=%p %s]\n", this, mSpec.get()));
   1277 
   1278  mRequest = req;
   1279  nsresult rv = mListener->OnStartRequest(this);
   1280  if (NS_FAILED(rv)) {
   1281    return rv;
   1282  }
   1283 
   1284  // Restrict loadable content types.
   1285  nsAutoCString contentType;
   1286  GetContentType(contentType);
   1287  auto contentPolicyType = mLoadInfo->GetExternalContentPolicyType();
   1288  if (contentType.Equals(APPLICATION_HTTP_INDEX_FORMAT) &&
   1289      contentPolicyType != ExtContentPolicy::TYPE_DOCUMENT &&
   1290      contentPolicyType != ExtContentPolicy::TYPE_FETCH) {
   1291    return NS_ERROR_CORRUPTED_CONTENT;
   1292  }
   1293  if (contentPolicyType == ExtContentPolicy::TYPE_STYLESHEET &&
   1294      !contentType.EqualsLiteral(TEXT_CSS)) {
   1295    return NS_ERROR_CORRUPTED_CONTENT;
   1296  }
   1297  if (contentPolicyType == ExtContentPolicy::TYPE_SCRIPT &&
   1298      !nsContentUtils::IsJavascriptMIMEType(
   1299          NS_ConvertUTF8toUTF16(contentType))) {
   1300    return NS_ERROR_CORRUPTED_CONTENT;
   1301  }
   1302 
   1303  return rv;
   1304 }
   1305 
   1306 NS_IMETHODIMP
   1307 nsJARChannel::OnStopRequest(nsIRequest* req, nsresult status) {
   1308  LOG(("nsJARChannel::OnStopRequest [this=%p %s status=%" PRIx32 "]\n", this,
   1309       mSpec.get(), static_cast<uint32_t>(status)));
   1310 
   1311  if (NS_SUCCEEDED(mStatus)) mStatus = status;
   1312 
   1313  if (mListener) {
   1314    if (!mOnDataCalled || NS_FAILED(status)) {
   1315      RecordZeroLengthEvent(false, mSpec, status, mCanceled, mCanceledReason,
   1316                            mLoadInfo);
   1317    }
   1318 
   1319    mListener->OnStopRequest(this, status);
   1320    mListener = nullptr;
   1321  }
   1322 
   1323  if (mLoadGroup) mLoadGroup->RemoveRequest(this, nullptr, status);
   1324 
   1325  mRequest = nullptr;
   1326  mPump = nullptr;
   1327  mIsPending = false;
   1328 
   1329  // Drop notification callbacks to prevent cycles.
   1330  mCallbacks = nullptr;
   1331  mProgressSink = nullptr;
   1332 
   1333 #if defined(XP_WIN) || defined(MOZ_WIDGET_COCOA)
   1334 #else
   1335  // To deallocate file descriptor by RemoteOpenFileChild destructor.
   1336  mJarFile = nullptr;
   1337 #endif
   1338 
   1339  return NS_OK;
   1340 }
   1341 
   1342 NS_IMETHODIMP
   1343 nsJARChannel::OnDataAvailable(nsIRequest* req, nsIInputStream* stream,
   1344                              uint64_t offset, uint32_t count) {
   1345  LOG(("nsJARChannel::OnDataAvailable [this=%p %s]\n", this, mSpec.get()));
   1346 
   1347  nsresult rv;
   1348 
   1349  // don't send out OnDataAvailable notifications if we've been canceled.
   1350  if (mCanceled) {
   1351    return mStatus;
   1352  }
   1353 
   1354  mOnDataCalled = true;
   1355  rv = mListener->OnDataAvailable(this, stream, offset, count);
   1356 
   1357  // simply report progress here instead of hooking ourselves up as a
   1358  // nsITransportEventSink implementation.
   1359  // XXX do the 64-bit stuff for real
   1360  if (mProgressSink && NS_SUCCEEDED(rv)) {
   1361    if (NS_IsMainThread()) {
   1362      FireOnProgress(offset + count);
   1363    } else {
   1364      NS_DispatchToMainThread(NewRunnableMethod<uint64_t>(
   1365          "nsJARChannel::FireOnProgress", this, &nsJARChannel::FireOnProgress,
   1366          offset + count));
   1367    }
   1368  }
   1369 
   1370  return rv;  // let the pump cancel on failure
   1371 }
   1372 
   1373 NS_IMETHODIMP
   1374 nsJARChannel::RetargetDeliveryTo(nsISerialEventTarget* aEventTarget) {
   1375  MOZ_ASSERT(NS_IsMainThread());
   1376 
   1377  nsCOMPtr<nsIThreadRetargetableRequest> request = do_QueryInterface(mRequest);
   1378  if (!request) {
   1379    return NS_ERROR_NO_INTERFACE;
   1380  }
   1381 
   1382  return request->RetargetDeliveryTo(aEventTarget);
   1383 }
   1384 
   1385 NS_IMETHODIMP
   1386 nsJARChannel::GetDeliveryTarget(nsISerialEventTarget** aEventTarget) {
   1387  MOZ_ASSERT(NS_IsMainThread());
   1388 
   1389  nsCOMPtr<nsIThreadRetargetableRequest> request = do_QueryInterface(mRequest);
   1390  if (!request) {
   1391    return NS_ERROR_NO_INTERFACE;
   1392  }
   1393 
   1394  return request->GetDeliveryTarget(aEventTarget);
   1395 }
   1396 
   1397 NS_IMETHODIMP
   1398 nsJARChannel::CheckListenerChain() {
   1399  MOZ_ASSERT(NS_IsMainThread());
   1400 
   1401  nsCOMPtr<nsIThreadRetargetableStreamListener> listener =
   1402      do_QueryInterface(mListener);
   1403  if (!listener) {
   1404    return NS_ERROR_NO_INTERFACE;
   1405  }
   1406 
   1407  return listener->CheckListenerChain();
   1408 }
   1409 
   1410 NS_IMETHODIMP
   1411 nsJARChannel::OnDataFinished(nsresult aStatus) {
   1412  nsCOMPtr<nsIThreadRetargetableStreamListener> listener =
   1413      do_QueryInterface(mListener);
   1414  if (listener) {
   1415    return listener->OnDataFinished(aStatus);
   1416  }
   1417 
   1418  return NS_OK;
   1419 }