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 #include "EmptyBlobImpl.h"
10 #include "MemoryBlobImpl.h"
11 #include "mozilla/dom/BlobBinding.h"
12 #include "mozilla/dom/ReadableStream.h"
13 #include "mozilla/dom/WorkerCommon.h"
14 #include "mozilla/dom/WorkerPrivate.h"
15 #include "mozilla/HoldDropJSObjects.h"
16 #include "MultipartBlobImpl.h"
17 #include "nsCycleCollectionParticipant.h"
18 #include "nsIGlobalObject.h"
19 #include "nsIInputStream.h"
20 #include "nsPIDOMWindow.h"
21 #include "StreamBlobImpl.h"
22 #include "StringBlobImpl.h"
24 namespace mozilla::dom
{
26 NS_IMPL_CYCLE_COLLECTION_CLASS(Blob
)
28 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Blob
)
29 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal
)
30 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
31 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
32 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
34 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Blob
)
35 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal
)
36 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
38 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Blob
)
39 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
40 NS_IMPL_CYCLE_COLLECTION_TRACE_END
42 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Blob
)
43 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
44 NS_INTERFACE_MAP_ENTRY(nsISupports
)
45 NS_INTERFACE_MAP_ENTRY_CONCRETE(Blob
)
46 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
49 NS_IMPL_CYCLE_COLLECTING_ADDREF(Blob
)
50 NS_IMPL_CYCLE_COLLECTING_RELEASE(Blob
)
52 void Blob::MakeValidBlobType(nsAString
& aType
) {
53 char16_t
* iter
= aType
.BeginWriting();
54 char16_t
* end
= aType
.EndWriting();
56 for (; iter
!= end
; ++iter
) {
58 if (c
< 0x20 || c
> 0x7E) {
59 // Non-ASCII char, bail out.
64 if (c
>= 'A' && c
<= 'Z') {
65 *iter
= c
+ ('a' - 'A');
71 Blob
* Blob::Create(nsIGlobalObject
* aGlobal
, BlobImpl
* aImpl
) {
75 if (NS_WARN_IF(!aGlobal
)) {
79 return aImpl
->IsFile() ? new File(aGlobal
, aImpl
) : new Blob(aGlobal
, aImpl
);
83 already_AddRefed
<Blob
> Blob::CreateStringBlob(nsIGlobalObject
* aGlobal
,
84 const nsACString
& aData
,
85 const nsAString
& aContentType
) {
87 if (NS_WARN_IF(!aGlobal
)) {
91 RefPtr
<BlobImpl
> blobImpl
= StringBlobImpl::Create(aData
, aContentType
);
92 RefPtr
<Blob
> blob
= Blob::Create(aGlobal
, blobImpl
);
93 MOZ_ASSERT(!blob
->mImpl
->IsFile());
98 already_AddRefed
<Blob
> Blob::CreateMemoryBlob(nsIGlobalObject
* aGlobal
,
101 const nsAString
& aContentType
) {
103 if (NS_WARN_IF(!aGlobal
)) {
107 RefPtr
<Blob
> blob
= Blob::Create(
108 aGlobal
, new MemoryBlobImpl(aMemoryBuffer
, aLength
, aContentType
));
109 MOZ_ASSERT(!blob
->mImpl
->IsFile());
110 return blob
.forget();
113 Blob::Blob(nsIGlobalObject
* aGlobal
, BlobImpl
* aImpl
)
114 : mImpl(aImpl
), mGlobal(aGlobal
) {
119 Blob::~Blob() = default;
121 bool Blob::IsFile() const { return mImpl
->IsFile(); }
123 const nsTArray
<RefPtr
<BlobImpl
>>* Blob::GetSubBlobImpls() const {
124 return mImpl
->GetSubBlobImpls();
127 already_AddRefed
<File
> Blob::ToFile() {
128 if (!mImpl
->IsFile()) {
133 if (HasFileInterface()) {
134 file
= static_cast<File
*>(this);
136 file
= new File(mGlobal
, mImpl
);
139 return file
.forget();
142 already_AddRefed
<File
> Blob::ToFile(const nsAString
& aName
,
143 ErrorResult
& aRv
) const {
144 AutoTArray
<RefPtr
<BlobImpl
>, 1> blobImpls({mImpl
});
146 nsAutoString contentType
;
147 mImpl
->GetType(contentType
);
149 RefPtr
<MultipartBlobImpl
> impl
=
150 MultipartBlobImpl::Create(std::move(blobImpls
), aName
, contentType
,
151 mGlobal
->GetRTPCallerType(), aRv
);
152 if (NS_WARN_IF(aRv
.Failed())) {
156 RefPtr
<File
> file
= new File(mGlobal
, impl
);
157 return file
.forget();
160 already_AddRefed
<Blob
> Blob::CreateSlice(uint64_t aStart
, uint64_t aLength
,
161 const nsAString
& aContentType
,
162 ErrorResult
& aRv
) const {
163 RefPtr
<BlobImpl
> impl
=
164 mImpl
->CreateSlice(aStart
, aLength
, aContentType
, aRv
);
169 RefPtr
<Blob
> blob
= Blob::Create(mGlobal
, impl
);
170 return blob
.forget();
173 uint64_t Blob::GetSize(ErrorResult
& aRv
) { return mImpl
->GetSize(aRv
); }
175 void Blob::GetType(nsAString
& aType
) { mImpl
->GetType(aType
); }
177 void Blob::GetBlobImplType(nsAString
& aBlobImplType
) {
178 mImpl
->GetBlobImplType(aBlobImplType
);
181 already_AddRefed
<Blob
> Blob::Slice(const Optional
<int64_t>& aStart
,
182 const Optional
<int64_t>& aEnd
,
183 const Optional
<nsAString
>& aContentType
,
185 nsAutoString contentType
;
186 if (aContentType
.WasPassed()) {
187 contentType
= aContentType
.Value();
190 RefPtr
<BlobImpl
> impl
= mImpl
->Slice(aStart
, aEnd
, contentType
, aRv
);
195 RefPtr
<Blob
> blob
= Blob::Create(mGlobal
, impl
);
196 return blob
.forget();
199 size_t Blob::GetAllocationSize() const { return mImpl
->GetAllocationSize(); }
201 // contentTypeWithCharset can be set to the contentType or
202 // contentType+charset based on what the spec says.
203 // See: https://fetch.spec.whatwg.org/#concept-bodyinit-extract
204 nsresult
Blob::GetSendInfo(nsIInputStream
** aBody
, uint64_t* aContentLength
,
205 nsACString
& aContentType
,
206 nsACString
& aCharset
) const {
207 return mImpl
->GetSendInfo(aBody
, aContentLength
, aContentType
, aCharset
);
210 JSObject
* Blob::WrapObject(JSContext
* aCx
, JS::Handle
<JSObject
*> aGivenProto
) {
211 return Blob_Binding::Wrap(aCx
, this, aGivenProto
);
215 already_AddRefed
<Blob
> Blob::Constructor(
216 const GlobalObject
& aGlobal
, const Optional
<Sequence
<BlobPart
>>& aData
,
217 const BlobPropertyBag
& aBag
, ErrorResult
& aRv
) {
218 RefPtr
<MultipartBlobImpl
> impl
= new MultipartBlobImpl();
220 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(aGlobal
.GetAsSupports());
222 if (aData
.WasPassed()) {
223 nsAutoString
type(aBag
.mType
);
224 MakeValidBlobType(type
);
225 impl
->InitializeBlob(aData
.Value(), type
,
226 aBag
.mEndings
== EndingType::Native
,
227 global
->GetRTPCallerType(), aRv
);
229 impl
->InitializeBlob(global
->GetRTPCallerType(), aRv
);
232 if (NS_WARN_IF(aRv
.Failed())) {
236 MOZ_ASSERT(!impl
->IsFile());
238 RefPtr
<Blob
> blob
= Blob::Create(global
, impl
);
239 return blob
.forget();
242 int64_t Blob::GetFileId() const { return mImpl
->GetFileId(); }
244 bool Blob::IsMemoryFile() const { return mImpl
->IsMemoryFile(); }
246 void Blob::CreateInputStream(nsIInputStream
** aStream
, ErrorResult
& aRv
) const {
247 mImpl
->CreateInputStream(aStream
, aRv
);
250 size_t BindingJSObjectMallocBytes(Blob
* aBlob
) {
253 return aBlob
->GetAllocationSize();
256 already_AddRefed
<Promise
> Blob::Text(ErrorResult
& aRv
) const {
257 return ConsumeBody(BodyConsumer::CONSUME_TEXT
, aRv
);
260 already_AddRefed
<Promise
> Blob::ArrayBuffer(ErrorResult
& aRv
) const {
261 return ConsumeBody(BodyConsumer::CONSUME_ARRAYBUFFER
, aRv
);
264 already_AddRefed
<Promise
> Blob::ConsumeBody(
265 BodyConsumer::ConsumeType aConsumeType
, ErrorResult
& aRv
) const {
266 if (NS_WARN_IF(!mGlobal
)) {
267 aRv
.Throw(NS_ERROR_FAILURE
);
271 nsCOMPtr
<nsISerialEventTarget
> mainThreadEventTarget
;
272 if (!NS_IsMainThread()) {
273 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
274 MOZ_ASSERT(workerPrivate
);
275 mainThreadEventTarget
= workerPrivate
->MainThreadEventTarget();
277 mainThreadEventTarget
= GetMainThreadSerialEventTarget();
280 MOZ_ASSERT(mainThreadEventTarget
);
282 nsCOMPtr
<nsIInputStream
> inputStream
;
283 CreateInputStream(getter_AddRefs(inputStream
), aRv
);
284 if (NS_WARN_IF(aRv
.Failed())) {
288 return BodyConsumer::Create(mGlobal
, mainThreadEventTarget
, inputStream
,
289 nullptr, aConsumeType
, VoidCString(),
290 VoidString(), VoidCString(), VoidCString(),
291 MutableBlobStorage::eOnlyInMemory
, aRv
);
294 // https://w3c.github.io/FileAPI/#stream-method-algo
295 // "The stream() method, when invoked, must return the result of calling get
297 // And that's https://w3c.github.io/FileAPI/#blob-get-stream.
298 already_AddRefed
<ReadableStream
> Blob::Stream(JSContext
* aCx
,
299 ErrorResult
& aRv
) const {
300 nsCOMPtr
<nsIInputStream
> stream
;
301 CreateInputStream(getter_AddRefs(stream
), aRv
);
302 if (NS_WARN_IF(aRv
.Failed())) {
306 if (NS_WARN_IF(!mGlobal
)) {
307 aRv
.Throw(NS_ERROR_FAILURE
);
312 MakeRefPtr
<NonAsyncInputToReadableStreamAlgorithms
>(*stream
);
314 // Step 1: Let stream be a new ReadableStream created in blob’s relevant
316 // Step 2: Set up stream with byte reading support.
318 // (The spec here does not define pullAlgorithm and instead greedily enqueues
319 // everything into the stream when .stream() is called, but here we only reads
320 // the data when actual read request happens, via
321 // InputToReadableStreamAlgorithms. See
322 // https://github.com/w3c/FileAPI/issues/194.)
323 RefPtr
<ReadableStream
> body
= ReadableStream::CreateByteNative(
324 aCx
, mGlobal
, *algorithms
, Nothing(), aRv
);
329 return body
.forget();
332 } // namespace mozilla::dom