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 "MultipartBlobImpl.h"
8 #include "jsfriendapi.h"
9 #include "mozilla/dom/BlobSet.h"
10 #include "mozilla/dom/FileBinding.h"
11 #include "mozilla/dom/UnionTypes.h"
12 #include "nsIMultiplexInputStream.h"
13 #include "nsRFPService.h"
14 #include "nsStringStream.h"
16 #include "nsJSUtils.h"
17 #include "nsContentUtils.h"
20 using namespace mozilla
;
21 using namespace mozilla::dom
;
24 already_AddRefed
<MultipartBlobImpl
> MultipartBlobImpl::Create(
25 nsTArray
<RefPtr
<BlobImpl
>>&& aBlobImpls
, const nsAString
& aName
,
26 const nsAString
& aContentType
, bool aCrossOriginIsolated
,
28 RefPtr
<MultipartBlobImpl
> blobImpl
=
29 new MultipartBlobImpl(std::move(aBlobImpls
), aName
, aContentType
);
30 blobImpl
->SetLengthAndModifiedDate(Some(aCrossOriginIsolated
), aRv
);
31 if (NS_WARN_IF(aRv
.Failed())) {
35 return blobImpl
.forget();
39 already_AddRefed
<MultipartBlobImpl
> MultipartBlobImpl::Create(
40 nsTArray
<RefPtr
<BlobImpl
>>&& aBlobImpls
, const nsAString
& aContentType
,
42 RefPtr
<MultipartBlobImpl
> blobImpl
=
43 new MultipartBlobImpl(std::move(aBlobImpls
), aContentType
);
44 blobImpl
->SetLengthAndModifiedDate(/* aCrossOriginIsolated */ Nothing(), aRv
);
45 if (NS_WARN_IF(aRv
.Failed())) {
49 return blobImpl
.forget();
52 void MultipartBlobImpl::CreateInputStream(nsIInputStream
** aStream
,
56 uint32_t length
= mBlobImpls
.Length();
57 if (length
== 0 || mLength
== 0) {
58 aRv
= NS_NewCStringInputStream(aStream
, EmptyCString());
63 BlobImpl
* blobImpl
= mBlobImpls
.ElementAt(0);
64 blobImpl
->CreateInputStream(aStream
, aRv
);
68 nsCOMPtr
<nsIMultiplexInputStream
> stream
=
69 do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
70 if (NS_WARN_IF(!stream
)) {
71 aRv
.Throw(NS_ERROR_FAILURE
);
76 for (i
= 0; i
< length
; i
++) {
77 nsCOMPtr
<nsIInputStream
> scratchStream
;
78 BlobImpl
* blobImpl
= mBlobImpls
.ElementAt(i
).get();
80 // nsIMultiplexInputStream doesn't work well with empty sub streams. Let's
81 // skip the empty blobs.
82 uint32_t size
= blobImpl
->GetSize(aRv
);
83 if (NS_WARN_IF(aRv
.Failed())) {
91 blobImpl
->CreateInputStream(getter_AddRefs(scratchStream
), aRv
);
92 if (NS_WARN_IF(aRv
.Failed())) {
96 aRv
= stream
->AppendStream(scratchStream
);
97 if (NS_WARN_IF(aRv
.Failed())) {
102 CallQueryInterface(stream
, aStream
);
105 already_AddRefed
<BlobImpl
> MultipartBlobImpl::CreateSlice(
106 uint64_t aStart
, uint64_t aLength
, const nsAString
& aContentType
,
108 // If we clamped to nothing we create an empty blob
109 nsTArray
<RefPtr
<BlobImpl
>> blobImpls
;
111 uint64_t length
= aLength
;
112 uint64_t skipStart
= aStart
;
114 // Prune the list of blobs if we can
116 for (i
= 0; length
&& skipStart
&& i
< mBlobImpls
.Length(); i
++) {
117 BlobImpl
* blobImpl
= mBlobImpls
[i
].get();
119 uint64_t l
= blobImpl
->GetSize(aRv
);
120 if (NS_WARN_IF(aRv
.Failed())) {
125 uint64_t upperBound
= std::min
<uint64_t>(l
- skipStart
, length
);
127 RefPtr
<BlobImpl
> firstBlobImpl
=
128 blobImpl
->CreateSlice(skipStart
, upperBound
, aContentType
, aRv
);
129 if (NS_WARN_IF(aRv
.Failed())) {
133 // Avoid wrapping a single blob inside an MultipartBlobImpl
134 if (length
== upperBound
) {
135 return firstBlobImpl
.forget();
138 blobImpls
.AppendElement(firstBlobImpl
);
139 length
-= upperBound
;
146 // Now append enough blobs until we're done
147 for (; length
&& i
< mBlobImpls
.Length(); i
++) {
148 BlobImpl
* blobImpl
= mBlobImpls
[i
].get();
150 uint64_t l
= blobImpl
->GetSize(aRv
);
151 if (NS_WARN_IF(aRv
.Failed())) {
156 RefPtr
<BlobImpl
> lastBlobImpl
=
157 blobImpl
->CreateSlice(0, length
, aContentType
, aRv
);
158 if (NS_WARN_IF(aRv
.Failed())) {
162 blobImpls
.AppendElement(lastBlobImpl
);
164 blobImpls
.AppendElement(blobImpl
);
166 length
-= std::min
<uint64_t>(l
, length
);
169 // we can create our blob now
170 RefPtr
<BlobImpl
> impl
= Create(std::move(blobImpls
), aContentType
, aRv
);
171 if (NS_WARN_IF(aRv
.Failed())) {
175 return impl
.forget();
178 void MultipartBlobImpl::InitializeBlob(bool aCrossOriginIsolated
,
180 SetLengthAndModifiedDate(Some(aCrossOriginIsolated
), aRv
);
181 NS_WARNING_ASSERTION(!aRv
.Failed(), "SetLengthAndModifiedDate failed");
184 void MultipartBlobImpl::InitializeBlob(const Sequence
<Blob::BlobPart
>& aData
,
185 const nsAString
& aContentType
,
187 bool aCrossOriginIsolated
,
189 mContentType
= aContentType
;
192 for (uint32_t i
= 0, len
= aData
.Length(); i
< len
; ++i
) {
193 const Blob::BlobPart
& data
= aData
[i
];
196 RefPtr
<Blob
> blob
= data
.GetAsBlob().get();
197 aRv
= blobSet
.AppendBlobImpl(blob
->Impl());
203 else if (data
.IsUSVString()) {
204 aRv
= blobSet
.AppendString(data
.GetAsUSVString(), aNativeEOL
);
210 else if (data
.IsArrayBuffer()) {
211 const ArrayBuffer
& buffer
= data
.GetAsArrayBuffer();
212 buffer
.ComputeState();
213 aRv
= blobSet
.AppendVoidPtr(buffer
.Data(), buffer
.Length());
219 else if (data
.IsArrayBufferView()) {
220 const ArrayBufferView
& buffer
= data
.GetAsArrayBufferView();
221 buffer
.ComputeState();
222 aRv
= blobSet
.AppendVoidPtr(buffer
.Data(), buffer
.Length());
229 MOZ_CRASH("Impossible blob data type.");
233 mBlobImpls
= blobSet
.GetBlobImpls();
234 SetLengthAndModifiedDate(Some(aCrossOriginIsolated
), aRv
);
235 NS_WARNING_ASSERTION(!aRv
.Failed(), "SetLengthAndModifiedDate failed");
238 void MultipartBlobImpl::SetLengthAndModifiedDate(
239 const Maybe
<bool>& aCrossOriginIsolated
, ErrorResult
& aRv
) {
240 MOZ_ASSERT(mLength
== MULTIPARTBLOBIMPL_UNKNOWN_LENGTH
);
241 MOZ_ASSERT_IF(mIsFile
, IsLastModificationDateUnset());
243 uint64_t totalLength
= 0;
244 int64_t lastModified
= 0;
245 bool lastModifiedSet
= false;
247 for (uint32_t index
= 0, count
= mBlobImpls
.Length(); index
< count
;
249 RefPtr
<BlobImpl
>& blob
= mBlobImpls
[index
];
251 uint64_t subBlobLength
= blob
->GetSize(aRv
);
252 if (NS_WARN_IF(aRv
.Failed())) {
256 MOZ_ASSERT(UINT64_MAX
- subBlobLength
>= totalLength
);
257 totalLength
+= subBlobLength
;
259 if (blob
->IsFile()) {
260 int64_t partLastModified
= blob
->GetLastModified(aRv
);
261 if (NS_WARN_IF(aRv
.Failed())) {
265 if (lastModified
< partLastModified
) {
266 lastModified
= partLastModified
* PR_USEC_PER_MSEC
;
267 lastModifiedSet
= true;
272 mLength
= totalLength
;
275 if (lastModifiedSet
) {
276 SetLastModificationDatePrecisely(lastModified
);
278 MOZ_ASSERT(aCrossOriginIsolated
.isSome());
280 // We cannot use PR_Now() because bug 493756 and, for this reason:
281 // var x = new Date(); var f = new File(...);
282 // x.getTime() < f.dateModified.getTime()
284 SetLastModificationDate(aCrossOriginIsolated
.value(), JS_Now());
289 size_t MultipartBlobImpl::GetAllocationSize() const {
290 FallibleTArray
<BlobImpl
*> visitedBlobs
;
292 // We want to report the unique blob allocation, avoiding duplicated blobs in
293 // the multipart blob tree.
295 for (uint32_t i
= 0; i
< mBlobImpls
.Length(); ++i
) {
296 total
+= mBlobImpls
[i
]->GetAllocationSize(visitedBlobs
);
302 size_t MultipartBlobImpl::GetAllocationSize(
303 FallibleTArray
<BlobImpl
*>& aVisitedBlobs
) const {
304 FallibleTArray
<BlobImpl
*> visitedBlobs
;
307 for (BlobImpl
* blobImpl
: mBlobImpls
) {
308 if (!aVisitedBlobs
.Contains(blobImpl
)) {
309 if (NS_WARN_IF(!aVisitedBlobs
.AppendElement(blobImpl
, fallible
))) {
312 total
+= blobImpl
->GetAllocationSize(aVisitedBlobs
);
319 void MultipartBlobImpl::GetBlobImplType(nsAString
& aBlobImplType
) const {
320 aBlobImplType
.AssignLiteral("MultipartBlobImpl[");
322 for (uint32_t i
= 0; i
< mBlobImpls
.Length(); ++i
) {
324 aBlobImplType
.AppendLiteral(", ");
327 nsAutoString blobImplType
;
328 mBlobImpls
[i
]->GetBlobImplType(blobImplType
);
330 aBlobImplType
.Append(blobImplType
);
333 aBlobImplType
.AppendLiteral("]");
336 void MultipartBlobImpl::SetLastModified(int64_t aLastModified
) {
337 SetLastModificationDatePrecisely(aLastModified
* PR_USEC_PER_MSEC
);