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"
25 namespace mozilla::dom
{
27 NS_IMPL_CYCLE_COLLECTION_CLASS(Blob
)
29 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Blob
)
30 NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal
)
31 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
32 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
33 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
35 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Blob
)
36 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal
)
37 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
39 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Blob
)
40 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
41 NS_IMPL_CYCLE_COLLECTION_TRACE_END
43 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Blob
)
44 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
45 NS_INTERFACE_MAP_ENTRY(nsISupports
)
46 NS_INTERFACE_MAP_ENTRY_CONCRETE(Blob
)
47 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
50 NS_IMPL_CYCLE_COLLECTING_ADDREF(Blob
)
51 NS_IMPL_CYCLE_COLLECTING_RELEASE(Blob
)
53 void Blob::MakeValidBlobType(nsAString
& aType
) {
54 char16_t
* iter
= aType
.BeginWriting();
55 char16_t
* end
= aType
.EndWriting();
57 for (; iter
!= end
; ++iter
) {
59 if (c
< 0x20 || c
> 0x7E) {
60 // Non-ASCII char, bail out.
65 if (c
>= 'A' && c
<= 'Z') {
66 *iter
= c
+ ('a' - 'A');
72 Blob
* Blob::Create(nsIGlobalObject
* aGlobal
, BlobImpl
* aImpl
) {
76 if (NS_WARN_IF(!aGlobal
)) {
80 return aImpl
->IsFile() ? new File(aGlobal
, aImpl
) : new Blob(aGlobal
, aImpl
);
84 already_AddRefed
<Blob
> Blob::CreateStringBlob(nsIGlobalObject
* aGlobal
,
85 const nsACString
& aData
,
86 const nsAString
& aContentType
) {
88 if (NS_WARN_IF(!aGlobal
)) {
92 RefPtr
<BlobImpl
> blobImpl
= StringBlobImpl::Create(aData
, aContentType
);
93 RefPtr
<Blob
> blob
= Blob::Create(aGlobal
, blobImpl
);
94 MOZ_ASSERT(!blob
->mImpl
->IsFile());
99 already_AddRefed
<Blob
> Blob::CreateMemoryBlob(nsIGlobalObject
* aGlobal
,
102 const nsAString
& aContentType
) {
104 if (NS_WARN_IF(!aGlobal
)) {
108 RefPtr
<Blob
> blob
= Blob::Create(
109 aGlobal
, new MemoryBlobImpl(aMemoryBuffer
, aLength
, aContentType
));
110 MOZ_ASSERT(!blob
->mImpl
->IsFile());
111 return blob
.forget();
114 Blob::Blob(nsIGlobalObject
* aGlobal
, BlobImpl
* aImpl
)
115 : mImpl(aImpl
), mGlobal(aGlobal
) {
120 Blob::~Blob() = default;
122 bool Blob::IsFile() const { return mImpl
->IsFile(); }
124 const nsTArray
<RefPtr
<BlobImpl
>>* Blob::GetSubBlobImpls() const {
125 return mImpl
->GetSubBlobImpls();
128 already_AddRefed
<File
> Blob::ToFile() {
129 if (!mImpl
->IsFile()) {
134 if (HasFileInterface()) {
135 file
= static_cast<File
*>(this);
137 file
= new File(mGlobal
, mImpl
);
140 return file
.forget();
143 already_AddRefed
<File
> Blob::ToFile(const nsAString
& aName
,
144 ErrorResult
& aRv
) const {
145 AutoTArray
<RefPtr
<BlobImpl
>, 1> blobImpls({mImpl
});
147 nsAutoString contentType
;
148 mImpl
->GetType(contentType
);
150 RefPtr
<MultipartBlobImpl
> impl
=
151 MultipartBlobImpl::Create(std::move(blobImpls
), aName
, contentType
,
152 mGlobal
->GetRTPCallerType(), aRv
);
153 if (NS_WARN_IF(aRv
.Failed())) {
157 RefPtr
<File
> file
= new File(mGlobal
, impl
);
158 return file
.forget();
161 already_AddRefed
<Blob
> Blob::CreateSlice(uint64_t aStart
, uint64_t aLength
,
162 const nsAString
& aContentType
,
163 ErrorResult
& aRv
) const {
164 RefPtr
<BlobImpl
> impl
=
165 mImpl
->CreateSlice(aStart
, aLength
, aContentType
, aRv
);
170 RefPtr
<Blob
> blob
= Blob::Create(mGlobal
, impl
);
171 return blob
.forget();
174 uint64_t Blob::GetSize(ErrorResult
& aRv
) { return mImpl
->GetSize(aRv
); }
176 void Blob::GetType(nsAString
& aType
) { mImpl
->GetType(aType
); }
178 void Blob::GetBlobImplType(nsAString
& aBlobImplType
) {
179 mImpl
->GetBlobImplType(aBlobImplType
);
182 already_AddRefed
<Blob
> Blob::Slice(const Optional
<int64_t>& aStart
,
183 const Optional
<int64_t>& aEnd
,
184 const Optional
<nsAString
>& aContentType
,
186 nsAutoString contentType
;
187 if (aContentType
.WasPassed()) {
188 contentType
= aContentType
.Value();
191 RefPtr
<BlobImpl
> impl
= mImpl
->Slice(aStart
, aEnd
, contentType
, aRv
);
196 RefPtr
<Blob
> blob
= Blob::Create(mGlobal
, impl
);
197 return blob
.forget();
200 size_t Blob::GetAllocationSize() const { return mImpl
->GetAllocationSize(); }
202 // contentTypeWithCharset can be set to the contentType or
203 // contentType+charset based on what the spec says.
204 // See: https://fetch.spec.whatwg.org/#concept-bodyinit-extract
205 nsresult
Blob::GetSendInfo(nsIInputStream
** aBody
, uint64_t* aContentLength
,
206 nsACString
& aContentType
,
207 nsACString
& aCharset
) const {
208 return mImpl
->GetSendInfo(aBody
, aContentLength
, aContentType
, aCharset
);
211 JSObject
* Blob::WrapObject(JSContext
* aCx
, JS::Handle
<JSObject
*> aGivenProto
) {
212 return Blob_Binding::Wrap(aCx
, this, aGivenProto
);
216 already_AddRefed
<Blob
> Blob::Constructor(
217 const GlobalObject
& aGlobal
, const Optional
<Sequence
<BlobPart
>>& aData
,
218 const BlobPropertyBag
& aBag
, ErrorResult
& aRv
) {
219 RefPtr
<MultipartBlobImpl
> impl
= new MultipartBlobImpl();
221 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(aGlobal
.GetAsSupports());
223 if (aData
.WasPassed()) {
224 nsAutoString
type(aBag
.mType
);
225 MakeValidBlobType(type
);
226 impl
->InitializeBlob(aData
.Value(), type
,
227 aBag
.mEndings
== EndingType::Native
,
228 global
->GetRTPCallerType(), aRv
);
230 impl
->InitializeBlob(global
->GetRTPCallerType(), aRv
);
233 if (NS_WARN_IF(aRv
.Failed())) {
237 MOZ_ASSERT(!impl
->IsFile());
239 RefPtr
<Blob
> blob
= Blob::Create(global
, impl
);
240 return blob
.forget();
243 int64_t Blob::GetFileId() const { return mImpl
->GetFileId(); }
245 bool Blob::IsMemoryFile() const { return mImpl
->IsMemoryFile(); }
247 void Blob::CreateInputStream(nsIInputStream
** aStream
, ErrorResult
& aRv
) const {
248 mImpl
->CreateInputStream(aStream
, aRv
);
251 size_t BindingJSObjectMallocBytes(Blob
* aBlob
) {
254 // TODO: The hazard analysis currently can't see that none of the
255 // implementations of the GetAllocationSize virtual method call can GC (see
257 JS::AutoSuppressGCAnalysis nogc
;
259 return aBlob
->GetAllocationSize();
262 already_AddRefed
<Promise
> Blob::Text(ErrorResult
& aRv
) const {
263 return ConsumeBody(BodyConsumer::CONSUME_TEXT
, aRv
);
266 already_AddRefed
<Promise
> Blob::ArrayBuffer(ErrorResult
& aRv
) const {
267 return ConsumeBody(BodyConsumer::CONSUME_ARRAYBUFFER
, aRv
);
270 already_AddRefed
<Promise
> Blob::ConsumeBody(
271 BodyConsumer::ConsumeType aConsumeType
, ErrorResult
& aRv
) const {
272 if (NS_WARN_IF(!mGlobal
)) {
273 aRv
.Throw(NS_ERROR_FAILURE
);
277 nsCOMPtr
<nsISerialEventTarget
> mainThreadEventTarget
;
278 if (!NS_IsMainThread()) {
279 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
280 MOZ_ASSERT(workerPrivate
);
281 mainThreadEventTarget
= workerPrivate
->MainThreadEventTarget();
283 mainThreadEventTarget
= mGlobal
->EventTargetFor(TaskCategory::Other
);
286 MOZ_ASSERT(mainThreadEventTarget
);
288 nsCOMPtr
<nsIInputStream
> inputStream
;
289 CreateInputStream(getter_AddRefs(inputStream
), aRv
);
290 if (NS_WARN_IF(aRv
.Failed())) {
294 return BodyConsumer::Create(mGlobal
, mainThreadEventTarget
, inputStream
,
295 nullptr, aConsumeType
, VoidCString(),
296 VoidString(), VoidCString(), VoidCString(),
297 MutableBlobStorage::eOnlyInMemory
, aRv
);
300 // https://w3c.github.io/FileAPI/#stream-method-algo
301 // "The stream() method, when invoked, must return the result of calling get
303 // And that's https://w3c.github.io/FileAPI/#blob-get-stream.
304 already_AddRefed
<ReadableStream
> Blob::Stream(JSContext
* aCx
,
305 ErrorResult
& aRv
) const {
306 nsCOMPtr
<nsIInputStream
> stream
;
307 CreateInputStream(getter_AddRefs(stream
), aRv
);
308 if (NS_WARN_IF(aRv
.Failed())) {
312 if (NS_WARN_IF(!mGlobal
)) {
313 aRv
.Throw(NS_ERROR_FAILURE
);
318 MakeRefPtr
<NonAsyncInputToReadableStreamAlgorithms
>(*stream
);
320 // Step 1: Let stream be a new ReadableStream created in blob’s relevant
322 // Step 2: Set up stream with byte reading support.
324 // (The spec here does not define pullAlgorithm and instead greedily enqueues
325 // everything into the stream when .stream() is called, but here we only reads
326 // the data when actual read request happens, via
327 // InputToReadableStreamAlgorithms. See
328 // https://github.com/w3c/FileAPI/issues/194.)
329 RefPtr
<ReadableStream
> body
= ReadableStream::CreateByteNative(
330 aCx
, mGlobal
, *algorithms
, Nothing(), aRv
);
335 return body
.forget();
338 } // namespace mozilla::dom