Bug 1850713: remove duplicated setting of early hint preloader id in `ScriptLoader...
[gecko.git] / dom / file / Blob.cpp
blobdc3a70c74c848f3bcf2f2c7fd3653cbbd9f0dade
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/. */
7 #include "Blob.h"
8 #include "EmptyBlobImpl.h"
9 #include "File.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"
23 #include "js/GCAPI.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)
48 NS_INTERFACE_MAP_END
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) {
58 char16_t c = *iter;
59 if (c < 0x20 || c > 0x7E) {
60 // Non-ASCII char, bail out.
61 aType.Truncate();
62 return;
65 if (c >= 'A' && c <= 'Z') {
66 *iter = c + ('a' - 'A');
71 /* static */
72 Blob* Blob::Create(nsIGlobalObject* aGlobal, BlobImpl* aImpl) {
73 MOZ_ASSERT(aImpl);
75 MOZ_ASSERT(aGlobal);
76 if (NS_WARN_IF(!aGlobal)) {
77 return nullptr;
80 return aImpl->IsFile() ? new File(aGlobal, aImpl) : new Blob(aGlobal, aImpl);
83 /* static */
84 already_AddRefed<Blob> Blob::CreateStringBlob(nsIGlobalObject* aGlobal,
85 const nsACString& aData,
86 const nsAString& aContentType) {
87 MOZ_ASSERT(aGlobal);
88 if (NS_WARN_IF(!aGlobal)) {
89 return nullptr;
92 RefPtr<BlobImpl> blobImpl = StringBlobImpl::Create(aData, aContentType);
93 RefPtr<Blob> blob = Blob::Create(aGlobal, blobImpl);
94 MOZ_ASSERT(!blob->mImpl->IsFile());
95 return blob.forget();
98 /* static */
99 already_AddRefed<Blob> Blob::CreateMemoryBlob(nsIGlobalObject* aGlobal,
100 void* aMemoryBuffer,
101 uint64_t aLength,
102 const nsAString& aContentType) {
103 MOZ_ASSERT(aGlobal);
104 if (NS_WARN_IF(!aGlobal)) {
105 return nullptr;
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) {
116 MOZ_ASSERT(mImpl);
117 MOZ_ASSERT(mGlobal);
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()) {
130 return nullptr;
133 RefPtr<File> file;
134 if (HasFileInterface()) {
135 file = static_cast<File*>(this);
136 } else {
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())) {
154 return nullptr;
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);
166 if (aRv.Failed()) {
167 return nullptr;
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,
185 ErrorResult& aRv) {
186 nsAutoString contentType;
187 if (aContentType.WasPassed()) {
188 contentType = aContentType.Value();
191 RefPtr<BlobImpl> impl = mImpl->Slice(aStart, aEnd, contentType, aRv);
192 if (aRv.Failed()) {
193 return nullptr;
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);
215 /* static */
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());
222 MOZ_ASSERT(global);
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);
229 } else {
230 impl->InitializeBlob(global->GetRTPCallerType(), aRv);
233 if (NS_WARN_IF(aRv.Failed())) {
234 return nullptr;
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) {
252 MOZ_ASSERT(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
256 // bug 1531951).
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);
274 return nullptr;
277 nsCOMPtr<nsISerialEventTarget> mainThreadEventTarget;
278 if (!NS_IsMainThread()) {
279 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
280 MOZ_ASSERT(workerPrivate);
281 mainThreadEventTarget = workerPrivate->MainThreadEventTarget();
282 } else {
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())) {
291 return nullptr;
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
302 // stream on this."
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())) {
309 return nullptr;
312 if (NS_WARN_IF(!mGlobal)) {
313 aRv.Throw(NS_ERROR_FAILURE);
314 return nullptr;
317 auto algorithms =
318 MakeRefPtr<NonAsyncInputToReadableStreamAlgorithms>(*stream);
320 // Step 1: Let stream be a new ReadableStream created in blob’s relevant
321 // Realm.
322 // Step 2: Set up stream with byte reading support.
323 // Step 3: ...
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);
331 if (aRv.Failed()) {
332 return nullptr;
335 return body.forget();
338 } // namespace mozilla::dom