1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "MultipartFileImpl.h"
7 #include "jsfriendapi.h"
8 #include "mozilla/dom/BlobSet.h"
9 #include "mozilla/dom/FileBinding.h"
10 #include "mozilla/dom/UnionTypes.h"
11 #include "nsAutoPtr.h"
12 #include "nsDOMClassInfoID.h"
13 #include "nsIMultiplexInputStream.h"
14 #include "nsStringStream.h"
16 #include "nsJSUtils.h"
17 #include "nsContentUtils.h"
18 #include "nsIScriptError.h"
19 #include "nsIXPConnect.h"
22 using namespace mozilla
;
23 using namespace mozilla::dom
;
25 NS_IMPL_ISUPPORTS_INHERITED0(MultipartFileImpl
, FileImpl
)
28 MultipartFileImpl::GetInternalStream(nsIInputStream
** aStream
)
33 nsCOMPtr
<nsIMultiplexInputStream
> stream
=
34 do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
35 NS_ENSURE_TRUE(stream
, NS_ERROR_FAILURE
);
38 for (i
= 0; i
< mBlobImpls
.Length(); i
++) {
39 nsCOMPtr
<nsIInputStream
> scratchStream
;
40 FileImpl
* blobImpl
= mBlobImpls
.ElementAt(i
).get();
42 rv
= blobImpl
->GetInternalStream(getter_AddRefs(scratchStream
));
43 NS_ENSURE_SUCCESS(rv
, rv
);
45 rv
= stream
->AppendStream(scratchStream
);
46 NS_ENSURE_SUCCESS(rv
, rv
);
49 return CallQueryInterface(stream
, aStream
);
52 already_AddRefed
<FileImpl
>
53 MultipartFileImpl::CreateSlice(uint64_t aStart
, uint64_t aLength
,
54 const nsAString
& aContentType
,
57 // If we clamped to nothing we create an empty blob
58 nsTArray
<nsRefPtr
<FileImpl
>> blobImpls
;
60 uint64_t length
= aLength
;
61 uint64_t skipStart
= aStart
;
63 // Prune the list of blobs if we can
65 for (i
= 0; length
&& skipStart
&& i
< mBlobImpls
.Length(); i
++) {
66 FileImpl
* blobImpl
= mBlobImpls
[i
].get();
68 uint64_t l
= blobImpl
->GetSize(aRv
);
69 if (NS_WARN_IF(aRv
.Failed())) {
74 uint64_t upperBound
= std::min
<uint64_t>(l
- skipStart
, length
);
76 nsRefPtr
<FileImpl
> firstBlobImpl
=
77 blobImpl
->CreateSlice(skipStart
, upperBound
,
79 if (NS_WARN_IF(aRv
.Failed())) {
83 // Avoid wrapping a single blob inside an MultipartFileImpl
84 if (length
== upperBound
) {
85 return firstBlobImpl
.forget();
88 blobImpls
.AppendElement(firstBlobImpl
);
96 // Now append enough blobs until we're done
97 for (; length
&& i
< mBlobImpls
.Length(); i
++) {
98 FileImpl
* blobImpl
= mBlobImpls
[i
].get();
100 uint64_t l
= blobImpl
->GetSize(aRv
);
101 if (NS_WARN_IF(aRv
.Failed())) {
106 nsRefPtr
<FileImpl
> lastBlobImpl
=
107 blobImpl
->CreateSlice(0, length
, aContentType
, aRv
);
108 if (NS_WARN_IF(aRv
.Failed())) {
112 blobImpls
.AppendElement(lastBlobImpl
);
114 blobImpls
.AppendElement(blobImpl
);
116 length
-= std::min
<uint64_t>(l
, length
);
119 // we can create our blob now
120 nsRefPtr
<FileImpl
> impl
=
121 new MultipartFileImpl(blobImpls
, aContentType
);
122 return impl
.forget();
126 MultipartFileImpl::InitializeBlob()
128 SetLengthAndModifiedDate();
132 MultipartFileImpl::InitializeBlob(
134 const Sequence
<OwningArrayBufferOrArrayBufferViewOrBlobOrString
>& aData
,
135 const nsAString
& aContentType
,
139 mContentType
= aContentType
;
142 for (uint32_t i
= 0, len
= aData
.Length(); i
< len
; ++i
) {
143 const OwningArrayBufferOrArrayBufferViewOrBlobOrString
& data
= aData
[i
];
146 nsRefPtr
<File
> file
= data
.GetAsBlob().get();
147 blobSet
.AppendBlobImpl(file
->Impl());
150 else if (data
.IsString()) {
151 aRv
= blobSet
.AppendString(data
.GetAsString(), aNativeEOL
, aCx
);
157 else if (data
.IsArrayBuffer()) {
158 const ArrayBuffer
& buffer
= data
.GetAsArrayBuffer();
159 buffer
.ComputeLengthAndData();
160 aRv
= blobSet
.AppendVoidPtr(buffer
.Data(), buffer
.Length());
166 else if (data
.IsArrayBufferView()) {
167 const ArrayBufferView
& buffer
= data
.GetAsArrayBufferView();
168 buffer
.ComputeLengthAndData();
169 aRv
= blobSet
.AppendVoidPtr(buffer
.Data(), buffer
.Length());
176 MOZ_CRASH("Impossible blob data type.");
181 mBlobImpls
= blobSet
.GetBlobImpls();
182 SetLengthAndModifiedDate();
186 MultipartFileImpl::SetLengthAndModifiedDate()
188 MOZ_ASSERT(mLength
== UINT64_MAX
);
189 MOZ_ASSERT(mLastModificationDate
== UINT64_MAX
);
191 uint64_t totalLength
= 0;
193 for (uint32_t index
= 0, count
= mBlobImpls
.Length(); index
< count
; index
++) {
194 nsRefPtr
<FileImpl
>& blob
= mBlobImpls
[index
];
197 MOZ_ASSERT(!blob
->IsSizeUnknown());
198 MOZ_ASSERT(!blob
->IsDateUnknown());
202 uint64_t subBlobLength
= blob
->GetSize(error
);
203 MOZ_ALWAYS_TRUE(!error
.Failed());
205 MOZ_ASSERT(UINT64_MAX
- subBlobLength
>= totalLength
);
206 totalLength
+= subBlobLength
;
209 mLength
= totalLength
;
212 // We cannot use PR_Now() because bug 493756 and, for this reason:
213 // var x = new Date(); var f = new File(...);
214 // x.getTime() < f.dateModified.getTime()
216 mLastModificationDate
= JS_Now();
221 MultipartFileImpl::GetMozFullPathInternal(nsAString
& aFilename
,
224 if (!mIsFromNsIFile
|| mBlobImpls
.Length() == 0) {
225 FileImplBase::GetMozFullPathInternal(aFilename
, aRv
);
229 FileImpl
* blobImpl
= mBlobImpls
.ElementAt(0).get();
231 FileImplBase::GetMozFullPathInternal(aFilename
, aRv
);
235 blobImpl
->GetMozFullPathInternal(aFilename
, aRv
);
239 MultipartFileImpl::SetMutable(bool aMutable
)
243 // This looks a little sketchy since FileImpl objects are supposed to be
244 // threadsafe. However, we try to enforce that all FileImpl objects must be
245 // set to immutable *before* being passed to another thread, so this should
247 if (!aMutable
&& !mImmutable
&& !mBlobImpls
.IsEmpty()) {
248 for (uint32_t index
= 0, count
= mBlobImpls
.Length();
251 rv
= mBlobImpls
[index
]->SetMutable(aMutable
);
252 if (NS_WARN_IF(NS_FAILED(rv
))) {
258 rv
= FileImplBase::SetMutable(aMutable
);
259 if (NS_WARN_IF(NS_FAILED(rv
))) {
263 MOZ_ASSERT_IF(!aMutable
, mImmutable
);
269 MultipartFileImpl::InitializeChromeFile(File
& aBlob
,
270 const ChromeFilePropertyBag
& aBag
,
273 NS_ASSERTION(!mImmutable
, "Something went wrong ...");
276 aRv
.Throw(NS_ERROR_UNEXPECTED
);
280 MOZ_ASSERT(nsContentUtils::IsCallerChrome());
283 mContentType
= aBag
.mType
;
284 mIsFromNsIFile
= true;
286 // XXXkhuey this is terrible
287 if (mContentType
.IsEmpty()) {
288 aBlob
.GetType(mContentType
);
293 blobSet
.AppendBlobImpl(aBlob
.Impl());
294 mBlobImpls
= blobSet
.GetBlobImpls();
296 SetLengthAndModifiedDate();
300 MultipartFileImpl::InitializeChromeFile(nsPIDOMWindow
* aWindow
,
302 const ChromeFilePropertyBag
& aBag
,
306 NS_ASSERTION(!mImmutable
, "Something went wrong ...");
308 aRv
.Throw(NS_ERROR_UNEXPECTED
);
312 MOZ_ASSERT(nsContentUtils::IsCallerChrome());
315 mContentType
= aBag
.mType
;
316 mIsFromNsIFile
= aIsFromNsIFile
;
319 aRv
= aFile
->Exists(&exists
);
320 if (NS_WARN_IF(aRv
.Failed())) {
325 aRv
.Throw(NS_ERROR_FILE_NOT_FOUND
);
330 aRv
= aFile
->IsDirectory(&isDir
);
331 if (NS_WARN_IF(aRv
.Failed())) {
336 aRv
.Throw(NS_ERROR_FILE_IS_DIRECTORY
);
340 if (mName
.IsEmpty()) {
341 aFile
->GetLeafName(mName
);
344 nsRefPtr
<File
> blob
= File::CreateFromFile(aWindow
, aFile
, aBag
.mTemporary
);
348 aRv
= blob
->GetSize(&unused
);
349 if (NS_WARN_IF(aRv
.Failed())) {
353 // Pre-cache modified date.
354 aRv
= blob
->GetMozLastModifiedDate(&unused
);
355 if (NS_WARN_IF(aRv
.Failed())) {
359 // XXXkhuey this is terrible
360 if (mContentType
.IsEmpty()) {
361 blob
->GetType(mContentType
);
365 blobSet
.AppendBlobImpl(static_cast<File
*>(blob
.get())->Impl());
366 mBlobImpls
= blobSet
.GetBlobImpls();
368 SetLengthAndModifiedDate();
372 MultipartFileImpl::InitializeChromeFile(nsPIDOMWindow
* aWindow
,
373 const nsAString
& aData
,
374 const ChromeFilePropertyBag
& aBag
,
377 nsCOMPtr
<nsIFile
> file
;
378 aRv
= NS_NewLocalFile(aData
, false, getter_AddRefs(file
));
379 if (NS_WARN_IF(aRv
.Failed())) {
383 InitializeChromeFile(aWindow
, file
, aBag
, false, aRv
);
387 MultipartFileImpl::MayBeClonedToOtherThreads() const
389 for (uint32_t i
= 0; i
< mBlobImpls
.Length(); ++i
) {
390 if (!mBlobImpls
[i
]->MayBeClonedToOtherThreads()) {