tor-browser

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

nsMultiMixedConv.cpp (31997B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* This Source Code Form is subject to the terms of the Mozilla Public
      3 * License, v. 2.0. If a copy of the MPL was not distributed with this
      4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      5 
      6 #include "nsMultiMixedConv.h"
      7 #include "nsIHttpChannel.h"
      8 #include "nsIThreadRetargetableStreamListener.h"
      9 #include "nsNetCID.h"
     10 #include "nsMimeTypes.h"
     11 #include "nsIStringStream.h"
     12 #include "nsCRT.h"
     13 #include "nsIHttpChannelInternal.h"
     14 #include "nsURLHelper.h"
     15 #include "nsIStreamConverterService.h"
     16 #include "nsContentSecurityManager.h"
     17 #include "nsHttp.h"
     18 #include "nsNetUtil.h"
     19 #include "nsIURI.h"
     20 #include "nsHttpHeaderArray.h"
     21 #include "mozilla/AutoRestore.h"
     22 #include "mozilla/Components.h"
     23 #include "mozilla/Tokenizer.h"
     24 #include "nsComponentManagerUtils.h"
     25 #include "mozilla/StaticPrefs_network.h"
     26 
     27 using namespace mozilla;
     28 
     29 nsPartChannel::nsPartChannel(nsIChannel* aMultipartChannel, uint32_t aPartID,
     30                             bool aIsFirstPart, nsIStreamListener* aListener)
     31    : mMultipartChannel(aMultipartChannel),
     32      mListener(aListener),
     33      mPartID(aPartID),
     34      mIsFirstPart(aIsFirstPart) {
     35  // Inherit the load flags from the original channel...
     36  mMultipartChannel->GetLoadFlags(&mLoadFlags);
     37 
     38  mMultipartChannel->GetLoadGroup(getter_AddRefs(mLoadGroup));
     39 }
     40 
     41 void nsPartChannel::InitializeByteRange(int64_t aStart, int64_t aEnd) {
     42  mIsByteRangeRequest = true;
     43 
     44  mByteRangeStart = aStart;
     45  mByteRangeEnd = aEnd;
     46 }
     47 
     48 nsresult nsPartChannel::SendOnStartRequest(nsISupports* aContext) {
     49  return mListener->OnStartRequest(this);
     50 }
     51 
     52 nsresult nsPartChannel::SendOnDataAvailable(nsISupports* aContext,
     53                                            nsIInputStream* aStream,
     54                                            uint64_t aOffset, uint32_t aLen) {
     55  return mListener->OnDataAvailable(this, aStream, aOffset, aLen);
     56 }
     57 
     58 nsresult nsPartChannel::SendOnStopRequest(nsISupports* aContext,
     59                                          nsresult aStatus) {
     60  // Drop the listener
     61  nsCOMPtr<nsIStreamListener> listener;
     62  listener.swap(mListener);
     63  return listener->OnStopRequest(this, aStatus);
     64 }
     65 
     66 void nsPartChannel::SetContentDisposition(
     67    const nsACString& aContentDispositionHeader) {
     68  mContentDispositionHeader = aContentDispositionHeader;
     69  nsCOMPtr<nsIURI> uri;
     70  GetURI(getter_AddRefs(uri));
     71  NS_GetFilenameFromDisposition(mContentDispositionFilename,
     72                                mContentDispositionHeader);
     73  mContentDisposition =
     74      NS_GetContentDispositionFromHeader(mContentDispositionHeader, this);
     75 }
     76 
     77 //
     78 // nsISupports implementation...
     79 //
     80 
     81 NS_IMPL_ADDREF(nsPartChannel)
     82 NS_IMPL_RELEASE(nsPartChannel)
     83 
     84 NS_INTERFACE_MAP_BEGIN(nsPartChannel)
     85  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIChannel)
     86  NS_INTERFACE_MAP_ENTRY(nsIRequest)
     87  NS_INTERFACE_MAP_ENTRY(nsIChannel)
     88  NS_INTERFACE_MAP_ENTRY(nsIByteRangeRequest)
     89  NS_INTERFACE_MAP_ENTRY(nsIMultiPartChannel)
     90 NS_INTERFACE_MAP_END
     91 
     92 //
     93 // nsIRequest implementation...
     94 //
     95 
     96 NS_IMETHODIMP
     97 nsPartChannel::GetName(nsACString& aResult) {
     98  return mMultipartChannel->GetName(aResult);
     99 }
    100 
    101 NS_IMETHODIMP
    102 nsPartChannel::IsPending(bool* aResult) {
    103  // For now, consider the active lifetime of each part the same as
    104  // the underlying multipart channel...  This is not exactly right,
    105  // but it is good enough :-)
    106  return mMultipartChannel->IsPending(aResult);
    107 }
    108 
    109 NS_IMETHODIMP
    110 nsPartChannel::GetStatus(nsresult* aResult) {
    111  nsresult rv = NS_OK;
    112 
    113  if (NS_FAILED(mStatus)) {
    114    *aResult = mStatus;
    115  } else {
    116    rv = mMultipartChannel->GetStatus(aResult);
    117  }
    118 
    119  return rv;
    120 }
    121 
    122 NS_IMETHODIMP nsPartChannel::SetCanceledReason(const nsACString& aReason) {
    123  return SetCanceledReasonImpl(aReason);
    124 }
    125 
    126 NS_IMETHODIMP nsPartChannel::GetCanceledReason(nsACString& aReason) {
    127  return GetCanceledReasonImpl(aReason);
    128 }
    129 
    130 NS_IMETHODIMP nsPartChannel::CancelWithReason(nsresult aStatus,
    131                                              const nsACString& aReason) {
    132  return CancelWithReasonImpl(aStatus, aReason);
    133 }
    134 
    135 NS_IMETHODIMP
    136 nsPartChannel::Cancel(nsresult aStatus) {
    137  // Cancelling an individual part must not cancel the underlying
    138  // multipart channel...
    139  // XXX but we should stop sending data for _this_ part channel!
    140  mStatus = aStatus;
    141  return NS_OK;
    142 }
    143 
    144 NS_IMETHODIMP
    145 nsPartChannel::GetCanceled(bool* aCanceled) {
    146  *aCanceled = NS_FAILED(mStatus);
    147  return NS_OK;
    148 }
    149 
    150 NS_IMETHODIMP
    151 nsPartChannel::Suspend(void) {
    152  // Suspending an individual part must not suspend the underlying
    153  // multipart channel...
    154  // XXX why not?
    155  return NS_OK;
    156 }
    157 
    158 NS_IMETHODIMP
    159 nsPartChannel::Resume(void) {
    160  // Resuming an individual part must not resume the underlying
    161  // multipart channel...
    162  // XXX why not?
    163  return NS_OK;
    164 }
    165 
    166 //
    167 // nsIChannel implementation
    168 //
    169 
    170 NS_IMETHODIMP
    171 nsPartChannel::GetOriginalURI(nsIURI** aURI) {
    172  return mMultipartChannel->GetOriginalURI(aURI);
    173 }
    174 
    175 NS_IMETHODIMP
    176 nsPartChannel::SetOriginalURI(nsIURI* aURI) {
    177  return mMultipartChannel->SetOriginalURI(aURI);
    178 }
    179 
    180 NS_IMETHODIMP
    181 nsPartChannel::GetURI(nsIURI** aURI) { return mMultipartChannel->GetURI(aURI); }
    182 
    183 NS_IMETHODIMP
    184 nsPartChannel::Open(nsIInputStream** aStream) {
    185  nsCOMPtr<nsIStreamListener> listener;
    186  nsresult rv =
    187      nsContentSecurityManager::doContentSecurityCheck(this, listener);
    188  NS_ENSURE_SUCCESS(rv, rv);
    189 
    190  // This channel cannot be opened!
    191  return NS_ERROR_FAILURE;
    192 }
    193 
    194 NS_IMETHODIMP
    195 nsPartChannel::AsyncOpen(nsIStreamListener* aListener) {
    196  nsCOMPtr<nsIStreamListener> listener = aListener;
    197  nsresult rv =
    198      nsContentSecurityManager::doContentSecurityCheck(this, listener);
    199  NS_ENSURE_SUCCESS(rv, rv);
    200 
    201  // This channel cannot be opened!
    202  return NS_ERROR_FAILURE;
    203 }
    204 
    205 NS_IMETHODIMP
    206 nsPartChannel::GetLoadFlags(nsLoadFlags* aLoadFlags) {
    207  *aLoadFlags = mLoadFlags;
    208  return NS_OK;
    209 }
    210 
    211 NS_IMETHODIMP
    212 nsPartChannel::SetLoadFlags(nsLoadFlags aLoadFlags) {
    213  mLoadFlags = aLoadFlags;
    214  return NS_OK;
    215 }
    216 
    217 NS_IMETHODIMP
    218 nsPartChannel::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
    219  return GetTRRModeImpl(aTRRMode);
    220 }
    221 
    222 NS_IMETHODIMP
    223 nsPartChannel::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
    224  return SetTRRModeImpl(aTRRMode);
    225 }
    226 
    227 NS_IMETHODIMP
    228 nsPartChannel::GetIsDocument(bool* aIsDocument) {
    229  return NS_GetIsDocumentChannel(this, aIsDocument);
    230 }
    231 
    232 NS_IMETHODIMP
    233 nsPartChannel::GetLoadGroup(nsILoadGroup** aLoadGroup) {
    234  *aLoadGroup = do_AddRef(mLoadGroup).take();
    235  return NS_OK;
    236 }
    237 
    238 NS_IMETHODIMP
    239 nsPartChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) {
    240  mLoadGroup = aLoadGroup;
    241 
    242  return NS_OK;
    243 }
    244 
    245 NS_IMETHODIMP
    246 nsPartChannel::GetOwner(nsISupports** aOwner) {
    247  return mMultipartChannel->GetOwner(aOwner);
    248 }
    249 
    250 NS_IMETHODIMP
    251 nsPartChannel::SetOwner(nsISupports* aOwner) {
    252  return mMultipartChannel->SetOwner(aOwner);
    253 }
    254 
    255 NS_IMETHODIMP
    256 nsPartChannel::GetLoadInfo(nsILoadInfo** aLoadInfo) {
    257  return mMultipartChannel->GetLoadInfo(aLoadInfo);
    258 }
    259 
    260 NS_IMETHODIMP
    261 nsPartChannel::SetLoadInfo(nsILoadInfo* aLoadInfo) {
    262  MOZ_RELEASE_ASSERT(aLoadInfo, "loadinfo can't be null");
    263  return mMultipartChannel->SetLoadInfo(aLoadInfo);
    264 }
    265 
    266 NS_IMETHODIMP
    267 nsPartChannel::GetNotificationCallbacks(nsIInterfaceRequestor** aCallbacks) {
    268  return mMultipartChannel->GetNotificationCallbacks(aCallbacks);
    269 }
    270 
    271 NS_IMETHODIMP
    272 nsPartChannel::SetNotificationCallbacks(nsIInterfaceRequestor* aCallbacks) {
    273  return mMultipartChannel->SetNotificationCallbacks(aCallbacks);
    274 }
    275 
    276 NS_IMETHODIMP
    277 nsPartChannel::GetSecurityInfo(nsITransportSecurityInfo** aSecurityInfo) {
    278  return mMultipartChannel->GetSecurityInfo(aSecurityInfo);
    279 }
    280 
    281 NS_IMETHODIMP
    282 nsPartChannel::GetContentType(nsACString& aContentType) {
    283  aContentType = mContentType;
    284  return NS_OK;
    285 }
    286 
    287 NS_IMETHODIMP
    288 nsPartChannel::SetContentType(const nsACString& aContentType) {
    289  bool dummy;
    290  net_ParseContentType(aContentType, mContentType, mContentCharset, &dummy);
    291  return NS_OK;
    292 }
    293 
    294 NS_IMETHODIMP
    295 nsPartChannel::GetContentCharset(nsACString& aContentCharset) {
    296  aContentCharset = mContentCharset;
    297  return NS_OK;
    298 }
    299 
    300 NS_IMETHODIMP
    301 nsPartChannel::SetContentCharset(const nsACString& aContentCharset) {
    302  mContentCharset = aContentCharset;
    303  return NS_OK;
    304 }
    305 
    306 NS_IMETHODIMP
    307 nsPartChannel::GetContentLength(int64_t* aContentLength) {
    308  *aContentLength = mContentLength;
    309  return NS_OK;
    310 }
    311 
    312 NS_IMETHODIMP
    313 nsPartChannel::SetContentLength(int64_t aContentLength) {
    314  mContentLength = aContentLength;
    315  return NS_OK;
    316 }
    317 
    318 NS_IMETHODIMP
    319 nsPartChannel::GetContentDisposition(uint32_t* aContentDisposition) {
    320  if (mContentDispositionHeader.IsEmpty()) return NS_ERROR_NOT_AVAILABLE;
    321 
    322  *aContentDisposition = mContentDisposition;
    323  return NS_OK;
    324 }
    325 
    326 NS_IMETHODIMP
    327 nsPartChannel::SetContentDisposition(uint32_t aContentDisposition) {
    328  return NS_ERROR_NOT_AVAILABLE;
    329 }
    330 
    331 NS_IMETHODIMP
    332 nsPartChannel::GetContentDispositionFilename(
    333    nsAString& aContentDispositionFilename) {
    334  if (mContentDispositionFilename.IsEmpty()) return NS_ERROR_NOT_AVAILABLE;
    335 
    336  aContentDispositionFilename = mContentDispositionFilename;
    337  return NS_OK;
    338 }
    339 
    340 NS_IMETHODIMP
    341 nsPartChannel::SetContentDispositionFilename(
    342    const nsAString& aContentDispositionFilename) {
    343  return NS_ERROR_NOT_AVAILABLE;
    344 }
    345 
    346 NS_IMETHODIMP
    347 nsPartChannel::GetContentDispositionHeader(
    348    nsACString& aContentDispositionHeader) {
    349  if (mContentDispositionHeader.IsEmpty()) return NS_ERROR_NOT_AVAILABLE;
    350 
    351  aContentDispositionHeader = mContentDispositionHeader;
    352  return NS_OK;
    353 }
    354 
    355 NS_IMETHODIMP
    356 nsPartChannel::GetPartID(uint32_t* aPartID) {
    357  *aPartID = mPartID;
    358  return NS_OK;
    359 }
    360 
    361 NS_IMETHODIMP
    362 nsPartChannel::GetIsFirstPart(bool* aIsFirstPart) {
    363  *aIsFirstPart = mIsFirstPart;
    364  return NS_OK;
    365 }
    366 
    367 NS_IMETHODIMP
    368 nsPartChannel::GetIsLastPart(bool* aIsLastPart) {
    369  *aIsLastPart = mIsLastPart;
    370  return NS_OK;
    371 }
    372 
    373 //
    374 // nsIByteRangeRequest implementation...
    375 //
    376 
    377 NS_IMETHODIMP
    378 nsPartChannel::GetIsByteRangeRequest(bool* aIsByteRangeRequest) {
    379  *aIsByteRangeRequest = mIsByteRangeRequest;
    380 
    381  return NS_OK;
    382 }
    383 
    384 NS_IMETHODIMP
    385 nsPartChannel::GetStartRange(int64_t* aStartRange) {
    386  *aStartRange = mByteRangeStart;
    387 
    388  return NS_OK;
    389 }
    390 
    391 NS_IMETHODIMP
    392 nsPartChannel::GetEndRange(int64_t* aEndRange) {
    393  *aEndRange = mByteRangeEnd;
    394  return NS_OK;
    395 }
    396 
    397 NS_IMETHODIMP
    398 nsPartChannel::GetBaseChannel(nsIChannel** aReturn) {
    399  NS_ENSURE_ARG_POINTER(aReturn);
    400 
    401  *aReturn = do_AddRef(mMultipartChannel).take();
    402  return NS_OK;
    403 }
    404 
    405 // nsISupports implementation
    406 NS_IMPL_ISUPPORTS(nsMultiMixedConv, nsIStreamConverter, nsIStreamListener,
    407                  nsIThreadRetargetableStreamListener, nsIRequestObserver)
    408 
    409 // nsIStreamConverter implementation
    410 
    411 // No syncronous conversion at this time.
    412 NS_IMETHODIMP
    413 nsMultiMixedConv::Convert(nsIInputStream* aFromStream, const char* aFromType,
    414                          const char* aToType, nsISupports* aCtxt,
    415                          nsIInputStream** _retval) {
    416  return NS_ERROR_NOT_IMPLEMENTED;
    417 }
    418 
    419 // Stream converter service calls this to initialize the actual stream converter
    420 // (us).
    421 NS_IMETHODIMP
    422 nsMultiMixedConv::AsyncConvertData(const char* aFromType, const char* aToType,
    423                                   nsIStreamListener* aListener,
    424                                   nsISupports* aCtxt) {
    425  NS_ASSERTION(aListener && aFromType && aToType,
    426               "null pointer passed into multi mixed converter");
    427 
    428  // hook up our final listener. this guy gets the various On*() calls we want
    429  // to throw at him.
    430  //
    431  // WARNING: this listener must be able to handle multiple OnStartRequest,
    432  // OnDataAvail() and OnStopRequest() call combinations. We call of series
    433  // of these for each sub-part in the raw stream.
    434  mFinalListener = aListener;
    435 
    436  return NS_OK;
    437 }
    438 
    439 NS_IMETHODIMP
    440 nsMultiMixedConv::GetConvertedType(const nsACString& aFromType,
    441                                   nsIChannel* aChannel, nsACString& aToType) {
    442  return NS_ERROR_NOT_IMPLEMENTED;
    443 }
    444 
    445 NS_IMETHODIMP
    446 nsMultiMixedConv::MaybeRetarget(nsIRequest* request) {
    447  return NS_ERROR_NOT_IMPLEMENTED;
    448 }
    449 
    450 // nsIRequestObserver implementation
    451 NS_IMETHODIMP
    452 nsMultiMixedConv::OnStartRequest(nsIRequest* request) {
    453  // we're assuming the content-type is available at this stage
    454  NS_ASSERTION(mBoundary.IsEmpty(), "a second on start???");
    455 
    456  nsresult rv;
    457 
    458  mTotalSent = 0;
    459  mChannel = do_QueryInterface(request, &rv);
    460  if (NS_FAILED(rv)) return rv;
    461 
    462  nsAutoCString contentType;
    463 
    464  // ask the HTTP channel for the content-type and extract the boundary from it.
    465  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel, &rv);
    466  if (NS_SUCCEEDED(rv)) {
    467    rv = httpChannel->GetResponseHeader("content-type"_ns, contentType);
    468    if (NS_FAILED(rv)) {
    469      return rv;
    470    }
    471    nsCString csp;
    472    rv = httpChannel->GetResponseHeader("content-security-policy"_ns, csp);
    473    if (NS_SUCCEEDED(rv)) {
    474      mRootContentSecurityPolicy = csp;
    475    }
    476    nsCString contentDisposition;
    477    rv = httpChannel->GetResponseHeader("content-disposition"_ns,
    478                                        contentDisposition);
    479    if (NS_SUCCEEDED(rv)) {
    480      mRootContentDisposition = contentDisposition;
    481    }
    482  } else {
    483    // try asking the channel directly
    484    rv = mChannel->GetContentType(contentType);
    485    if (NS_FAILED(rv)) {
    486      return NS_ERROR_FAILURE;
    487    }
    488  }
    489 
    490  Tokenizer p(contentType);
    491  p.SkipUntil(Token::Char(';'));
    492  if (!p.CheckChar(';')) {
    493    return NS_ERROR_CORRUPTED_CONTENT;
    494  }
    495  p.SkipWhites();
    496  if (!p.CheckWord("boundary")) {
    497    return NS_ERROR_CORRUPTED_CONTENT;
    498  }
    499  p.SkipWhites();
    500  if (!p.CheckChar('=')) {
    501    return NS_ERROR_CORRUPTED_CONTENT;
    502  }
    503  p.SkipWhites();
    504  (void)p.ReadUntil(Token::Char(';'), mBoundary);
    505  mBoundary.Trim(
    506      " \"");  // ignoring potential quoted string formatting violations
    507  if (mBoundary.IsEmpty()) {
    508    return NS_ERROR_CORRUPTED_CONTENT;
    509  }
    510 
    511  mHeaderTokens[HEADER_CONTENT_TYPE] = mTokenizer.AddCustomToken(
    512      "content-type", mTokenizer.CASE_INSENSITIVE, false);
    513  mHeaderTokens[HEADER_CONTENT_LENGTH] = mTokenizer.AddCustomToken(
    514      "content-length", mTokenizer.CASE_INSENSITIVE, false);
    515  mHeaderTokens[HEADER_CONTENT_DISPOSITION] = mTokenizer.AddCustomToken(
    516      "content-disposition", mTokenizer.CASE_INSENSITIVE, false);
    517  mHeaderTokens[HEADER_SET_COOKIE] = mTokenizer.AddCustomToken(
    518      "set-cookie", mTokenizer.CASE_INSENSITIVE, false);
    519  mHeaderTokens[HEADER_CONTENT_RANGE] = mTokenizer.AddCustomToken(
    520      "content-range", mTokenizer.CASE_INSENSITIVE, false);
    521  mHeaderTokens[HEADER_RANGE] =
    522      mTokenizer.AddCustomToken("range", mTokenizer.CASE_INSENSITIVE, false);
    523  mHeaderTokens[HEADER_CONTENT_SECURITY_POLICY] = mTokenizer.AddCustomToken(
    524      "content-security-policy", mTokenizer.CASE_INSENSITIVE, false);
    525 
    526  mLFToken = mTokenizer.AddCustomToken("\n", mTokenizer.CASE_SENSITIVE, false);
    527  mCRLFToken =
    528      mTokenizer.AddCustomToken("\r\n", mTokenizer.CASE_SENSITIVE, false);
    529 
    530  SwitchToControlParsing();
    531 
    532  mBoundaryToken =
    533      mTokenizer.AddCustomToken(mBoundary, mTokenizer.CASE_SENSITIVE);
    534  mBoundaryTokenWithDashes =
    535      mTokenizer.AddCustomToken("--"_ns + mBoundary, mTokenizer.CASE_SENSITIVE);
    536 
    537  return NS_OK;
    538 }
    539 
    540 // nsIStreamListener implementation
    541 NS_IMETHODIMP
    542 nsMultiMixedConv::OnDataAvailable(nsIRequest* request, nsIInputStream* inStr,
    543                                  uint64_t sourceOffset, uint32_t count) {
    544  // Failing these assertions may indicate that some of the target listeners of
    545  // this converter is looping the thead queue, which is harmful to how we
    546  // collect the raw (content) data.
    547  MOZ_DIAGNOSTIC_ASSERT(!mInOnDataAvailable,
    548                        "nsMultiMixedConv::OnDataAvailable reentered!");
    549  MOZ_DIAGNOSTIC_ASSERT(
    550      !mRawData, "There are unsent data from the previous tokenizer feed!");
    551 
    552  if (mInOnDataAvailable) {
    553    // The multipart logic is incapable of being reentered.
    554    return NS_ERROR_UNEXPECTED;
    555  }
    556 
    557  mozilla::AutoRestore<bool> restore(mInOnDataAvailable);
    558  mInOnDataAvailable = true;
    559 
    560  nsresult rv_feed = mTokenizer.FeedInput(inStr, count);
    561  // We must do this every time.  Regardless if something has failed during the
    562  // parsing process.  Otherwise the raw data reference would not be thrown
    563  // away.
    564  nsresult rv_send = SendData();
    565 
    566  return NS_FAILED(rv_send) ? rv_send : rv_feed;
    567 }
    568 
    569 NS_IMETHODIMP
    570 nsMultiMixedConv::OnDataFinished(nsresult aStatus) { return NS_OK; }
    571 
    572 NS_IMETHODIMP
    573 nsMultiMixedConv::CheckListenerChain() { return NS_ERROR_NOT_IMPLEMENTED; }
    574 
    575 NS_IMETHODIMP
    576 nsMultiMixedConv::OnStopRequest(nsIRequest* request, nsresult aStatus) {
    577  nsresult rv;
    578 
    579  if (mPartChannel) {
    580    mPartChannel->SetIsLastPart();
    581 
    582    MOZ_DIAGNOSTIC_ASSERT(
    583        !mRawData, "There are unsent data from the previous tokenizer feed!");
    584 
    585    rv = mTokenizer.FinishInput();
    586    if (NS_SUCCEEDED(aStatus)) {
    587      aStatus = rv;
    588    }
    589    rv = SendData();
    590    if (NS_SUCCEEDED(aStatus)) {
    591      aStatus = rv;
    592    }
    593 
    594    (void)SendStop(aStatus);
    595  } else if (NS_FAILED(aStatus) && !mRequestListenerNotified) {
    596    // underlying data production problem. we should not be in
    597    // the middle of sending data. if we were, mPartChannel,
    598    // above, would have been non-null.
    599 
    600    (void)mFinalListener->OnStartRequest(request);
    601    (void)mFinalListener->OnStopRequest(request, aStatus);
    602  }
    603 
    604  nsCOMPtr<nsIMultiPartChannelListener> multiListener =
    605      do_QueryInterface(mFinalListener);
    606  if (multiListener) {
    607    multiListener->OnAfterLastPart(aStatus);
    608  }
    609 
    610  return NS_OK;
    611 }
    612 
    613 nsresult nsMultiMixedConv::ConsumeToken(Token const& token) {
    614  nsresult rv;
    615 
    616  switch (mParserState) {
    617    case PREAMBLE:
    618      if (token.Equals(mBoundaryTokenWithDashes)) {
    619        // The server first used boundary '--boundary'.  Hence, we no longer
    620        // accept plain 'boundary' token as a delimiter.
    621        mTokenizer.RemoveCustomToken(mBoundaryToken);
    622        mParserState = BOUNDARY_CRLF;
    623        break;
    624      }
    625      if (token.Equals(mBoundaryToken)) {
    626        // And here the opposite from the just above block...
    627        mTokenizer.RemoveCustomToken(mBoundaryTokenWithDashes);
    628        mParserState = BOUNDARY_CRLF;
    629        break;
    630      }
    631 
    632      // This is a preamble, just ignore it and wait for the boundary.
    633      break;
    634 
    635    case BOUNDARY_CRLF:
    636      if (token.Equals(Token::NewLine())) {
    637        mParserState = HEADER_NAME;
    638        mResponseHeader = HEADER_UNKNOWN;
    639        HeadersToDefault();
    640        SetHeaderTokensEnabled(true);
    641        break;
    642      }
    643      return NS_ERROR_CORRUPTED_CONTENT;
    644 
    645    case HEADER_NAME:
    646      SetHeaderTokensEnabled(false);
    647      if (token.Equals(Token::NewLine())) {
    648        mParserState = BODY_INIT;
    649        SwitchToBodyParsing();
    650        break;
    651      }
    652      for (uint32_t h = HEADER_CONTENT_TYPE; h < HEADER_UNKNOWN; ++h) {
    653        if (token.Equals(mHeaderTokens[h])) {
    654          mResponseHeader = static_cast<EHeader>(h);
    655          break;
    656        }
    657      }
    658      mParserState = HEADER_SEP;
    659      break;
    660 
    661    case HEADER_SEP:
    662      if (token.Equals(Token::Char(':'))) {
    663        mParserState = HEADER_VALUE;
    664        mResponseHeaderValue.Truncate();
    665        break;
    666      }
    667      if (mResponseHeader == HEADER_UNKNOWN) {
    668        // If the header is not of any we understand, just pass everything till
    669        // ':'
    670        break;
    671      }
    672      if (token.Equals(Token::Whitespace())) {
    673        // Accept only header-name traling whitespaces after known headers
    674        break;
    675      }
    676      return NS_ERROR_CORRUPTED_CONTENT;
    677 
    678    case HEADER_VALUE:
    679      if (token.Equals(Token::Whitespace()) && mResponseHeaderValue.IsEmpty()) {
    680        // Eat leading whitespaces
    681        break;
    682      }
    683      if (token.Equals(Token::NewLine())) {
    684        nsresult rv = ProcessHeader();
    685        if (NS_FAILED(rv)) {
    686          return rv;
    687        }
    688        mParserState = HEADER_NAME;
    689        mResponseHeader = HEADER_UNKNOWN;
    690        SetHeaderTokensEnabled(true);
    691      } else {
    692        mResponseHeaderValue.Append(token.Fragment());
    693      }
    694      break;
    695 
    696    case BODY_INIT:
    697      rv = SendStart();
    698      if (NS_FAILED(rv)) {
    699        return rv;
    700      }
    701      mParserState = BODY;
    702      [[fallthrough]];
    703 
    704    case BODY: {
    705      if (!token.Equals(mLFToken) && !token.Equals(mCRLFToken)) {
    706        if (token.Equals(mBoundaryTokenWithDashes) ||
    707            token.Equals(mBoundaryToken)) {
    708          // Allow CRLF to NOT be part of the boundary as well
    709          SwitchToControlParsing();
    710          mParserState = TRAIL_DASH1;
    711          break;
    712        }
    713        AccumulateData(token);
    714        break;
    715      }
    716 
    717      // After CRLF we must explicitly check for boundary.  If found,
    718      // that CRLF is part of the boundary and must not be send to the
    719      // data listener.
    720      Token token2;
    721      if (!mTokenizer.Next(token2)) {
    722        // Note: this will give us the CRLF token again when more data
    723        // or OnStopRequest arrive.  I.e. we will enter BODY case in
    724        // the very same state as we are now and start this block over.
    725        mTokenizer.NeedMoreInput();
    726        break;
    727      }
    728      if (token2.Equals(mBoundaryTokenWithDashes) ||
    729          token2.Equals(mBoundaryToken)) {
    730        SwitchToControlParsing();
    731        mParserState = TRAIL_DASH1;
    732        break;
    733      }
    734 
    735      AccumulateData(token);
    736      AccumulateData(token2);
    737      break;
    738    }
    739 
    740    case TRAIL_DASH1:
    741      if (token.Equals(Token::NewLine())) {
    742        rv = SendStop(NS_OK);
    743        if (NS_FAILED(rv)) {
    744          return rv;
    745        }
    746        mParserState = BOUNDARY_CRLF;
    747        mTokenizer.Rollback();
    748        break;
    749      }
    750      if (token.Equals(Token::Char('-'))) {
    751        mParserState = TRAIL_DASH2;
    752        break;
    753      }
    754      return NS_ERROR_CORRUPTED_CONTENT;
    755 
    756    case TRAIL_DASH2:
    757      if (token.Equals(Token::Char('-'))) {
    758        mPartChannel->SetIsLastPart();
    759        // SendStop calls SendData first.
    760        rv = SendStop(NS_OK);
    761        if (NS_FAILED(rv)) {
    762          return rv;
    763        }
    764        mParserState = EPILOGUE;
    765        break;
    766      }
    767      return NS_ERROR_CORRUPTED_CONTENT;
    768 
    769    case EPILOGUE:
    770      // Just ignore
    771      break;
    772 
    773    default:
    774      MOZ_ASSERT(false, "Missing parser state handling branch");
    775      break;
    776  }  // switch
    777 
    778  return NS_OK;
    779 }
    780 
    781 void nsMultiMixedConv::SetHeaderTokensEnabled(bool aEnable) {
    782  for (uint32_t h = HEADER_FIRST; h < HEADER_UNKNOWN; ++h) {
    783    mTokenizer.EnableCustomToken(mHeaderTokens[h], aEnable);
    784  }
    785 }
    786 
    787 void nsMultiMixedConv::SwitchToBodyParsing() {
    788  mTokenizer.SetTokenizingMode(Tokenizer::Mode::CUSTOM_ONLY);
    789  mTokenizer.EnableCustomToken(mLFToken, true);
    790  mTokenizer.EnableCustomToken(mCRLFToken, true);
    791  mTokenizer.EnableCustomToken(mBoundaryTokenWithDashes, true);
    792  mTokenizer.EnableCustomToken(mBoundaryToken, true);
    793 }
    794 
    795 void nsMultiMixedConv::SwitchToControlParsing() {
    796  mTokenizer.SetTokenizingMode(Tokenizer::Mode::FULL);
    797  mTokenizer.EnableCustomToken(mLFToken, false);
    798  mTokenizer.EnableCustomToken(mCRLFToken, false);
    799  mTokenizer.EnableCustomToken(mBoundaryTokenWithDashes, false);
    800  mTokenizer.EnableCustomToken(mBoundaryToken, false);
    801 }
    802 
    803 // nsMultiMixedConv methods
    804 nsMultiMixedConv::nsMultiMixedConv()
    805    // XXX: This is a hack to bypass the raw pointer to refcounted object in
    806    // lambda analysis. It should be removed and replaced when the
    807    // IncrementalTokenizer API is improved to avoid the need for such
    808    // workarounds.
    809    //
    810    // This is safe because `mTokenizer` will not outlive `this`, meaning
    811    // that this std::bind object will be destroyed before `this` dies.
    812    : mTokenizer(std::bind(&nsMultiMixedConv::ConsumeToken, this,
    813                           std::placeholders::_1)) {}
    814 
    815 nsresult nsMultiMixedConv::SendStart() {
    816  nsresult rv = NS_OK;
    817 
    818  nsCOMPtr<nsIStreamListener> partListener(mFinalListener);
    819  if (mContentType.IsEmpty()) {
    820    mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE);
    821    nsCOMPtr<nsIStreamConverterService> serv;
    822    serv = mozilla::components::StreamConverter::Service(&rv);
    823    if (NS_SUCCEEDED(rv)) {
    824      nsCOMPtr<nsIStreamListener> converter;
    825      rv = serv->AsyncConvertData(UNKNOWN_CONTENT_TYPE, "*/*", mFinalListener,
    826                                  mContext, getter_AddRefs(converter));
    827      if (NS_SUCCEEDED(rv)) {
    828        partListener = converter;
    829      }
    830    }
    831  }
    832 
    833  // if we already have an mPartChannel, that means we never sent a Stop()
    834  // before starting up another "part." that would be bad.
    835  MOZ_ASSERT(!mPartChannel, "tisk tisk, shouldn't be overwriting a channel");
    836 
    837  nsPartChannel* newChannel;
    838  newChannel = new nsPartChannel(mChannel, mCurrentPartID, mCurrentPartID == 0,
    839                                 partListener);
    840 
    841  ++mCurrentPartID;
    842 
    843  if (mIsByteRangeRequest) {
    844    newChannel->InitializeByteRange(mByteRangeStart, mByteRangeEnd);
    845  }
    846 
    847  mTotalSent = 0;
    848 
    849  // Set up the new part channel...
    850  mPartChannel = newChannel;
    851 
    852  rv = mPartChannel->SetContentType(mContentType);
    853  if (NS_FAILED(rv)) return rv;
    854 
    855  rv = mPartChannel->SetContentLength(mContentLength);
    856  if (NS_FAILED(rv)) return rv;
    857 
    858  if (!mRootContentDisposition.IsEmpty()) {
    859    mPartChannel->SetContentDisposition(mRootContentDisposition);
    860  } else {
    861    mPartChannel->SetContentDisposition(mContentDisposition);
    862  }
    863 
    864  // Each part of a multipart/replace response can be used
    865  // for the top level document.  We must inform upper layers
    866  // about this by setting the LOAD_REPLACE flag so that certain
    867  // state assertions are evaluated as positive.
    868  nsLoadFlags loadFlags = 0;
    869  mPartChannel->GetLoadFlags(&loadFlags);
    870  loadFlags |= nsIChannel::LOAD_REPLACE;
    871  mPartChannel->SetLoadFlags(loadFlags);
    872 
    873  nsCOMPtr<nsILoadGroup> loadGroup;
    874  (void)mPartChannel->GetLoadGroup(getter_AddRefs(loadGroup));
    875 
    876  // Add the new channel to the load group (if any)
    877  if (loadGroup) {
    878    rv = loadGroup->AddRequest(mPartChannel, nullptr);
    879    if (NS_FAILED(rv)) return rv;
    880  }
    881 
    882  // This prevents artificial call to OnStart/StopRequest when the root
    883  // channel fails.  Since now it's ensured to keep with the nsIStreamListener
    884  // contract every time.
    885  mRequestListenerNotified = true;
    886 
    887  // Let's start off the load. NOTE: we don't forward on the channel passed
    888  // into our OnDataAvailable() as it's the root channel for the raw stream.
    889  return mPartChannel->SendOnStartRequest(mContext);
    890 }
    891 
    892 nsresult nsMultiMixedConv::SendStop(nsresult aStatus) {
    893  // Make sure we send out all accumulcated data prior call to OnStopRequest.
    894  // If there is no data, this is a no-op.
    895  nsresult rv = SendData();
    896  if (NS_SUCCEEDED(aStatus)) {
    897    aStatus = rv;
    898  }
    899  if (mPartChannel) {
    900    rv = mPartChannel->SendOnStopRequest(mContext, aStatus);
    901    // don't check for failure here, we need to remove the channel from
    902    // the loadgroup.
    903 
    904    // Remove the channel from its load group (if any)
    905    nsCOMPtr<nsILoadGroup> loadGroup;
    906    (void)mPartChannel->GetLoadGroup(getter_AddRefs(loadGroup));
    907    if (loadGroup) {
    908      (void)loadGroup->RemoveRequest(mPartChannel, mContext, aStatus);
    909    }
    910  }
    911 
    912  mPartChannel = nullptr;
    913  return rv;
    914 }
    915 
    916 void nsMultiMixedConv::AccumulateData(Token const& aToken) {
    917  if (!mRawData) {
    918    // This is the first read of raw data during this FeedInput loop
    919    // of the incremental tokenizer.  All 'raw' tokens are coming from
    920    // the same linear buffer, hence begining of this loop raw data
    921    // is begining of the first raw token.  Length of this loop raw
    922    // data is just sum of all 'raw' tokens we collect during this loop.
    923    //
    924    // It's ensured we flush (send to to the listener via OnDataAvailable)
    925    // and nullify the collected raw data right after FeedInput call.
    926    // Hence, the reference can't outlive the actual buffer.
    927    mRawData = aToken.Fragment().BeginReading();
    928    mRawDataLength = 0;
    929  }
    930 
    931  mRawDataLength += aToken.Fragment().Length();
    932 }
    933 
    934 nsresult nsMultiMixedConv::SendData() {
    935  nsresult rv;
    936 
    937  if (!mRawData) {
    938    return NS_OK;
    939  }
    940 
    941  nsACString::const_char_iterator rawData = mRawData;
    942  mRawData = nullptr;
    943 
    944  if (!mPartChannel) {
    945    return NS_ERROR_FAILURE;  // something went wrong w/ processing
    946  }
    947 
    948  if (mContentLength != UINT64_MAX) {
    949    // make sure that we don't send more than the mContentLength
    950    // XXX why? perhaps the Content-Length header was actually wrong!!
    951    if ((uint64_t(mRawDataLength) + mTotalSent) > mContentLength) {
    952      mRawDataLength = static_cast<uint32_t>(mContentLength - mTotalSent);
    953    }
    954 
    955    if (mRawDataLength == 0) return NS_OK;
    956  }
    957 
    958  uint64_t offset = mTotalSent;
    959  mTotalSent += mRawDataLength;
    960 
    961  nsCOMPtr<nsIStringInputStream> ss(
    962      do_CreateInstance("@mozilla.org/io/string-input-stream;1", &rv));
    963  if (NS_FAILED(rv)) return rv;
    964 
    965  rv = ss->ShareData(rawData, mRawDataLength);
    966  mRawData = nullptr;
    967  if (NS_FAILED(rv)) return rv;
    968 
    969  return mPartChannel->SendOnDataAvailable(mContext, ss, offset,
    970                                           mRawDataLength);
    971 }
    972 
    973 void nsMultiMixedConv::HeadersToDefault() {
    974  mContentLength = UINT64_MAX;
    975  mContentType.Truncate();
    976  mContentDisposition.Truncate();
    977  mContentSecurityPolicy.Truncate();
    978  mIsByteRangeRequest = false;
    979 }
    980 
    981 nsresult nsMultiMixedConv::ProcessHeader() {
    982  mozilla::Tokenizer p(mResponseHeaderValue);
    983 
    984  switch (mResponseHeader) {
    985    case HEADER_CONTENT_TYPE:
    986      mContentType = mResponseHeaderValue;
    987      mContentType.CompressWhitespace();
    988      break;
    989    case HEADER_CONTENT_LENGTH:
    990      p.SkipWhites();
    991      if (!p.ReadInteger(&mContentLength)) {
    992        return NS_ERROR_CORRUPTED_CONTENT;
    993      }
    994      break;
    995    case HEADER_CONTENT_DISPOSITION:
    996      mContentDisposition = mResponseHeaderValue;
    997      mContentDisposition.CompressWhitespace();
    998      break;
    999    case HEADER_SET_COOKIE: {
   1000      nsCOMPtr<nsIHttpChannelInternal> httpInternal =
   1001          do_QueryInterface(mChannel);
   1002      mResponseHeaderValue.CompressWhitespace();
   1003      if (!StaticPrefs::network_cookie_prevent_set_cookie_from_multipart() &&
   1004          httpInternal) {
   1005        AutoTArray<nsCString, 1> cookieHeaderArray;
   1006        cookieHeaderArray.AppendElement(mResponseHeaderValue);
   1007        DebugOnly<nsresult> rv =
   1008            httpInternal->SetCookieHeaders(cookieHeaderArray);
   1009        MOZ_ASSERT(NS_SUCCEEDED(rv));
   1010      }
   1011      break;
   1012    }
   1013    case HEADER_RANGE:
   1014    case HEADER_CONTENT_RANGE: {
   1015      if (!p.CheckWord("bytes") || !p.CheckWhite()) {
   1016        return NS_ERROR_CORRUPTED_CONTENT;
   1017      }
   1018      p.SkipWhites();
   1019      if (p.CheckChar('*')) {
   1020        mByteRangeStart = mByteRangeEnd = 0;
   1021      } else if (!p.ReadInteger(&mByteRangeStart) || !p.CheckChar('-') ||
   1022                 !p.ReadInteger(&mByteRangeEnd)) {
   1023        return NS_ERROR_CORRUPTED_CONTENT;
   1024      }
   1025      mIsByteRangeRequest = true;
   1026      if (mContentLength == UINT64_MAX) {
   1027        mContentLength = uint64_t(mByteRangeEnd - mByteRangeStart + 1);
   1028      }
   1029      break;
   1030    }
   1031    case HEADER_CONTENT_SECURITY_POLICY: {
   1032      mContentSecurityPolicy = mResponseHeaderValue;
   1033      mContentSecurityPolicy.CompressWhitespace();
   1034      nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
   1035      if (httpChannel) {
   1036        nsCString resultCSP = mRootContentSecurityPolicy;
   1037        if (!mContentSecurityPolicy.IsEmpty()) {
   1038          // We are updating the root channel CSP header respectively for
   1039          // each part as: CSP-root + CSP-partN, where N is the part number.
   1040          // Here we append current part's CSP to root CSP and reset CSP
   1041          // header for each part.
   1042          if (!resultCSP.IsEmpty()) {
   1043            resultCSP.Append(";");
   1044          }
   1045          resultCSP.Append(mContentSecurityPolicy);
   1046        }
   1047        nsresult rv = httpChannel->SetResponseHeader(
   1048            "Content-Security-Policy"_ns, resultCSP, false);
   1049        if (NS_FAILED(rv)) {
   1050          return NS_ERROR_CORRUPTED_CONTENT;
   1051        }
   1052      }
   1053      break;
   1054    }
   1055    case HEADER_UNKNOWN:
   1056      // We ignore anything else...
   1057      break;
   1058  }
   1059 
   1060  return NS_OK;
   1061 }
   1062 
   1063 nsresult NS_NewMultiMixedConv(nsMultiMixedConv** aMultiMixedConv) {
   1064  MOZ_ASSERT(aMultiMixedConv != nullptr, "null ptr");
   1065 
   1066  RefPtr<nsMultiMixedConv> conv = new nsMultiMixedConv();
   1067  conv.forget(aMultiMixedConv);
   1068  return NS_OK;
   1069 }