tor-browser

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

nsIncrementalStreamLoader.cpp (6258B)


      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 "nsIncrementalStreamLoader.h"
      7 #include "nsIInputStream.h"
      8 #include "nsIChannel.h"
      9 #include "nsError.h"
     10 #include "mozilla/ProfilerLabels.h"
     11 
     12 #include <limits>
     13 
     14 nsIncrementalStreamLoader::nsIncrementalStreamLoader() = default;
     15 
     16 NS_IMETHODIMP
     17 nsIncrementalStreamLoader::Init(nsIIncrementalStreamLoaderObserver* observer) {
     18  NS_ENSURE_ARG_POINTER(observer);
     19  mObserver = observer;
     20  return NS_OK;
     21 }
     22 
     23 nsresult nsIncrementalStreamLoader::Create(REFNSIID aIID, void** aResult) {
     24  RefPtr<nsIncrementalStreamLoader> it = new nsIncrementalStreamLoader();
     25  return it->QueryInterface(aIID, aResult);
     26 }
     27 
     28 NS_IMPL_ISUPPORTS(nsIncrementalStreamLoader, nsIIncrementalStreamLoader,
     29                  nsIRequestObserver, nsIStreamListener,
     30                  nsIThreadRetargetableStreamListener)
     31 
     32 NS_IMETHODIMP
     33 nsIncrementalStreamLoader::GetNumBytesRead(uint32_t* aNumBytes) {
     34  *aNumBytes = mBytesRead;
     35  return NS_OK;
     36 }
     37 
     38 /* readonly attribute nsIRequest request; */
     39 NS_IMETHODIMP
     40 nsIncrementalStreamLoader::GetRequest(nsIRequest** aRequest) {
     41  *aRequest = do_AddRef(mRequest).take();
     42  return NS_OK;
     43 }
     44 
     45 NS_IMETHODIMP
     46 nsIncrementalStreamLoader::OnStartRequest(nsIRequest* request) {
     47  nsresult rv = mObserver->OnStartRequest(request);
     48  if (NS_FAILED(rv)) {
     49    return rv;
     50  }
     51 
     52  nsCOMPtr<nsIChannel> chan(do_QueryInterface(request));
     53  if (chan) {
     54    int64_t contentLength = -1;
     55    chan->GetContentLength(&contentLength);
     56    if (contentLength >= 0) {
     57      // On 64bit platforms size of uint64_t coincides with the size of size_t,
     58      // so we want to compare with the minimum from size_t and int64_t.
     59      if (static_cast<uint64_t>(contentLength) >
     60          std::min(std::numeric_limits<size_t>::max(),
     61                   static_cast<size_t>(std::numeric_limits<int64_t>::max()))) {
     62        // Too big to fit into size_t, so let's bail.
     63        return NS_ERROR_OUT_OF_MEMORY;
     64      }
     65 
     66      // preallocate buffer
     67      if (!mData.initCapacity(contentLength)) {
     68        return NS_ERROR_OUT_OF_MEMORY;
     69      }
     70    }
     71  }
     72  return NS_OK;
     73 }
     74 
     75 NS_IMETHODIMP
     76 nsIncrementalStreamLoader::OnStopRequest(nsIRequest* request,
     77                                         nsresult aStatus) {
     78  AUTO_PROFILER_LABEL("nsIncrementalStreamLoader::OnStopRequest", NETWORK);
     79 
     80  if (mObserver) {
     81    // provide nsIIncrementalStreamLoader::request during call to
     82    // OnStreamComplete
     83    mRequest = request;
     84    size_t length = mData.length();
     85    uint8_t* elems = mData.extractOrCopyRawBuffer();
     86    nsresult rv =
     87        mObserver->OnStreamComplete(this, mContext, aStatus, length, elems);
     88    if (rv != NS_SUCCESS_ADOPTED_DATA) {
     89      // The observer didn't take ownership of the extracted data buffer, so
     90      // put it back into mData.
     91      mData.replaceRawBuffer(elems, length);
     92    }
     93    // done.. cleanup
     94    ReleaseData();
     95    mRequest = nullptr;
     96    mObserver = nullptr;
     97  }
     98  return NS_OK;
     99 }
    100 
    101 nsresult nsIncrementalStreamLoader::WriteSegmentFun(
    102    nsIInputStream* inStr, void* closure, const char* fromSegment,
    103    uint32_t toOffset, uint32_t count, uint32_t* writeCount) {
    104  nsIncrementalStreamLoader* self = (nsIncrementalStreamLoader*)closure;
    105 
    106  const uint8_t* data = reinterpret_cast<const uint8_t*>(fromSegment);
    107  uint32_t consumedCount = 0;
    108  nsresult rv;
    109  if (self->mData.empty()) {
    110    // Shortcut when observer wants to keep the listener's buffer empty.
    111    rv = self->mObserver->OnIncrementalData(self, self->mContext, count, data,
    112                                            &consumedCount);
    113 
    114    if (rv != NS_OK) {
    115      return rv;
    116    }
    117 
    118    if (consumedCount > count) {
    119      return NS_ERROR_INVALID_ARG;
    120    }
    121 
    122    if (consumedCount < count) {
    123      if (!self->mData.append(fromSegment + consumedCount,
    124                              count - consumedCount)) {
    125        self->mData.clearAndFree();
    126        return NS_ERROR_OUT_OF_MEMORY;
    127      }
    128    }
    129  } else {
    130    // We have some non-consumed data from previous OnIncrementalData call,
    131    // appending new data and reporting combined data.
    132    if (!self->mData.append(fromSegment, count)) {
    133      self->mData.clearAndFree();
    134      return NS_ERROR_OUT_OF_MEMORY;
    135    }
    136    size_t length = self->mData.length();
    137    uint32_t reportCount = length > UINT32_MAX ? UINT32_MAX : (uint32_t)length;
    138    uint8_t* elems = self->mData.extractOrCopyRawBuffer();
    139 
    140    rv = self->mObserver->OnIncrementalData(self, self->mContext, reportCount,
    141                                            elems, &consumedCount);
    142 
    143    // We still own elems, freeing its memory when exiting scope.
    144    if (rv != NS_OK) {
    145      free(elems);
    146      return rv;
    147    }
    148 
    149    if (consumedCount > reportCount) {
    150      free(elems);
    151      return NS_ERROR_INVALID_ARG;
    152    }
    153 
    154    if (consumedCount == length) {
    155      free(elems);  // good case -- fully consumed data
    156    } else {
    157      // Adopting elems back (at least its portion).
    158      self->mData.replaceRawBuffer(elems, length);
    159      if (consumedCount > 0) {
    160        self->mData.erase(self->mData.begin(),
    161                          self->mData.begin() + consumedCount);
    162      }
    163    }
    164  }
    165 
    166  *writeCount = count;
    167  return NS_OK;
    168 }
    169 
    170 NS_IMETHODIMP
    171 nsIncrementalStreamLoader::OnDataAvailable(nsIRequest* request,
    172                                           nsIInputStream* inStr,
    173                                           uint64_t sourceOffset,
    174                                           uint32_t count) {
    175  if (mObserver) {
    176    // provide nsIIncrementalStreamLoader::request during call to
    177    // OnStreamComplete
    178    mRequest = request;
    179  }
    180  uint32_t countRead;
    181  nsresult rv = inStr->ReadSegments(WriteSegmentFun, this, count, &countRead);
    182  mRequest = nullptr;
    183  NS_ENSURE_SUCCESS(rv, rv);
    184  mBytesRead += countRead;
    185  return rv;
    186 }
    187 
    188 void nsIncrementalStreamLoader::ReleaseData() { mData.clearAndFree(); }
    189 
    190 NS_IMETHODIMP
    191 nsIncrementalStreamLoader::CheckListenerChain() {
    192  return NS_ERROR_NO_INTERFACE;
    193 }
    194 
    195 NS_IMETHODIMP
    196 nsIncrementalStreamLoader::OnDataFinished(nsresult aStatus) { return NS_OK; }