Line data Source code
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/. */
6 :
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 "nsDOMClassInfoID.h"
13 : #include "nsIMultiplexInputStream.h"
14 : #include "nsRFPService.h"
15 : #include "nsStringStream.h"
16 : #include "nsTArray.h"
17 : #include "nsJSUtils.h"
18 : #include "nsContentUtils.h"
19 : #include "nsIScriptError.h"
20 : #include "nsIXPConnect.h"
21 : #include <algorithm>
22 :
23 : using namespace mozilla;
24 : using namespace mozilla::dom;
25 :
26 0 : NS_IMPL_ISUPPORTS_INHERITED0(MultipartBlobImpl, BlobImpl)
27 :
28 : /* static */ already_AddRefed<MultipartBlobImpl>
29 0 : MultipartBlobImpl::Create(nsTArray<RefPtr<BlobImpl>>&& aBlobImpls,
30 : const nsAString& aName,
31 : const nsAString& aContentType,
32 : ErrorResult& aRv)
33 : {
34 : RefPtr<MultipartBlobImpl> blobImpl =
35 0 : new MultipartBlobImpl(Move(aBlobImpls), aName, aContentType);
36 0 : blobImpl->SetLengthAndModifiedDate(aRv);
37 0 : if (NS_WARN_IF(aRv.Failed())) {
38 0 : return nullptr;
39 : }
40 :
41 0 : return blobImpl.forget();
42 : }
43 :
44 : /* static */ already_AddRefed<MultipartBlobImpl>
45 0 : MultipartBlobImpl::Create(nsTArray<RefPtr<BlobImpl>>&& aBlobImpls,
46 : const nsAString& aContentType,
47 : ErrorResult& aRv)
48 : {
49 : RefPtr<MultipartBlobImpl> blobImpl =
50 0 : new MultipartBlobImpl(Move(aBlobImpls), aContentType);
51 0 : blobImpl->SetLengthAndModifiedDate(aRv);
52 0 : if (NS_WARN_IF(aRv.Failed())) {
53 0 : return nullptr;
54 : }
55 :
56 0 : return blobImpl.forget();
57 : }
58 :
59 : void
60 0 : MultipartBlobImpl::GetInternalStream(nsIInputStream** aStream,
61 : ErrorResult& aRv)
62 : {
63 0 : *aStream = nullptr;
64 :
65 : nsCOMPtr<nsIMultiplexInputStream> stream =
66 0 : do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
67 0 : if (NS_WARN_IF(!stream)) {
68 0 : aRv.Throw(NS_ERROR_FAILURE);
69 0 : return;
70 : }
71 :
72 : uint32_t i;
73 0 : for (i = 0; i < mBlobImpls.Length(); i++) {
74 0 : nsCOMPtr<nsIInputStream> scratchStream;
75 0 : BlobImpl* blobImpl = mBlobImpls.ElementAt(i).get();
76 :
77 0 : blobImpl->GetInternalStream(getter_AddRefs(scratchStream), aRv);
78 0 : if (NS_WARN_IF(aRv.Failed())) {
79 0 : return;
80 : }
81 :
82 0 : aRv = stream->AppendStream(scratchStream);
83 0 : if (NS_WARN_IF(aRv.Failed())) {
84 0 : return;
85 : }
86 : }
87 :
88 0 : stream.forget(aStream);
89 : }
90 :
91 : already_AddRefed<BlobImpl>
92 0 : MultipartBlobImpl::CreateSlice(uint64_t aStart, uint64_t aLength,
93 : const nsAString& aContentType,
94 : ErrorResult& aRv)
95 : {
96 : // If we clamped to nothing we create an empty blob
97 0 : nsTArray<RefPtr<BlobImpl>> blobImpls;
98 :
99 0 : uint64_t length = aLength;
100 0 : uint64_t skipStart = aStart;
101 :
102 : // Prune the list of blobs if we can
103 : uint32_t i;
104 0 : for (i = 0; length && skipStart && i < mBlobImpls.Length(); i++) {
105 0 : BlobImpl* blobImpl = mBlobImpls[i].get();
106 :
107 0 : uint64_t l = blobImpl->GetSize(aRv);
108 0 : if (NS_WARN_IF(aRv.Failed())) {
109 0 : return nullptr;
110 : }
111 :
112 0 : if (skipStart < l) {
113 0 : uint64_t upperBound = std::min<uint64_t>(l - skipStart, length);
114 :
115 : RefPtr<BlobImpl> firstBlobImpl =
116 0 : blobImpl->CreateSlice(skipStart, upperBound,
117 0 : aContentType, aRv);
118 0 : if (NS_WARN_IF(aRv.Failed())) {
119 0 : return nullptr;
120 : }
121 :
122 : // Avoid wrapping a single blob inside an MultipartBlobImpl
123 0 : if (length == upperBound) {
124 0 : return firstBlobImpl.forget();
125 : }
126 :
127 0 : blobImpls.AppendElement(firstBlobImpl);
128 0 : length -= upperBound;
129 0 : i++;
130 0 : break;
131 : }
132 0 : skipStart -= l;
133 : }
134 :
135 : // Now append enough blobs until we're done
136 0 : for (; length && i < mBlobImpls.Length(); i++) {
137 0 : BlobImpl* blobImpl = mBlobImpls[i].get();
138 :
139 0 : uint64_t l = blobImpl->GetSize(aRv);
140 0 : if (NS_WARN_IF(aRv.Failed())) {
141 0 : return nullptr;
142 : }
143 :
144 0 : if (length < l) {
145 : RefPtr<BlobImpl> lastBlobImpl =
146 0 : blobImpl->CreateSlice(0, length, aContentType, aRv);
147 0 : if (NS_WARN_IF(aRv.Failed())) {
148 0 : return nullptr;
149 : }
150 :
151 0 : blobImpls.AppendElement(lastBlobImpl);
152 : } else {
153 0 : blobImpls.AppendElement(blobImpl);
154 : }
155 0 : length -= std::min<uint64_t>(l, length);
156 : }
157 :
158 : // we can create our blob now
159 0 : RefPtr<BlobImpl> impl = Create(Move(blobImpls), aContentType, aRv);
160 0 : if (NS_WARN_IF(aRv.Failed())) {
161 0 : return nullptr;
162 : }
163 :
164 0 : return impl.forget();
165 : }
166 :
167 : void
168 0 : MultipartBlobImpl::InitializeBlob(ErrorResult& aRv)
169 : {
170 0 : SetLengthAndModifiedDate(aRv);
171 0 : NS_WARNING_ASSERTION(!aRv.Failed(), "SetLengthAndModifiedDate failed");
172 0 : }
173 :
174 : void
175 0 : MultipartBlobImpl::InitializeBlob(const Sequence<Blob::BlobPart>& aData,
176 : const nsAString& aContentType,
177 : bool aNativeEOL,
178 : ErrorResult& aRv)
179 : {
180 0 : mContentType = aContentType;
181 0 : BlobSet blobSet;
182 :
183 0 : for (uint32_t i = 0, len = aData.Length(); i < len; ++i) {
184 0 : const Blob::BlobPart& data = aData[i];
185 :
186 0 : if (data.IsBlob()) {
187 0 : RefPtr<Blob> blob = data.GetAsBlob().get();
188 0 : blobSet.AppendBlobImpl(blob->Impl());
189 : }
190 :
191 0 : else if (data.IsUSVString()) {
192 0 : aRv = blobSet.AppendString(data.GetAsUSVString(), aNativeEOL);
193 0 : if (aRv.Failed()) {
194 0 : return;
195 : }
196 : }
197 :
198 0 : else if (data.IsArrayBuffer()) {
199 0 : const ArrayBuffer& buffer = data.GetAsArrayBuffer();
200 0 : buffer.ComputeLengthAndData();
201 0 : aRv = blobSet.AppendVoidPtr(buffer.Data(), buffer.Length());
202 0 : if (aRv.Failed()) {
203 0 : return;
204 : }
205 : }
206 :
207 0 : else if (data.IsArrayBufferView()) {
208 0 : const ArrayBufferView& buffer = data.GetAsArrayBufferView();
209 0 : buffer.ComputeLengthAndData();
210 0 : aRv = blobSet.AppendVoidPtr(buffer.Data(), buffer.Length());
211 0 : if (aRv.Failed()) {
212 0 : return;
213 : }
214 : }
215 :
216 : else {
217 0 : MOZ_CRASH("Impossible blob data type.");
218 : }
219 : }
220 :
221 :
222 0 : mBlobImpls = blobSet.GetBlobImpls();
223 0 : SetLengthAndModifiedDate(aRv);
224 0 : NS_WARNING_ASSERTION(!aRv.Failed(), "SetLengthAndModifiedDate failed");
225 : }
226 :
227 : void
228 0 : MultipartBlobImpl::SetLengthAndModifiedDate(ErrorResult& aRv)
229 : {
230 0 : MOZ_ASSERT(mLength == UINT64_MAX);
231 0 : MOZ_ASSERT(mLastModificationDate == INT64_MAX);
232 :
233 0 : uint64_t totalLength = 0;
234 0 : int64_t lastModified = 0;
235 0 : bool lastModifiedSet = false;
236 :
237 0 : for (uint32_t index = 0, count = mBlobImpls.Length(); index < count; index++) {
238 0 : RefPtr<BlobImpl>& blob = mBlobImpls[index];
239 :
240 : #ifdef DEBUG
241 0 : MOZ_ASSERT(!blob->IsSizeUnknown());
242 0 : MOZ_ASSERT(!blob->IsDateUnknown());
243 : #endif
244 :
245 0 : uint64_t subBlobLength = blob->GetSize(aRv);
246 0 : if (NS_WARN_IF(aRv.Failed())) {
247 0 : return;
248 : }
249 :
250 0 : MOZ_ASSERT(UINT64_MAX - subBlobLength >= totalLength);
251 0 : totalLength += subBlobLength;
252 :
253 0 : if (blob->IsFile()) {
254 0 : int64_t partLastModified = blob->GetLastModified(aRv);
255 0 : if (NS_WARN_IF(aRv.Failed())) {
256 0 : return;
257 : }
258 :
259 0 : if (lastModified < partLastModified) {
260 0 : lastModified = partLastModified;
261 0 : lastModifiedSet = true;
262 : }
263 : }
264 : }
265 :
266 0 : mLength = totalLength;
267 :
268 0 : if (mIsFile) {
269 : // We cannot use PR_Now() because bug 493756 and, for this reason:
270 : // var x = new Date(); var f = new File(...);
271 : // x.getTime() < f.dateModified.getTime()
272 : // could fail.
273 0 : mLastModificationDate = nsRFPService::ReduceTimePrecisionAsUSecs(
274 0 : lastModifiedSet ? lastModified * PR_USEC_PER_MSEC : JS_Now());
275 : }
276 : }
277 :
278 : void
279 0 : MultipartBlobImpl::GetMozFullPathInternal(nsAString& aFilename,
280 : ErrorResult& aRv) const
281 : {
282 0 : if (!mIsFromNsIFile || mBlobImpls.Length() == 0) {
283 0 : BaseBlobImpl::GetMozFullPathInternal(aFilename, aRv);
284 0 : return;
285 : }
286 :
287 0 : BlobImpl* blobImpl = mBlobImpls.ElementAt(0).get();
288 0 : if (!blobImpl) {
289 0 : BaseBlobImpl::GetMozFullPathInternal(aFilename, aRv);
290 0 : return;
291 : }
292 :
293 0 : blobImpl->GetMozFullPathInternal(aFilename, aRv);
294 : }
295 :
296 : nsresult
297 0 : MultipartBlobImpl::SetMutable(bool aMutable)
298 : {
299 : nsresult rv;
300 :
301 : // This looks a little sketchy since BlobImpl objects are supposed to be
302 : // threadsafe. However, we try to enforce that all BlobImpl objects must be
303 : // set to immutable *before* being passed to another thread, so this should
304 : // be safe.
305 0 : if (!aMutable && !mImmutable && !mBlobImpls.IsEmpty()) {
306 0 : for (uint32_t index = 0, count = mBlobImpls.Length();
307 0 : index < count;
308 : index++) {
309 0 : rv = mBlobImpls[index]->SetMutable(aMutable);
310 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
311 0 : return rv;
312 : }
313 : }
314 : }
315 :
316 0 : rv = BaseBlobImpl::SetMutable(aMutable);
317 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
318 0 : return rv;
319 : }
320 :
321 0 : MOZ_ASSERT_IF(!aMutable, mImmutable);
322 :
323 0 : return NS_OK;
324 : }
325 :
326 : nsresult
327 0 : MultipartBlobImpl::InitializeChromeFile(nsIFile* aFile,
328 : const nsAString& aType,
329 : const nsAString& aName,
330 : bool aLastModifiedPassed,
331 : int64_t aLastModified,
332 : bool aIsFromNsIFile)
333 : {
334 0 : MOZ_ASSERT(!mImmutable, "Something went wrong ...");
335 0 : if (mImmutable) {
336 0 : return NS_ERROR_UNEXPECTED;
337 : }
338 :
339 0 : mName = aName;
340 0 : mContentType = aType;
341 0 : mIsFromNsIFile = aIsFromNsIFile;
342 :
343 : bool exists;
344 0 : nsresult rv= aFile->Exists(&exists);
345 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
346 0 : return rv;
347 : }
348 :
349 0 : if (!exists) {
350 0 : return NS_ERROR_FILE_NOT_FOUND;
351 : }
352 :
353 : bool isDir;
354 0 : rv = aFile->IsDirectory(&isDir);
355 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
356 0 : return rv;
357 : }
358 :
359 0 : if (isDir) {
360 0 : return NS_ERROR_FILE_IS_DIRECTORY;
361 : }
362 :
363 0 : if (mName.IsEmpty()) {
364 0 : aFile->GetLeafName(mName);
365 : }
366 :
367 0 : RefPtr<File> blob = File::CreateFromFile(nullptr, aFile);
368 :
369 : // Pre-cache size.
370 0 : ErrorResult error;
371 0 : blob->GetSize(error);
372 0 : if (NS_WARN_IF(error.Failed())) {
373 0 : return error.StealNSResult();
374 : }
375 :
376 : // Pre-cache modified date.
377 0 : blob->GetLastModified(error);
378 0 : if (NS_WARN_IF(error.Failed())) {
379 0 : return error.StealNSResult();
380 : }
381 :
382 : // XXXkhuey this is terrible
383 0 : if (mContentType.IsEmpty()) {
384 0 : blob->GetType(mContentType);
385 : }
386 :
387 0 : BlobSet blobSet;
388 0 : blobSet.AppendBlobImpl(static_cast<File*>(blob.get())->Impl());
389 0 : mBlobImpls = blobSet.GetBlobImpls();
390 :
391 0 : SetLengthAndModifiedDate(error);
392 0 : if (NS_WARN_IF(error.Failed())) {
393 0 : return error.StealNSResult();
394 : }
395 :
396 0 : if (aLastModifiedPassed) {
397 0 : SetLastModified(aLastModified);
398 : }
399 :
400 0 : return NS_OK;
401 : }
402 :
403 : bool
404 0 : MultipartBlobImpl::MayBeClonedToOtherThreads() const
405 : {
406 0 : for (uint32_t i = 0; i < mBlobImpls.Length(); ++i) {
407 0 : if (!mBlobImpls[i]->MayBeClonedToOtherThreads()) {
408 0 : return false;
409 : }
410 : }
411 :
412 0 : return true;
413 : }
|