Bug 1748719 - Make nsStringInputStream more flexible as to the backing data buffer, r=mccr8

This change aims to make the way that the nsStringInputStream owns the backing
data buffer more flexible. This has a few impacts, including allowing
arbitrarily large payload sizes on 64-bit platforms, not requiring as complex
checks around borrowed string buffers or nsTArray data storage, and supporting
custom data ownership schemes such as those used by blobs.

The new system uses a separate refcounted object internally to provide the
contiguous backing buffer, with the nsStringInputStream wrapping it and
providing the `nsIInputStream` interface and cursor. This also avoids issues
around the buffer being mutated during reads, as mutating the `nsIInputStream`
no longer mutates the actual data storage object.

Differential Revision: https://phabricator.services.mozilla.com/D135162
This commit is contained in:
Nika Layzell 2022-01-07 20:34:52 +00:00
Родитель 7a827c900c
Коммит 4f556b1bea
7 изменённых файлов: 258 добавлений и 204 удалений

Просмотреть файл

@ -17,19 +17,6 @@
namespace mozilla::dom {
NS_IMPL_ADDREF(MemoryBlobImpl::DataOwnerAdapter)
NS_IMPL_RELEASE(MemoryBlobImpl::DataOwnerAdapter)
NS_INTERFACE_MAP_BEGIN(MemoryBlobImpl::DataOwnerAdapter)
NS_INTERFACE_MAP_ENTRY(nsIInputStream)
NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
NS_INTERFACE_MAP_ENTRY(nsITellableStream)
NS_INTERFACE_MAP_ENTRY(nsICloneableInputStream)
NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
mSerializableInputStream)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
NS_INTERFACE_MAP_END
// static
already_AddRefed<MemoryBlobImpl> MemoryBlobImpl::CreateWithCustomLastModified(
void* aMemoryBuffer, uint64_t aLength, const nsAString& aName,
@ -51,70 +38,12 @@ already_AddRefed<MemoryBlobImpl> MemoryBlobImpl::CreateWithLastModifiedNow(
}
nsresult MemoryBlobImpl::DataOwnerAdapter::Create(DataOwner* aDataOwner,
uint32_t aStart,
uint32_t aLength,
size_t aStart, size_t aLength,
nsIInputStream** _retval) {
nsresult rv;
MOZ_ASSERT(aDataOwner, "Uh ...");
nsCOMPtr<nsIInputStream> stream;
rv = NS_NewByteInputStream(
getter_AddRefs(stream),
Span(static_cast<const char*>(aDataOwner->mData) + aStart, aLength),
NS_ASSIGNMENT_DEPEND);
NS_ENSURE_SUCCESS(rv, rv);
NS_ADDREF(*_retval = new MemoryBlobImpl::DataOwnerAdapter(aDataOwner, stream,
aLength));
return NS_OK;
}
void MemoryBlobImpl::DataOwnerAdapter::Serialize(
mozilla::ipc::InputStreamParams& aParams,
FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
uint32_t aMaxSize, uint32_t* aSizeUsed,
mozilla::ipc::ChildToParentStreamActorManager* aManager) {
SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
aSizeUsed, aManager);
}
void MemoryBlobImpl::DataOwnerAdapter::Serialize(
mozilla::ipc::InputStreamParams& aParams,
FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
uint32_t aMaxSize, uint32_t* aSizeUsed,
mozilla::ipc::ParentToChildStreamActorManager* aManager) {
SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
aSizeUsed, aManager);
}
template <typename M>
void MemoryBlobImpl::DataOwnerAdapter::SerializeInternal(
mozilla::ipc::InputStreamParams& aParams,
FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
uint32_t aMaxSize, uint32_t* aSizeUsed, M* aManager) {
MOZ_ASSERT(aSizeUsed);
// If we'd be serializing the underlying nsStringInputStream as a pipe,
// serialize ourselves as a pipe directly instead to avoid the string copy in
// nsStringInputStream's serialize method. Otherwise we'll delegate and
// serialize as a string internally.
if (mLength >= aMaxSize) {
*aSizeUsed = 0;
mozilla::ipc::InputStreamHelper::SerializeInputStreamAsPipe(
this, aParams, aDelayedStart, aManager);
return;
}
mSerializableInputStream->Serialize(aParams, aFileDescriptors, aDelayedStart,
aMaxSize, aSizeUsed, aManager);
}
bool MemoryBlobImpl::DataOwnerAdapter::Deserialize(
const mozilla::ipc::InputStreamParams&, const FileDescriptorArray&) {
MOZ_CRASH("This method should never be called");
return false;
Span data{static_cast<const char*>(aDataOwner->mData) + aStart, aLength};
RefPtr adapter = new MemoryBlobImpl::DataOwnerAdapter(aDataOwner, data);
return NS_NewByteInputStream(_retval, adapter);
}
already_AddRefed<BlobImpl> MemoryBlobImpl::CreateSlice(

Просмотреть файл

@ -11,6 +11,7 @@
#include "mozilla/LinkedList.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/StreamBufferSource.h"
#include "nsCOMPtr.h"
#include "nsICloneableInputStream.h"
#include "nsIInputStream.h"
@ -104,53 +105,31 @@ class MemoryBlobImpl final : public BaseBlobImpl {
uint64_t mLength;
};
class DataOwnerAdapter final : public nsIInputStream,
public nsISeekableStream,
public nsIIPCSerializableInputStream,
public nsICloneableInputStream {
class DataOwnerAdapter final : public StreamBufferSource {
using DataOwner = MemoryBlobImpl::DataOwner;
public:
static nsresult Create(DataOwner* aDataOwner, uint32_t aStart,
uint32_t aLength, nsIInputStream** _retval);
static nsresult Create(DataOwner* aDataOwner, size_t aStart, size_t aLength,
nsIInputStream** _retval);
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
Span<const char> Data() override { return mData; }
// These are mandatory.
NS_FORWARD_NSIINPUTSTREAM(mStream->)
NS_FORWARD_NSISEEKABLESTREAM(mSeekableStream->)
NS_FORWARD_NSITELLABLESTREAM(mSeekableStream->)
NS_FORWARD_NSICLONEABLEINPUTSTREAM(mCloneableInputStream->)
// This StreamBufferSource is owning, as the `mData` span references the
// immutable data buffer owned by `mDataOwner` which is being kept alive.
bool Owning() override { return true; }
// The memory usage from `DataOwner` is reported elsewhere, so we don't need
// to record it here.
size_t SizeOfExcludingThisEvenIfShared(MallocSizeOf) override { return 0; }
private:
~DataOwnerAdapter() = default;
DataOwnerAdapter(DataOwner* aDataOwner, nsIInputStream* aStream,
uint32_t aLength)
: mDataOwner(aDataOwner),
mStream(aStream),
mSeekableStream(do_QueryInterface(aStream)),
mSerializableInputStream(do_QueryInterface(aStream)),
mCloneableInputStream(do_QueryInterface(aStream)),
mLength(aLength) {
MOZ_ASSERT(
mSeekableStream && mSerializableInputStream && mCloneableInputStream,
"Somebody gave us the wrong stream!");
}
template <typename M>
void SerializeInternal(mozilla::ipc::InputStreamParams& aParams,
FileDescriptorArray& aFileDescriptors,
bool aDelayedStart, uint32_t aMaxSize,
uint32_t* aSizeUsed, M* aManager);
DataOwnerAdapter(DataOwner* aDataOwner, Span<const char> aData)
: mDataOwner(aDataOwner), mData(aData) {}
RefPtr<DataOwner> mDataOwner;
nsCOMPtr<nsIInputStream> mStream;
nsCOMPtr<nsISeekableStream> mSeekableStream;
nsCOMPtr<nsIIPCSerializableInputStream> mSerializableInputStream;
nsCOMPtr<nsICloneableInputStream> mCloneableInputStream;
uint32_t mLength;
Span<const char> mData;
};
private:

Просмотреть файл

@ -0,0 +1,58 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_StreamBufferSource_h
#define mozilla_StreamBufferSource_h
#include "mozilla/MemoryReporting.h"
#include "nsString.h"
#include "nsISupportsImpl.h"
namespace mozilla {
class StreamBufferSource {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(StreamBufferSource)
virtual Span<const char> Data() = 0;
virtual nsresult GetData(nsACString& aString) {
Span<const char> data = Data();
if (!aString.Assign(data.Elements(), data.Length(), fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
virtual bool Owning() = 0;
virtual size_t SizeOfExcludingThisIfUnshared(MallocSizeOf aMallocSizeOf) {
return SizeOfExcludingThisEvenIfShared(aMallocSizeOf);
}
size_t SizeOfIncludingThisIfUnshared(MallocSizeOf aMallocSizeOf) {
if (mRefCnt > 1) {
return 0;
}
size_t n = aMallocSizeOf(this);
n += SizeOfExcludingThisIfUnshared(aMallocSizeOf);
return n;
}
virtual size_t SizeOfExcludingThisEvenIfShared(
MallocSizeOf aMallocSizeOf) = 0;
size_t SizeOfIncludingThisEvenIfShared(MallocSizeOf aMallocSizeOf) {
size_t n = aMallocSizeOf(this);
n += SizeOfExcludingThisEvenIfShared(aMallocSizeOf);
return n;
}
protected:
virtual ~StreamBufferSource() = default;
};
} // namespace mozilla
#endif // mozilla_StreamBufferSource_h

Просмотреть файл

@ -99,6 +99,7 @@ EXPORTS.mozilla += [
"SnappyCompressOutputStream.h",
"SnappyFrameUtils.h",
"SnappyUncompressInputStream.h",
"StreamBufferSource.h",
]
UNIFIED_SOURCES += [

Просмотреть файл

@ -7,9 +7,14 @@
%{C++
#include "mozilla/MemoryReporting.h"
namespace mozilla {
class StreamBufferSource;
} // namespace mozilla
%}
native MallocSizeOf(mozilla::MallocSizeOf);
[ptr] native StreamBufferSource(mozilla::StreamBufferSource);
/**
* nsIStringInputStream
@ -71,6 +76,14 @@ interface nsIStringInputStream : nsIInputStream
*/
[noscript] void shareData(in string data, in long dataLen);
/**
* SetDataSource - assign data to the input stream. the input stream holds
* a strong reference to the given data buffer until it is destroyed.
*
* @param source - stream data source
*/
[noscript] void setDataSource(in StreamBufferSource source);
[noscript, notxpcom]
size_t SizeOfIncludingThisIfUnshared(in MallocSizeOf aMallocSizeOf);

Просмотреть файл

@ -24,15 +24,81 @@
#include "mozilla/ipc/InputStreamUtils.h"
#include "mozilla/Maybe.h"
#include "mozilla/ReentrantMonitor.h"
#include "mozilla/StreamBufferSource.h"
#include "nsIIPCSerializableInputStream.h"
#include "XPCOMModule.h"
using namespace mozilla::ipc;
using mozilla::fallible;
using mozilla::MakeRefPtr;
using mozilla::MallocSizeOf;
using mozilla::Maybe;
using mozilla::ReentrantMonitorAutoEnter;
using mozilla::Some;
using mozilla::Span;
using mozilla::StreamBufferSource;
//-----------------------------------------------------------------------------
// StreamBufferSource implementations
//-----------------------------------------------------------------------------
class nsTArraySource final : public StreamBufferSource {
public:
explicit nsTArraySource(nsTArray<uint8_t>&& aArray)
: mArray(std::move(aArray)) {}
Span<const char> Data() override {
return Span{reinterpret_cast<const char*>(mArray.Elements()),
mArray.Length()};
}
bool Owning() override { return true; }
size_t SizeOfExcludingThisEvenIfShared(MallocSizeOf aMallocSizeOf) override {
return mArray.ShallowSizeOfExcludingThis(aMallocSizeOf);
}
nsTArray<uint8_t> mArray;
};
class nsCStringSource final : public StreamBufferSource {
public:
nsCStringSource() = default;
Span<const char> Data() override { return mString; }
nsresult GetData(nsACString& aString) override {
if (!aString.Assign(mString, mozilla::fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
bool Owning() override { return true; }
size_t SizeOfExcludingThisIfUnshared(MallocSizeOf aMallocSizeOf) override {
return mString.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
}
size_t SizeOfExcludingThisEvenIfShared(MallocSizeOf aMallocSizeOf) override {
return mString.SizeOfExcludingThisEvenIfShared(aMallocSizeOf);
}
nsCString mString;
};
class nsBorrowedSource final : public StreamBufferSource {
public:
explicit nsBorrowedSource(Span<const char> aBuffer) : mBuffer(aBuffer) {}
Span<const char> Data() override { return mBuffer; }
bool Owning() override { return false; }
size_t SizeOfExcludingThisEvenIfShared(MallocSizeOf aMallocSizeOf) override {
return 0;
}
Span<const char> mBuffer;
};
//-----------------------------------------------------------------------------
// nsIStringInputStream implementation
@ -54,7 +120,7 @@ class nsStringInputStream final : public nsIStringInputStream,
NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
NS_DECL_NSICLONEABLEINPUTSTREAM
nsStringInputStream() : mOffset(0), mMon("nsStringInputStream") { Clear(); }
nsStringInputStream() = default;
nsresult Init(nsCString&& aString);
@ -67,51 +133,31 @@ class nsStringInputStream final : public nsIStringInputStream,
void SerializeInternal(InputStreamParams& aParams, bool aDelayedStart,
uint32_t aMaxSize, uint32_t* aSizeUsed, M* aManager);
uint32_t Length() const { return mData.Length(); }
size_t Length() const { return mSource ? mSource->Data().Length() : 0; }
uint32_t LengthRemaining() const { return Length() - mOffset; }
size_t LengthRemaining() const { return Length() - mOffset; }
void Clear() { mData.SetIsVoid(true); }
void Clear() { mSource = nullptr; }
bool Closed() { return mData.IsVoid(); }
bool Closed() { return !mSource; }
nsDependentCSubstring mData;
uint32_t mOffset;
RefPtr<StreamBufferSource> mSource;
size_t mOffset = 0;
// If we were initialized from an nsTArray, we store its data here.
Maybe<nsTArray<uint8_t>> mArray;
mozilla::ReentrantMonitor mMon;
mozilla::ReentrantMonitor mMon{"nsStringInputStream"};
};
nsresult nsStringInputStream::Init(nsCString&& aString) {
ReentrantMonitorAutoEnter lock(mMon);
mArray.reset();
if (!mData.Assign(std::move(aString), fallible)) {
auto source = MakeRefPtr<nsCStringSource>();
if (!source->mString.Assign(std::move(aString), fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
mOffset = 0;
return NS_OK;
return SetDataSource(source);
}
nsresult nsStringInputStream::Init(nsTArray<uint8_t>&& aArray) {
ReentrantMonitorAutoEnter lock(mMon);
mArray.reset();
mArray.emplace(std::move(aArray));
mOffset = 0;
if (mArray->IsEmpty()) {
// Not sure it's safe to Rebind() with a null pointer. Pretty
// sure it's not, in fact.
mData.Truncate();
} else {
mData.Rebind(reinterpret_cast<const char*>(mArray->Elements()),
mArray->Length());
}
return NS_OK;
auto source = MakeRefPtr<nsTArraySource>(std::move(aArray));
return SetDataSource(source);
}
// This class needs to support threadsafe refcounting since people often
@ -152,21 +198,16 @@ nsStringInputStream::GetData(nsACString& data) {
return NS_BASE_STREAM_CLOSED;
}
data.Assign(mData);
return NS_OK;
return mSource->GetData(data);
}
NS_IMETHODIMP
nsStringInputStream::SetData(const nsACString& aData) {
ReentrantMonitorAutoEnter lock(mMon);
mArray.reset();
if (NS_WARN_IF(!mData.Assign(aData, fallible))) {
auto source = MakeRefPtr<nsCStringSource>();
if (!source->mString.Assign(aData, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}
mOffset = 0;
return NS_OK;
return SetDataSource(source);
}
NS_IMETHODIMP
@ -181,19 +222,15 @@ nsStringInputStream::ToString(char** aResult) {
NS_IMETHODIMP
nsStringInputStream::SetData(const char* aData, int32_t aDataLen) {
ReentrantMonitorAutoEnter lock(mMon);
if (NS_WARN_IF(!aData)) {
return NS_ERROR_INVALID_ARG;
}
mArray.reset();
if (NS_WARN_IF(!mData.Assign(aData, aDataLen, fallible))) {
auto source = MakeRefPtr<nsCStringSource>();
if (NS_WARN_IF(!source->mString.Assign(aData, aDataLen, fallible))) {
return NS_ERROR_OUT_OF_MEMORY;
}
mOffset = 0;
return NS_OK;
return SetDataSource(source);
}
NS_IMETHODIMP
@ -203,31 +240,35 @@ nsStringInputStream::SetUTF8Data(const nsACString& aData) {
NS_IMETHODIMP
nsStringInputStream::AdoptData(char* aData, int32_t aDataLen) {
ReentrantMonitorAutoEnter lock(mMon);
if (NS_WARN_IF(!aData)) {
return NS_ERROR_INVALID_ARG;
}
mArray.reset();
mData.Adopt(aData, aDataLen);
mOffset = 0;
return NS_OK;
auto source = MakeRefPtr<nsCStringSource>();
source->mString.Adopt(aData, aDataLen);
return SetDataSource(source);
}
NS_IMETHODIMP
nsStringInputStream::ShareData(const char* aData, int32_t aDataLen) {
ReentrantMonitorAutoEnter lock(mMon);
if (NS_WARN_IF(!aData)) {
return NS_ERROR_INVALID_ARG;
}
mArray.reset();
if (aDataLen < 0) {
aDataLen = strlen(aData);
size_t length = aDataLen < 0 ? strlen(aData) : size_t(aDataLen);
auto source = MakeRefPtr<nsBorrowedSource>(Span{aData, length});
return SetDataSource(source);
}
NS_IMETHODIMP
nsStringInputStream::SetDataSource(StreamBufferSource* aSource) {
ReentrantMonitorAutoEnter lock(mMon);
if (NS_WARN_IF(!aSource)) {
return NS_ERROR_INVALID_ARG;
}
mData.Rebind(aData, aDataLen);
mSource = aSource;
mOffset = 0;
return NS_OK;
}
@ -237,7 +278,9 @@ nsStringInputStream::SizeOfIncludingThisIfUnshared(MallocSizeOf aMallocSizeOf) {
ReentrantMonitorAutoEnter lock(mMon);
size_t n = aMallocSizeOf(this);
n += mData.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
if (mSource) {
n += mSource->SizeOfIncludingThisIfUnshared(aMallocSizeOf);
}
return n;
}
@ -247,7 +290,9 @@ nsStringInputStream::SizeOfIncludingThisEvenIfShared(
ReentrantMonitorAutoEnter lock(mMon);
size_t n = aMallocSizeOf(this);
n += mData.SizeOfExcludingThisEvenIfShared(aMallocSizeOf);
if (mSource) {
n += mSource->SizeOfIncludingThisEvenIfShared(aMallocSizeOf);
}
return n;
}
@ -296,7 +341,7 @@ nsStringInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
}
// We may be at end-of-file
uint32_t maxCount = LengthRemaining();
size_t maxCount = LengthRemaining();
if (maxCount == 0) {
*aResult = 0;
return NS_OK;
@ -306,26 +351,24 @@ nsStringInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
aCount = maxCount;
}
nsDependentCSubstring tempData;
tempData.SetIsVoid(true);
if (mData.GetDataFlags() & nsACString::DataFlags::OWNED) {
tempData.Assign(std::move(mData));
mData.Rebind(tempData.BeginReading(), tempData.EndReading());
}
RefPtr<StreamBufferSource> source = mSource;
size_t offset = mOffset;
nsresult rv = aWriter(this, aClosure, mData.BeginReading() + mOffset, 0,
nsresult rv = aWriter(this, aClosure, source->Data().Elements() + offset, 0,
aCount, aResult);
if (!mData.IsVoid() && !tempData.IsVoid()) {
MOZ_DIAGNOSTIC_ASSERT(mData == tempData, "String was replaced!");
mData.SetIsVoid(true);
mData.Assign(std::move(tempData));
if (Closed()) {
NS_WARNING("nsStringInputStream was closed during ReadSegments");
return NS_OK;
}
MOZ_RELEASE_ASSERT(mSource == source, "String was replaced!");
MOZ_RELEASE_ASSERT(mOffset == offset, "Nested read operation!");
if (NS_SUCCEEDED(rv)) {
NS_ASSERTION(*aResult <= aCount,
"writer should not write more than we asked it to write");
mOffset += *aResult;
mOffset = offset + *aResult;
}
// errors returned from the writer end here!
@ -357,21 +400,21 @@ nsStringInputStream::Seek(int32_t aWhence, int64_t aOffset) {
case NS_SEEK_SET:
break;
case NS_SEEK_CUR:
newPos += mOffset;
newPos += (int64_t)mOffset;
break;
case NS_SEEK_END:
newPos += Length();
newPos += (int64_t)Length();
break;
default:
NS_ERROR("invalid aWhence");
return NS_ERROR_INVALID_ARG;
}
if (NS_WARN_IF(newPos < 0) || NS_WARN_IF(newPos > Length())) {
if (NS_WARN_IF(newPos < 0) || NS_WARN_IF(newPos > (int64_t)Length())) {
return NS_ERROR_INVALID_ARG;
}
mOffset = (uint32_t)newPos;
mOffset = (size_t)newPos;
return NS_OK;
}
@ -399,7 +442,7 @@ nsStringInputStream::Tell(int64_t* aOutWhere) {
return NS_BASE_STREAM_CLOSED;
}
*aOutWhere = mOffset;
*aOutWhere = (int64_t)mOffset;
return NS_OK;
}
@ -428,17 +471,18 @@ void nsStringInputStream::SerializeInternal(InputStreamParams& aParams,
uint32_t* aSizeUsed, M* aManager) {
ReentrantMonitorAutoEnter lock(mMon);
MOZ_DIAGNOSTIC_ASSERT(!Closed(), "cannot send a closed stream!");
MOZ_ASSERT(aSizeUsed);
*aSizeUsed = 0;
if (Length() >= aMaxSize) {
// If the input stream is non-owning (i.e. it was initialized with
// `ShareData`), request mutable access to `mData`, forcing our string to
// take ownership so that it doesn't go away while async copying.
if (!mArray && !(mData.GetDataFlags() & (nsCString::DataFlags::REFCOUNTED |
nsCString::DataFlags::OWNED |
nsCString::DataFlags::LITERAL))) {
mData.BeginWriting();
// `ShareData`), create a new owning source so that it doesn't go away while
// async copying.
if (!mSource->Owning()) {
auto source = MakeRefPtr<nsCStringSource>();
source->mString.Assign(nsDependentCSubstring{mSource->Data()});
mSource = source;
}
InputStreamHelper::SerializeInputStreamAsPipe(this, aParams, aDelayedStart,
@ -449,7 +493,7 @@ void nsStringInputStream::SerializeInternal(InputStreamParams& aParams,
*aSizeUsed = Length();
StringInputStreamParams params;
params.data() = PromiseFlatCString(mData);
mSource->GetData(params.data());
aParams = params;
}
@ -485,9 +529,14 @@ nsStringInputStream::Clone(nsIInputStream** aCloneOut) {
ReentrantMonitorAutoEnter lock(mMon);
RefPtr<nsStringInputStream> ref = new nsStringInputStream();
nsresult rv = ref->SetData(mData);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
if (mSource && !mSource->Owning()) {
auto data = mSource->Data();
nsresult rv = ref->SetData(data.Elements(), data.Length());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
} else {
ref->mSource = mSource;
}
// mOffset is overwritten by SetData().
@ -544,6 +593,21 @@ nsresult NS_NewByteInputStream(nsIInputStream** aStreamResult,
return NS_OK;
}
extern nsresult NS_NewByteInputStream(nsIInputStream** aStreamResult,
mozilla::StreamBufferSource* aSource) {
MOZ_ASSERT(aStreamResult, "null out ptr");
RefPtr<nsStringInputStream> stream = new nsStringInputStream();
nsresult rv = stream->SetDataSource(aSource);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
stream.forget(aStreamResult);
return NS_OK;
}
nsresult NS_NewCStringInputStream(nsIInputStream** aStreamResult,
const nsACString& aStringToRead) {
MOZ_ASSERT(aStreamResult, "null out ptr");

Просмотреть файл

@ -59,6 +59,16 @@ extern nsresult NS_NewByteInputStream(nsIInputStream** aStreamResult,
extern nsresult NS_NewByteInputStream(nsIInputStream** aStreamResult,
nsTArray<uint8_t>&& aArray);
/**
* Factory method to get an nsIInputStream from an arbitrary StreamBufferSource.
* This will take a strong reference to the source.
*
* Result will implement nsIStringInputStream, nsITellableStream and
* nsISeekableStream.
*/
extern nsresult NS_NewByteInputStream(nsIInputStream** aStreamResult,
mozilla::StreamBufferSource* aSource);
/**
* Factory method to get an nsInputStream from an nsACString. Result will
* implement nsIStringInputStream, nsTellableStream and nsISeekableStream.