Source code

Revision control

Other Tools

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