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