1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
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/. */
8 * Based on original code from nsIStringStream.cpp
11 #include "ipc/IPCMessageUtils.h"
13 #include "nsStringStream.h"
14 #include "nsStreamUtils.h"
15 #include "nsReadableUtils.h"
16 #include "nsICloneableInputStream.h"
17 #include "nsISeekableStream.h"
18 #include "nsISupportsPrimitives.h"
21 #include "nsIClassInfoImpl.h"
22 #include "mozilla/Attributes.h"
23 #include "mozilla/ipc/InputStreamUtils.h"
24 #include "mozilla/Maybe.h"
25 #include "mozilla/ReentrantMonitor.h"
26 #include "mozilla/StreamBufferSourceImpl.h"
27 #include "nsIIPCSerializableInputStream.h"
28 #include "XPCOMModule.h"
30 using namespace mozilla::ipc
;
31 using mozilla::fallible
;
32 using mozilla::MakeRefPtr
;
33 using mozilla::MallocSizeOf
;
34 using mozilla::nsBorrowedSource
;
35 using mozilla::nsCStringSource
;
36 using mozilla::nsTArraySource
;
37 using mozilla::ReentrantMonitorAutoEnter
;
39 using mozilla::StreamBufferSource
;
41 //-----------------------------------------------------------------------------
42 // nsIStringInputStream implementation
43 //-----------------------------------------------------------------------------
45 class nsStringInputStream final
: public nsIStringInputStream
,
46 public nsISeekableStream
,
47 public nsISupportsCString
,
48 public nsIIPCSerializableInputStream
,
49 public nsICloneableInputStream
{
51 NS_DECL_THREADSAFE_ISUPPORTS
52 NS_DECL_NSIINPUTSTREAM
53 NS_DECL_NSISTRINGINPUTSTREAM
54 NS_DECL_NSISEEKABLESTREAM
55 NS_DECL_NSITELLABLESTREAM
56 NS_DECL_NSISUPPORTSPRIMITIVE
57 NS_DECL_NSISUPPORTSCSTRING
58 NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
59 NS_DECL_NSICLONEABLEINPUTSTREAM
61 nsStringInputStream() = default;
63 nsresult
Init(nsCString
&& aString
);
65 nsresult
Init(nsTArray
<uint8_t>&& aArray
);
68 ~nsStringInputStream() = default;
70 size_t Length() const MOZ_REQUIRES(mMon
) {
71 return mSource
? mSource
->Data().Length() : 0;
74 size_t LengthRemaining() const MOZ_REQUIRES(mMon
) {
75 return Length() - mOffset
;
78 void Clear() MOZ_REQUIRES(mMon
) { mSource
= nullptr; }
80 bool Closed() MOZ_REQUIRES(mMon
) { return !mSource
; }
82 RefPtr
<StreamBufferSource
> mSource
MOZ_GUARDED_BY(mMon
);
83 size_t mOffset
MOZ_GUARDED_BY(mMon
) = 0;
85 mutable mozilla::ReentrantMonitor mMon
{"nsStringInputStream"};
88 nsresult
nsStringInputStream::Init(nsCString
&& aString
) {
90 if (!string
.Assign(std::move(aString
), fallible
)) {
91 return NS_ERROR_OUT_OF_MEMORY
;
93 auto source
= MakeRefPtr
<nsCStringSource
>(std::move(string
));
94 return SetDataSource(source
);
97 nsresult
nsStringInputStream::Init(nsTArray
<uint8_t>&& aArray
) {
98 auto source
= MakeRefPtr
<nsTArraySource
>(std::move(aArray
));
99 return SetDataSource(source
);
102 // This class needs to support threadsafe refcounting since people often
103 // allocate a string stream, and then read it from a background thread.
104 NS_IMPL_ADDREF(nsStringInputStream
)
105 NS_IMPL_RELEASE(nsStringInputStream
)
107 NS_IMPL_CLASSINFO(nsStringInputStream
, nullptr, nsIClassInfo::THREADSAFE
,
108 NS_STRINGINPUTSTREAM_CID
)
109 NS_IMPL_QUERY_INTERFACE_CI(nsStringInputStream
, nsIStringInputStream
,
110 nsIInputStream
, nsISupportsCString
,
111 nsISeekableStream
, nsITellableStream
,
112 nsIIPCSerializableInputStream
,
113 nsICloneableInputStream
)
114 NS_IMPL_CI_INTERFACE_GETTER(nsStringInputStream
, nsIStringInputStream
,
115 nsIInputStream
, nsISupportsCString
,
116 nsISeekableStream
, nsITellableStream
,
117 nsICloneableInputStream
)
120 // nsISupportsCString implementation
124 nsStringInputStream::GetType(uint16_t* aType
) {
125 *aType
= TYPE_CSTRING
;
130 nsStringInputStream::GetData(nsACString
& data
) {
131 ReentrantMonitorAutoEnter
lock(mMon
);
133 // The stream doesn't have any data when it is closed. We could fake it
134 // and return an empty string here, but it seems better to keep this return
135 // value consistent with the behavior of the other 'getter' methods.
136 if (NS_WARN_IF(Closed())) {
137 return NS_BASE_STREAM_CLOSED
;
140 return mSource
->GetData(data
);
144 nsStringInputStream::SetData(const nsACString
& aData
) {
146 if (!string
.Assign(aData
, fallible
)) {
147 return NS_ERROR_OUT_OF_MEMORY
;
149 auto source
= MakeRefPtr
<nsCStringSource
>(std::move(string
));
150 return SetDataSource(source
);
154 nsStringInputStream::ToString(char** aResult
) {
155 // NOTE: This method may result in data loss, so we do not implement it.
156 return NS_ERROR_NOT_IMPLEMENTED
;
160 // nsIStringInputStream implementation
164 nsStringInputStream::SetData(const char* aData
, int32_t aDataLen
) {
165 if (NS_WARN_IF(!aData
)) {
166 return NS_ERROR_INVALID_ARG
;
170 if (NS_WARN_IF(!string
.Assign(aData
, aDataLen
, fallible
))) {
171 return NS_ERROR_OUT_OF_MEMORY
;
173 auto source
= MakeRefPtr
<nsCStringSource
>(std::move(string
));
174 return SetDataSource(source
);
178 nsStringInputStream::SetUTF8Data(const nsACString
& aData
) {
179 return nsStringInputStream::SetData(aData
);
183 nsStringInputStream::AdoptData(char* aData
, int32_t aDataLen
) {
184 if (NS_WARN_IF(!aData
)) {
185 return NS_ERROR_INVALID_ARG
;
189 string
.Adopt(aData
, aDataLen
);
190 auto source
= MakeRefPtr
<nsCStringSource
>(std::move(string
));
191 return SetDataSource(source
);
195 nsStringInputStream::ShareData(const char* aData
, int32_t aDataLen
) {
196 if (NS_WARN_IF(!aData
)) {
197 return NS_ERROR_INVALID_ARG
;
200 size_t length
= aDataLen
< 0 ? strlen(aData
) : size_t(aDataLen
);
201 auto source
= MakeRefPtr
<nsBorrowedSource
>(Span
{aData
, length
});
202 return SetDataSource(source
);
206 nsStringInputStream::SetDataSource(StreamBufferSource
* aSource
) {
207 ReentrantMonitorAutoEnter
lock(mMon
);
209 if (NS_WARN_IF(!aSource
)) {
210 return NS_ERROR_INVALID_ARG
;
218 NS_IMETHODIMP_(size_t)
219 nsStringInputStream::SizeOfIncludingThisIfUnshared(MallocSizeOf aMallocSizeOf
) {
220 ReentrantMonitorAutoEnter
lock(mMon
);
222 size_t n
= aMallocSizeOf(this);
224 n
+= mSource
->SizeOfIncludingThisIfUnshared(aMallocSizeOf
);
229 NS_IMETHODIMP_(size_t)
230 nsStringInputStream::SizeOfIncludingThisEvenIfShared(
231 MallocSizeOf aMallocSizeOf
) {
232 ReentrantMonitorAutoEnter
lock(mMon
);
234 size_t n
= aMallocSizeOf(this);
236 n
+= mSource
->SizeOfIncludingThisEvenIfShared(aMallocSizeOf
);
242 // nsIInputStream implementation
246 nsStringInputStream::Close() {
247 ReentrantMonitorAutoEnter
lock(mMon
);
254 nsStringInputStream::Available(uint64_t* aLength
) {
255 ReentrantMonitorAutoEnter
lock(mMon
);
257 NS_ASSERTION(aLength
, "null ptr");
260 return NS_BASE_STREAM_CLOSED
;
263 *aLength
= LengthRemaining();
268 nsStringInputStream::StreamStatus() {
269 ReentrantMonitorAutoEnter
lock(mMon
);
270 return Closed() ? NS_BASE_STREAM_CLOSED
: NS_OK
;
274 nsStringInputStream::Read(char* aBuf
, uint32_t aCount
, uint32_t* aReadCount
) {
275 NS_ASSERTION(aBuf
, "null ptr");
276 return ReadSegments(NS_CopySegmentToBuffer
, aBuf
, aCount
, aReadCount
);
280 nsStringInputStream::ReadSegments(nsWriteSegmentFun aWriter
, void* aClosure
,
281 uint32_t aCount
, uint32_t* aResult
) {
282 ReentrantMonitorAutoEnter
lock(mMon
);
284 NS_ASSERTION(aResult
, "null ptr");
285 NS_ASSERTION(Length() >= mOffset
, "bad stream state");
288 return NS_BASE_STREAM_CLOSED
;
291 // We may be at end-of-file
292 size_t maxCount
= LengthRemaining();
298 if (aCount
> maxCount
) {
302 RefPtr
<StreamBufferSource
> source
= mSource
;
303 size_t offset
= mOffset
;
305 nsresult rv
= aWriter(this, aClosure
, source
->Data().Elements() + offset
, 0,
309 NS_WARNING("nsStringInputStream was closed during ReadSegments");
313 MOZ_RELEASE_ASSERT(mSource
== source
, "String was replaced!");
314 MOZ_RELEASE_ASSERT(mOffset
== offset
, "Nested read operation!");
316 if (NS_SUCCEEDED(rv
)) {
317 NS_ASSERTION(*aResult
<= aCount
,
318 "writer should not write more than we asked it to write");
319 mOffset
= offset
+ *aResult
;
322 // errors returned from the writer end here!
327 nsStringInputStream::IsNonBlocking(bool* aNonBlocking
) {
328 *aNonBlocking
= true;
333 // nsISeekableStream implementation
337 nsStringInputStream::Seek(int32_t aWhence
, int64_t aOffset
) {
338 ReentrantMonitorAutoEnter
lock(mMon
);
341 return NS_BASE_STREAM_CLOSED
;
344 // Compute new stream position. The given offset may be a negative value.
346 int64_t newPos
= aOffset
;
351 newPos
+= (int64_t)mOffset
;
354 newPos
+= (int64_t)Length();
357 NS_ERROR("invalid aWhence");
358 return NS_ERROR_INVALID_ARG
;
361 if (NS_WARN_IF(newPos
< 0) || NS_WARN_IF(newPos
> (int64_t)Length())) {
362 return NS_ERROR_INVALID_ARG
;
365 mOffset
= (size_t)newPos
;
370 nsStringInputStream::SetEOF() {
371 ReentrantMonitorAutoEnter
lock(mMon
);
374 return NS_BASE_STREAM_CLOSED
;
382 // nsITellableStream implementation
386 nsStringInputStream::Tell(int64_t* aOutWhere
) {
387 ReentrantMonitorAutoEnter
lock(mMon
);
390 return NS_BASE_STREAM_CLOSED
;
393 *aOutWhere
= (int64_t)mOffset
;
398 // nsIIPCSerializableInputStream implementation
401 void nsStringInputStream::SerializedComplexity(uint32_t aMaxSize
,
404 uint32_t* aTransferables
) {
405 ReentrantMonitorAutoEnter
lock(mMon
);
407 if (Length() >= aMaxSize
) {
410 *aSizeUsed
= Length();
414 void nsStringInputStream::Serialize(InputStreamParams
& aParams
,
415 uint32_t aMaxSize
, uint32_t* aSizeUsed
) {
416 ReentrantMonitorAutoEnter
lock(mMon
);
418 MOZ_DIAGNOSTIC_ASSERT(!Closed(), "cannot send a closed stream!");
419 MOZ_ASSERT(aSizeUsed
);
422 if (Length() >= aMaxSize
) {
423 // If the input stream is non-owning (i.e. it was initialized with
424 // `ShareData`), create a new owning source so that it doesn't go away while
426 if (!mSource
->Owning()) {
428 MakeRefPtr
<nsCStringSource
>(nsDependentCSubstring(mSource
->Data()));
432 InputStreamHelper::SerializeInputStreamAsPipe(this, aParams
);
436 *aSizeUsed
= Length();
438 StringInputStreamParams params
;
439 mSource
->GetData(params
.data());
443 bool nsStringInputStream::Deserialize(const InputStreamParams
& aParams
) {
444 if (aParams
.type() != InputStreamParams::TStringInputStreamParams
) {
445 NS_ERROR("Received unknown parameters from the other process!");
449 const StringInputStreamParams
& params
= aParams
.get_StringInputStreamParams();
451 if (NS_FAILED(SetData(params
.data()))) {
452 NS_WARNING("SetData failed!");
460 // nsICloneableInputStream implementation
464 nsStringInputStream::GetCloneable(bool* aCloneableOut
) {
465 *aCloneableOut
= true;
470 nsStringInputStream::Clone(nsIInputStream
** aCloneOut
) {
471 ReentrantMonitorAutoEnter
lock(mMon
);
473 RefPtr
<nsStringInputStream
> ref
= new nsStringInputStream();
474 // Nothing else can access this yet, but suppress static analysis warnings
475 ReentrantMonitorAutoEnter
reflock(ref
->mMon
);
476 if (mSource
&& !mSource
->Owning()) {
477 auto data
= mSource
->Data();
478 nsresult rv
= ref
->SetData(data
.Elements(), data
.Length());
479 if (NS_WARN_IF(NS_FAILED(rv
))) {
483 ref
->mSource
= mSource
;
486 // mOffset is overwritten by SetData().
487 ref
->mOffset
= mOffset
;
489 ref
.forget(aCloneOut
);
493 nsresult
NS_NewByteInputStream(nsIInputStream
** aStreamResult
,
494 mozilla::Span
<const char> aStringToRead
,
495 nsAssignmentType aAssignment
) {
496 MOZ_ASSERT(aStreamResult
, "null out ptr");
498 RefPtr
<nsStringInputStream
> stream
= new nsStringInputStream();
501 switch (aAssignment
) {
502 case NS_ASSIGNMENT_COPY
:
503 rv
= stream
->SetData(aStringToRead
.Elements(), aStringToRead
.Length());
505 case NS_ASSIGNMENT_DEPEND
:
506 rv
= stream
->ShareData(aStringToRead
.Elements(), aStringToRead
.Length());
508 case NS_ASSIGNMENT_ADOPT
:
509 rv
= stream
->AdoptData(const_cast<char*>(aStringToRead
.Elements()),
510 aStringToRead
.Length());
513 NS_ERROR("invalid assignment type");
514 rv
= NS_ERROR_INVALID_ARG
;
521 stream
.forget(aStreamResult
);
525 nsresult
NS_NewByteInputStream(nsIInputStream
** aStreamResult
,
526 nsTArray
<uint8_t>&& aArray
) {
527 MOZ_ASSERT(aStreamResult
, "null out ptr");
529 RefPtr
<nsStringInputStream
> stream
= new nsStringInputStream();
531 nsresult rv
= stream
->Init(std::move(aArray
));
532 if (NS_WARN_IF(NS_FAILED(rv
))) {
536 stream
.forget(aStreamResult
);
540 extern nsresult
NS_NewByteInputStream(nsIInputStream
** aStreamResult
,
541 mozilla::StreamBufferSource
* aSource
) {
542 MOZ_ASSERT(aStreamResult
, "null out ptr");
544 RefPtr
<nsStringInputStream
> stream
= new nsStringInputStream();
546 nsresult rv
= stream
->SetDataSource(aSource
);
547 if (NS_WARN_IF(NS_FAILED(rv
))) {
551 stream
.forget(aStreamResult
);
555 nsresult
NS_NewCStringInputStream(nsIInputStream
** aStreamResult
,
556 const nsACString
& aStringToRead
) {
557 MOZ_ASSERT(aStreamResult
, "null out ptr");
559 RefPtr
<nsStringInputStream
> stream
= new nsStringInputStream();
561 nsresult rv
= stream
->SetData(aStringToRead
);
562 if (NS_WARN_IF(NS_FAILED(rv
))) {
566 stream
.forget(aStreamResult
);
570 nsresult
NS_NewCStringInputStream(nsIInputStream
** aStreamResult
,
571 nsCString
&& aStringToRead
) {
572 MOZ_ASSERT(aStreamResult
, "null out ptr");
574 RefPtr
<nsStringInputStream
> stream
= new nsStringInputStream();
576 nsresult rv
= stream
->Init(std::move(aStringToRead
));
577 if (NS_WARN_IF(NS_FAILED(rv
))) {
581 stream
.forget(aStreamResult
);
585 // factory method for constructing a nsStringInputStream object
586 nsresult
nsStringInputStreamConstructor(REFNSIID aIID
, void** aResult
) {
589 RefPtr
<nsStringInputStream
> inst
= new nsStringInputStream();
590 return inst
->QueryInterface(aIID
, aResult
);