Bumping manifests a=b2g-bump
[gecko.git] / dom / base / MultipartFileImpl.cpp
blob69baed2e7ab79a4c9ed59155fd6fbfba918c6ff3
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"
15 #include "nsTArray.h"
16 #include "nsJSUtils.h"
17 #include "nsContentUtils.h"
18 #include "nsIScriptError.h"
19 #include "nsIXPConnect.h"
20 #include <algorithm>
22 using namespace mozilla;
23 using namespace mozilla::dom;
25 NS_IMPL_ISUPPORTS_INHERITED0(MultipartFileImpl, FileImpl)
27 nsresult
28 MultipartFileImpl::GetInternalStream(nsIInputStream** aStream)
30 nsresult rv;
31 *aStream = nullptr;
33 nsCOMPtr<nsIMultiplexInputStream> stream =
34 do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
35 NS_ENSURE_TRUE(stream, NS_ERROR_FAILURE);
37 uint32_t i;
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,
55 ErrorResult& aRv)
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
64 uint32_t i;
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())) {
70 return nullptr;
73 if (skipStart < l) {
74 uint64_t upperBound = std::min<uint64_t>(l - skipStart, length);
76 nsRefPtr<FileImpl> firstBlobImpl =
77 blobImpl->CreateSlice(skipStart, upperBound,
78 aContentType, aRv);
79 if (NS_WARN_IF(aRv.Failed())) {
80 return nullptr;
83 // Avoid wrapping a single blob inside an MultipartFileImpl
84 if (length == upperBound) {
85 return firstBlobImpl.forget();
88 blobImpls.AppendElement(firstBlobImpl);
89 length -= upperBound;
90 i++;
91 break;
93 skipStart -= l;
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())) {
102 return nullptr;
105 if (length < l) {
106 nsRefPtr<FileImpl> lastBlobImpl =
107 blobImpl->CreateSlice(0, length, aContentType, aRv);
108 if (NS_WARN_IF(aRv.Failed())) {
109 return nullptr;
112 blobImpls.AppendElement(lastBlobImpl);
113 } else {
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();
125 void
126 MultipartFileImpl::InitializeBlob()
128 SetLengthAndModifiedDate();
131 void
132 MultipartFileImpl::InitializeBlob(
133 JSContext* aCx,
134 const Sequence<OwningArrayBufferOrArrayBufferViewOrBlobOrString>& aData,
135 const nsAString& aContentType,
136 bool aNativeEOL,
137 ErrorResult& aRv)
139 mContentType = aContentType;
140 BlobSet blobSet;
142 for (uint32_t i = 0, len = aData.Length(); i < len; ++i) {
143 const OwningArrayBufferOrArrayBufferViewOrBlobOrString& data = aData[i];
145 if (data.IsBlob()) {
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);
152 if (aRv.Failed()) {
153 return;
157 else if (data.IsArrayBuffer()) {
158 const ArrayBuffer& buffer = data.GetAsArrayBuffer();
159 buffer.ComputeLengthAndData();
160 aRv = blobSet.AppendVoidPtr(buffer.Data(), buffer.Length());
161 if (aRv.Failed()) {
162 return;
166 else if (data.IsArrayBufferView()) {
167 const ArrayBufferView& buffer = data.GetAsArrayBufferView();
168 buffer.ComputeLengthAndData();
169 aRv = blobSet.AppendVoidPtr(buffer.Data(), buffer.Length());
170 if (aRv.Failed()) {
171 return;
175 else {
176 MOZ_CRASH("Impossible blob data type.");
181 mBlobImpls = blobSet.GetBlobImpls();
182 SetLengthAndModifiedDate();
185 void
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];
196 #ifdef DEBUG
197 MOZ_ASSERT(!blob->IsSizeUnknown());
198 MOZ_ASSERT(!blob->IsDateUnknown());
199 #endif
201 ErrorResult error;
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;
211 if (mIsFile) {
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()
215 // could fail.
216 mLastModificationDate = JS_Now();
220 void
221 MultipartFileImpl::GetMozFullPathInternal(nsAString& aFilename,
222 ErrorResult& aRv)
224 if (!mIsFromNsIFile || mBlobImpls.Length() == 0) {
225 FileImplBase::GetMozFullPathInternal(aFilename, aRv);
226 return;
229 FileImpl* blobImpl = mBlobImpls.ElementAt(0).get();
230 if (!blobImpl) {
231 FileImplBase::GetMozFullPathInternal(aFilename, aRv);
232 return;
235 blobImpl->GetMozFullPathInternal(aFilename, aRv);
238 nsresult
239 MultipartFileImpl::SetMutable(bool aMutable)
241 nsresult rv;
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
246 // be safe.
247 if (!aMutable && !mImmutable && !mBlobImpls.IsEmpty()) {
248 for (uint32_t index = 0, count = mBlobImpls.Length();
249 index < count;
250 index++) {
251 rv = mBlobImpls[index]->SetMutable(aMutable);
252 if (NS_WARN_IF(NS_FAILED(rv))) {
253 return rv;
258 rv = FileImplBase::SetMutable(aMutable);
259 if (NS_WARN_IF(NS_FAILED(rv))) {
260 return rv;
263 MOZ_ASSERT_IF(!aMutable, mImmutable);
265 return NS_OK;
268 void
269 MultipartFileImpl::InitializeChromeFile(File& aBlob,
270 const ChromeFilePropertyBag& aBag,
271 ErrorResult& aRv)
273 NS_ASSERTION(!mImmutable, "Something went wrong ...");
275 if (mImmutable) {
276 aRv.Throw(NS_ERROR_UNEXPECTED);
277 return;
280 MOZ_ASSERT(nsContentUtils::IsCallerChrome());
282 mName = aBag.mName;
283 mContentType = aBag.mType;
284 mIsFromNsIFile = true;
286 // XXXkhuey this is terrible
287 if (mContentType.IsEmpty()) {
288 aBlob.GetType(mContentType);
292 BlobSet blobSet;
293 blobSet.AppendBlobImpl(aBlob.Impl());
294 mBlobImpls = blobSet.GetBlobImpls();
296 SetLengthAndModifiedDate();
299 void
300 MultipartFileImpl::InitializeChromeFile(nsPIDOMWindow* aWindow,
301 nsIFile* aFile,
302 const ChromeFilePropertyBag& aBag,
303 bool aIsFromNsIFile,
304 ErrorResult& aRv)
306 NS_ASSERTION(!mImmutable, "Something went wrong ...");
307 if (mImmutable) {
308 aRv.Throw(NS_ERROR_UNEXPECTED);
309 return;
312 MOZ_ASSERT(nsContentUtils::IsCallerChrome());
314 mName = aBag.mName;
315 mContentType = aBag.mType;
316 mIsFromNsIFile = aIsFromNsIFile;
318 bool exists;
319 aRv = aFile->Exists(&exists);
320 if (NS_WARN_IF(aRv.Failed())) {
321 return;
324 if (!exists) {
325 aRv.Throw(NS_ERROR_FILE_NOT_FOUND);
326 return;
329 bool isDir;
330 aRv = aFile->IsDirectory(&isDir);
331 if (NS_WARN_IF(aRv.Failed())) {
332 return;
335 if (isDir) {
336 aRv.Throw(NS_ERROR_FILE_IS_DIRECTORY);
337 return;
340 if (mName.IsEmpty()) {
341 aFile->GetLeafName(mName);
344 nsRefPtr<File> blob = File::CreateFromFile(aWindow, aFile, aBag.mTemporary);
346 // Pre-cache size.
347 uint64_t unused;
348 aRv = blob->GetSize(&unused);
349 if (NS_WARN_IF(aRv.Failed())) {
350 return;
353 // Pre-cache modified date.
354 aRv = blob->GetMozLastModifiedDate(&unused);
355 if (NS_WARN_IF(aRv.Failed())) {
356 return;
359 // XXXkhuey this is terrible
360 if (mContentType.IsEmpty()) {
361 blob->GetType(mContentType);
364 BlobSet blobSet;
365 blobSet.AppendBlobImpl(static_cast<File*>(blob.get())->Impl());
366 mBlobImpls = blobSet.GetBlobImpls();
368 SetLengthAndModifiedDate();
371 void
372 MultipartFileImpl::InitializeChromeFile(nsPIDOMWindow* aWindow,
373 const nsAString& aData,
374 const ChromeFilePropertyBag& aBag,
375 ErrorResult& aRv)
377 nsCOMPtr<nsIFile> file;
378 aRv = NS_NewLocalFile(aData, false, getter_AddRefs(file));
379 if (NS_WARN_IF(aRv.Failed())) {
380 return;
383 InitializeChromeFile(aWindow, file, aBag, false, aRv);
386 bool
387 MultipartFileImpl::MayBeClonedToOtherThreads() const
389 for (uint32_t i = 0; i < mBlobImpls.Length(); ++i) {
390 if (!mBlobImpls[i]->MayBeClonedToOtherThreads()) {
391 return false;
395 return true;