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; }