Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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/. */
7 /**
8 * Based on original code from nsIStringStream.cpp
9 */
11 #include "ipc/IPCMessageUtils.h"
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"
26 using namespace mozilla::ipc;
28 //-----------------------------------------------------------------------------
29 // nsIStringInputStream implementation
30 //-----------------------------------------------------------------------------
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
46 nsStringInputStream()
47 {
48 Clear();
49 }
51 private:
52 ~nsStringInputStream()
53 {}
55 uint32_t Length() const
56 {
57 return mData.Length();
58 }
60 uint32_t LengthRemaining() const
61 {
62 return Length() - mOffset;
63 }
65 void Clear()
66 {
67 mData.SetIsVoid(true);
68 }
70 bool Closed()
71 {
72 return mData.IsVoid();
73 }
75 nsDependentCSubstring mData;
76 uint32_t mOffset;
77 };
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)
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)
98 /////////
99 // nsISupportsCString implementation
100 /////////
102 NS_IMETHODIMP
103 nsStringInputStream::GetType(uint16_t *type)
104 {
105 *type = TYPE_CSTRING;
106 return NS_OK;
107 }
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;
118 data.Assign(mData);
119 return NS_OK;
120 }
122 NS_IMETHODIMP
123 nsStringInputStream::SetData(const nsACString &data)
124 {
125 mData.Assign(data);
126 mOffset = 0;
127 return NS_OK;
128 }
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 }
137 /////////
138 // nsIStringInputStream implementation
139 /////////
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 }
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 }
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;
167 if (dataLen < 0)
168 dataLen = strlen(data);
170 mData.Rebind(data, dataLen);
171 mOffset = 0;
172 return NS_OK;
173 }
175 /////////
176 // nsIInputStream implementation
177 /////////
179 NS_IMETHODIMP
180 nsStringInputStream::Close()
181 {
182 Clear();
183 return NS_OK;
184 }
186 NS_IMETHODIMP
187 nsStringInputStream::Available(uint64_t *aLength)
188 {
189 NS_ASSERTION(aLength, "null ptr");
191 if (Closed())
192 return NS_BASE_STREAM_CLOSED;
194 *aLength = LengthRemaining();
195 return NS_OK;
196 }
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 }
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");
212 if (Closed())
213 return NS_BASE_STREAM_CLOSED;
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 }
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 }
231 // errors returned from the writer end here!
232 return NS_OK;
233 }
235 NS_IMETHODIMP
236 nsStringInputStream::IsNonBlocking(bool *aNonBlocking)
237 {
238 *aNonBlocking = true;
239 return NS_OK;
240 }
242 /////////
243 // nsISeekableStream implementation
244 /////////
246 NS_IMETHODIMP
247 nsStringInputStream::Seek(int32_t whence, int64_t offset)
248 {
249 if (Closed())
250 return NS_BASE_STREAM_CLOSED;
252 // Compute new stream position. The given offset may be a negative value.
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 }
269 if (NS_WARN_IF(newPos < 0) || NS_WARN_IF(newPos > Length()))
270 return NS_ERROR_INVALID_ARG;
272 mOffset = (uint32_t)newPos;
273 return NS_OK;
274 }
276 NS_IMETHODIMP
277 nsStringInputStream::Tell(int64_t* outWhere)
278 {
279 if (Closed())
280 return NS_BASE_STREAM_CLOSED;
282 *outWhere = mOffset;
283 return NS_OK;
284 }
286 NS_IMETHODIMP
287 nsStringInputStream::SetEOF()
288 {
289 if (Closed())
290 return NS_BASE_STREAM_CLOSED;
292 mOffset = Length();
293 return NS_OK;
294 }
296 void
297 nsStringInputStream::Serialize(InputStreamParams& aParams,
298 FileDescriptorArray& /* aFDs */)
299 {
300 StringInputStreamParams params;
301 params.data() = PromiseFlatCString(mData);
302 aParams = params;
303 }
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 }
314 const StringInputStreamParams& params =
315 aParams.get_StringInputStreamParams();
317 if (NS_FAILED(SetData(params.data()))) {
318 NS_WARNING("SetData failed!");
319 return false;
320 }
322 return true;
323 }
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");
332 nsStringInputStream* stream = new nsStringInputStream();
333 if (! stream)
334 return NS_ERROR_OUT_OF_MEMORY;
336 NS_ADDREF(stream);
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 }
354 if (NS_FAILED(rv)) {
355 NS_RELEASE(stream);
356 return rv;
357 }
359 *aStreamResult = stream;
360 return NS_OK;
361 }
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 }
371 nsresult
372 NS_NewCStringInputStream(nsIInputStream** aStreamResult,
373 const nsACString& aStringToRead)
374 {
375 NS_PRECONDITION(aStreamResult, "null out ptr");
377 nsStringInputStream* stream = new nsStringInputStream();
378 if (! stream)
379 return NS_ERROR_OUT_OF_MEMORY;
381 NS_ADDREF(stream);
383 stream->SetData(aStringToRead);
385 *aStreamResult = stream;
386 return NS_OK;
387 }
389 // factory method for constructing a nsStringInputStream object
390 nsresult
391 nsStringInputStreamConstructor(nsISupports *outer, REFNSIID iid, void **result)
392 {
393 *result = nullptr;
395 if (NS_WARN_IF(outer))
396 return NS_ERROR_NO_AGGREGATION;
398 nsStringInputStream *inst = new nsStringInputStream();
399 if (!inst)
400 return NS_ERROR_OUT_OF_MEMORY;
402 NS_ADDREF(inst);
403 nsresult rv = inst->QueryInterface(iid, result);
404 NS_RELEASE(inst);
406 return rv;
407 }