|
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
2 /* vim:set ts=4 sts=4 sw=4 cin et: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 /** |
|
8 * Based on original code from nsIStringStream.cpp |
|
9 */ |
|
10 |
|
11 #include "ipc/IPCMessageUtils.h" |
|
12 |
|
13 #include "nsStringStream.h" |
|
14 #include "nsStreamUtils.h" |
|
15 #include "nsReadableUtils.h" |
|
16 #include "nsISeekableStream.h" |
|
17 #include "nsISupportsPrimitives.h" |
|
18 #include "nsCRT.h" |
|
19 #include "prerror.h" |
|
20 #include "plstr.h" |
|
21 #include "nsIClassInfoImpl.h" |
|
22 #include "mozilla/Attributes.h" |
|
23 #include "mozilla/ipc/InputStreamUtils.h" |
|
24 #include "nsIIPCSerializableInputStream.h" |
|
25 |
|
26 using namespace mozilla::ipc; |
|
27 |
|
28 //----------------------------------------------------------------------------- |
|
29 // nsIStringInputStream implementation |
|
30 //----------------------------------------------------------------------------- |
|
31 |
|
32 class nsStringInputStream MOZ_FINAL : public nsIStringInputStream |
|
33 , public nsISeekableStream |
|
34 , public nsISupportsCString |
|
35 , public nsIIPCSerializableInputStream |
|
36 { |
|
37 public: |
|
38 NS_DECL_THREADSAFE_ISUPPORTS |
|
39 NS_DECL_NSIINPUTSTREAM |
|
40 NS_DECL_NSISTRINGINPUTSTREAM |
|
41 NS_DECL_NSISEEKABLESTREAM |
|
42 NS_DECL_NSISUPPORTSPRIMITIVE |
|
43 NS_DECL_NSISUPPORTSCSTRING |
|
44 NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM |
|
45 |
|
46 nsStringInputStream() |
|
47 { |
|
48 Clear(); |
|
49 } |
|
50 |
|
51 private: |
|
52 ~nsStringInputStream() |
|
53 {} |
|
54 |
|
55 uint32_t Length() const |
|
56 { |
|
57 return mData.Length(); |
|
58 } |
|
59 |
|
60 uint32_t LengthRemaining() const |
|
61 { |
|
62 return Length() - mOffset; |
|
63 } |
|
64 |
|
65 void Clear() |
|
66 { |
|
67 mData.SetIsVoid(true); |
|
68 } |
|
69 |
|
70 bool Closed() |
|
71 { |
|
72 return mData.IsVoid(); |
|
73 } |
|
74 |
|
75 nsDependentCSubstring mData; |
|
76 uint32_t mOffset; |
|
77 }; |
|
78 |
|
79 // This class needs to support threadsafe refcounting since people often |
|
80 // allocate a string stream, and then read it from a background thread. |
|
81 NS_IMPL_ADDREF(nsStringInputStream) |
|
82 NS_IMPL_RELEASE(nsStringInputStream) |
|
83 |
|
84 NS_IMPL_CLASSINFO(nsStringInputStream, nullptr, nsIClassInfo::THREADSAFE, |
|
85 NS_STRINGINPUTSTREAM_CID) |
|
86 NS_IMPL_QUERY_INTERFACE_CI(nsStringInputStream, |
|
87 nsIStringInputStream, |
|
88 nsIInputStream, |
|
89 nsISupportsCString, |
|
90 nsISeekableStream, |
|
91 nsIIPCSerializableInputStream) |
|
92 NS_IMPL_CI_INTERFACE_GETTER(nsStringInputStream, |
|
93 nsIStringInputStream, |
|
94 nsIInputStream, |
|
95 nsISupportsCString, |
|
96 nsISeekableStream) |
|
97 |
|
98 ///////// |
|
99 // nsISupportsCString implementation |
|
100 ///////// |
|
101 |
|
102 NS_IMETHODIMP |
|
103 nsStringInputStream::GetType(uint16_t *type) |
|
104 { |
|
105 *type = TYPE_CSTRING; |
|
106 return NS_OK; |
|
107 } |
|
108 |
|
109 NS_IMETHODIMP |
|
110 nsStringInputStream::GetData(nsACString &data) |
|
111 { |
|
112 // The stream doesn't have any data when it is closed. We could fake it |
|
113 // and return an empty string here, but it seems better to keep this return |
|
114 // value consistent with the behavior of the other 'getter' methods. |
|
115 if (NS_WARN_IF(Closed())) |
|
116 return NS_BASE_STREAM_CLOSED; |
|
117 |
|
118 data.Assign(mData); |
|
119 return NS_OK; |
|
120 } |
|
121 |
|
122 NS_IMETHODIMP |
|
123 nsStringInputStream::SetData(const nsACString &data) |
|
124 { |
|
125 mData.Assign(data); |
|
126 mOffset = 0; |
|
127 return NS_OK; |
|
128 } |
|
129 |
|
130 NS_IMETHODIMP |
|
131 nsStringInputStream::ToString(char **result) |
|
132 { |
|
133 // NOTE: This method may result in data loss, so we do not implement it. |
|
134 return NS_ERROR_NOT_IMPLEMENTED; |
|
135 } |
|
136 |
|
137 ///////// |
|
138 // nsIStringInputStream implementation |
|
139 ///////// |
|
140 |
|
141 NS_IMETHODIMP |
|
142 nsStringInputStream::SetData(const char *data, int32_t dataLen) |
|
143 { |
|
144 if (NS_WARN_IF(!data)) |
|
145 return NS_ERROR_INVALID_ARG; |
|
146 mData.Assign(data, dataLen); |
|
147 mOffset = 0; |
|
148 return NS_OK; |
|
149 } |
|
150 |
|
151 NS_IMETHODIMP |
|
152 nsStringInputStream::AdoptData(char *data, int32_t dataLen) |
|
153 { |
|
154 if (NS_WARN_IF(!data)) |
|
155 return NS_ERROR_INVALID_ARG; |
|
156 mData.Adopt(data, dataLen); |
|
157 mOffset = 0; |
|
158 return NS_OK; |
|
159 } |
|
160 |
|
161 NS_IMETHODIMP |
|
162 nsStringInputStream::ShareData(const char *data, int32_t dataLen) |
|
163 { |
|
164 if (NS_WARN_IF(!data)) |
|
165 return NS_ERROR_INVALID_ARG; |
|
166 |
|
167 if (dataLen < 0) |
|
168 dataLen = strlen(data); |
|
169 |
|
170 mData.Rebind(data, dataLen); |
|
171 mOffset = 0; |
|
172 return NS_OK; |
|
173 } |
|
174 |
|
175 ///////// |
|
176 // nsIInputStream implementation |
|
177 ///////// |
|
178 |
|
179 NS_IMETHODIMP |
|
180 nsStringInputStream::Close() |
|
181 { |
|
182 Clear(); |
|
183 return NS_OK; |
|
184 } |
|
185 |
|
186 NS_IMETHODIMP |
|
187 nsStringInputStream::Available(uint64_t *aLength) |
|
188 { |
|
189 NS_ASSERTION(aLength, "null ptr"); |
|
190 |
|
191 if (Closed()) |
|
192 return NS_BASE_STREAM_CLOSED; |
|
193 |
|
194 *aLength = LengthRemaining(); |
|
195 return NS_OK; |
|
196 } |
|
197 |
|
198 NS_IMETHODIMP |
|
199 nsStringInputStream::Read(char* aBuf, uint32_t aCount, uint32_t *aReadCount) |
|
200 { |
|
201 NS_ASSERTION(aBuf, "null ptr"); |
|
202 return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, aReadCount); |
|
203 } |
|
204 |
|
205 NS_IMETHODIMP |
|
206 nsStringInputStream::ReadSegments(nsWriteSegmentFun writer, void *closure, |
|
207 uint32_t aCount, uint32_t *result) |
|
208 { |
|
209 NS_ASSERTION(result, "null ptr"); |
|
210 NS_ASSERTION(Length() >= mOffset, "bad stream state"); |
|
211 |
|
212 if (Closed()) |
|
213 return NS_BASE_STREAM_CLOSED; |
|
214 |
|
215 // We may be at end-of-file |
|
216 uint32_t maxCount = LengthRemaining(); |
|
217 if (maxCount == 0) { |
|
218 *result = 0; |
|
219 return NS_OK; |
|
220 } |
|
221 |
|
222 if (aCount > maxCount) |
|
223 aCount = maxCount; |
|
224 nsresult rv = writer(this, closure, mData.BeginReading() + mOffset, 0, aCount, result); |
|
225 if (NS_SUCCEEDED(rv)) { |
|
226 NS_ASSERTION(*result <= aCount, |
|
227 "writer should not write more than we asked it to write"); |
|
228 mOffset += *result; |
|
229 } |
|
230 |
|
231 // errors returned from the writer end here! |
|
232 return NS_OK; |
|
233 } |
|
234 |
|
235 NS_IMETHODIMP |
|
236 nsStringInputStream::IsNonBlocking(bool *aNonBlocking) |
|
237 { |
|
238 *aNonBlocking = true; |
|
239 return NS_OK; |
|
240 } |
|
241 |
|
242 ///////// |
|
243 // nsISeekableStream implementation |
|
244 ///////// |
|
245 |
|
246 NS_IMETHODIMP |
|
247 nsStringInputStream::Seek(int32_t whence, int64_t offset) |
|
248 { |
|
249 if (Closed()) |
|
250 return NS_BASE_STREAM_CLOSED; |
|
251 |
|
252 // Compute new stream position. The given offset may be a negative value. |
|
253 |
|
254 int64_t newPos = offset; |
|
255 switch (whence) { |
|
256 case NS_SEEK_SET: |
|
257 break; |
|
258 case NS_SEEK_CUR: |
|
259 newPos += mOffset; |
|
260 break; |
|
261 case NS_SEEK_END: |
|
262 newPos += Length(); |
|
263 break; |
|
264 default: |
|
265 NS_ERROR("invalid whence"); |
|
266 return NS_ERROR_INVALID_ARG; |
|
267 } |
|
268 |
|
269 if (NS_WARN_IF(newPos < 0) || NS_WARN_IF(newPos > Length())) |
|
270 return NS_ERROR_INVALID_ARG; |
|
271 |
|
272 mOffset = (uint32_t)newPos; |
|
273 return NS_OK; |
|
274 } |
|
275 |
|
276 NS_IMETHODIMP |
|
277 nsStringInputStream::Tell(int64_t* outWhere) |
|
278 { |
|
279 if (Closed()) |
|
280 return NS_BASE_STREAM_CLOSED; |
|
281 |
|
282 *outWhere = mOffset; |
|
283 return NS_OK; |
|
284 } |
|
285 |
|
286 NS_IMETHODIMP |
|
287 nsStringInputStream::SetEOF() |
|
288 { |
|
289 if (Closed()) |
|
290 return NS_BASE_STREAM_CLOSED; |
|
291 |
|
292 mOffset = Length(); |
|
293 return NS_OK; |
|
294 } |
|
295 |
|
296 void |
|
297 nsStringInputStream::Serialize(InputStreamParams& aParams, |
|
298 FileDescriptorArray& /* aFDs */) |
|
299 { |
|
300 StringInputStreamParams params; |
|
301 params.data() = PromiseFlatCString(mData); |
|
302 aParams = params; |
|
303 } |
|
304 |
|
305 bool |
|
306 nsStringInputStream::Deserialize(const InputStreamParams& aParams, |
|
307 const FileDescriptorArray& /* aFDs */) |
|
308 { |
|
309 if (aParams.type() != InputStreamParams::TStringInputStreamParams) { |
|
310 NS_ERROR("Received unknown parameters from the other process!"); |
|
311 return false; |
|
312 } |
|
313 |
|
314 const StringInputStreamParams& params = |
|
315 aParams.get_StringInputStreamParams(); |
|
316 |
|
317 if (NS_FAILED(SetData(params.data()))) { |
|
318 NS_WARNING("SetData failed!"); |
|
319 return false; |
|
320 } |
|
321 |
|
322 return true; |
|
323 } |
|
324 |
|
325 nsresult |
|
326 NS_NewByteInputStream(nsIInputStream** aStreamResult, |
|
327 const char* aStringToRead, int32_t aLength, |
|
328 nsAssignmentType aAssignment) |
|
329 { |
|
330 NS_PRECONDITION(aStreamResult, "null out ptr"); |
|
331 |
|
332 nsStringInputStream* stream = new nsStringInputStream(); |
|
333 if (! stream) |
|
334 return NS_ERROR_OUT_OF_MEMORY; |
|
335 |
|
336 NS_ADDREF(stream); |
|
337 |
|
338 nsresult rv; |
|
339 switch (aAssignment) { |
|
340 case NS_ASSIGNMENT_COPY: |
|
341 rv = stream->SetData(aStringToRead, aLength); |
|
342 break; |
|
343 case NS_ASSIGNMENT_DEPEND: |
|
344 rv = stream->ShareData(aStringToRead, aLength); |
|
345 break; |
|
346 case NS_ASSIGNMENT_ADOPT: |
|
347 rv = stream->AdoptData(const_cast<char*>(aStringToRead), aLength); |
|
348 break; |
|
349 default: |
|
350 NS_ERROR("invalid assignment type"); |
|
351 rv = NS_ERROR_INVALID_ARG; |
|
352 } |
|
353 |
|
354 if (NS_FAILED(rv)) { |
|
355 NS_RELEASE(stream); |
|
356 return rv; |
|
357 } |
|
358 |
|
359 *aStreamResult = stream; |
|
360 return NS_OK; |
|
361 } |
|
362 |
|
363 nsresult |
|
364 NS_NewStringInputStream(nsIInputStream** aStreamResult, |
|
365 const nsAString& aStringToRead) |
|
366 { |
|
367 NS_LossyConvertUTF16toASCII data(aStringToRead); // truncates high-order bytes |
|
368 return NS_NewCStringInputStream(aStreamResult, data); |
|
369 } |
|
370 |
|
371 nsresult |
|
372 NS_NewCStringInputStream(nsIInputStream** aStreamResult, |
|
373 const nsACString& aStringToRead) |
|
374 { |
|
375 NS_PRECONDITION(aStreamResult, "null out ptr"); |
|
376 |
|
377 nsStringInputStream* stream = new nsStringInputStream(); |
|
378 if (! stream) |
|
379 return NS_ERROR_OUT_OF_MEMORY; |
|
380 |
|
381 NS_ADDREF(stream); |
|
382 |
|
383 stream->SetData(aStringToRead); |
|
384 |
|
385 *aStreamResult = stream; |
|
386 return NS_OK; |
|
387 } |
|
388 |
|
389 // factory method for constructing a nsStringInputStream object |
|
390 nsresult |
|
391 nsStringInputStreamConstructor(nsISupports *outer, REFNSIID iid, void **result) |
|
392 { |
|
393 *result = nullptr; |
|
394 |
|
395 if (NS_WARN_IF(outer)) |
|
396 return NS_ERROR_NO_AGGREGATION; |
|
397 |
|
398 nsStringInputStream *inst = new nsStringInputStream(); |
|
399 if (!inst) |
|
400 return NS_ERROR_OUT_OF_MEMORY; |
|
401 |
|
402 NS_ADDREF(inst); |
|
403 nsresult rv = inst->QueryInterface(iid, result); |
|
404 NS_RELEASE(inst); |
|
405 |
|
406 return rv; |
|
407 } |