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 "MutableBlobStorage.h"
8 : #include "MemoryBlobImpl.h"
9 : #include "mozilla/CheckedInt.h"
10 : #include "mozilla/Preferences.h"
11 : #include "mozilla/TaskQueue.h"
12 : #include "File.h"
13 : #include "nsAnonymousTemporaryFile.h"
14 : #include "nsNetCID.h"
15 : #include "nsProxyRelease.h"
16 : #include "WorkerPrivate.h"
17 :
18 : #define BLOB_MEMORY_TEMPORARY_FILE 1048576
19 :
20 : namespace mozilla {
21 : namespace dom {
22 :
23 : namespace {
24 :
25 : // This class uses the callback to inform when the Blob is created or when the
26 : // error must be propagated.
27 : class BlobCreationDoneRunnable final : public Runnable
28 : {
29 : public:
30 0 : BlobCreationDoneRunnable(MutableBlobStorage* aBlobStorage,
31 : MutableBlobStorageCallback* aCallback,
32 : Blob* aBlob,
33 : nsresult aRv)
34 0 : : Runnable("dom::BlobCreationDoneRunnable")
35 : , mBlobStorage(aBlobStorage)
36 : , mCallback(aCallback)
37 : , mBlob(aBlob)
38 0 : , mRv(aRv)
39 : {
40 0 : MOZ_ASSERT(aBlobStorage);
41 0 : MOZ_ASSERT(aCallback);
42 0 : MOZ_ASSERT((NS_FAILED(aRv) && !aBlob) ||
43 : (NS_SUCCEEDED(aRv) && aBlob));
44 0 : }
45 :
46 : NS_IMETHOD
47 0 : Run() override
48 : {
49 0 : MOZ_ASSERT(NS_IsMainThread());
50 0 : MOZ_ASSERT(mBlobStorage);
51 0 : mCallback->BlobStoreCompleted(mBlobStorage, mBlob, mRv);
52 0 : mCallback = nullptr;
53 0 : mBlob = nullptr;
54 0 : return NS_OK;
55 : }
56 :
57 : private:
58 0 : ~BlobCreationDoneRunnable()
59 0 : {
60 0 : MOZ_ASSERT(mBlobStorage);
61 : // If something when wrong, we still have to release these objects in the
62 : // correct thread.
63 0 : NS_ProxyRelease(
64 : "BlobCreationDoneRunnable::mCallback",
65 0 : mBlobStorage->EventTarget(), mCallback.forget());
66 0 : NS_ProxyRelease(
67 : "BlobCreationDoneRunnable::mBlob",
68 0 : mBlobStorage->EventTarget(), mBlob.forget());
69 0 : }
70 :
71 : RefPtr<MutableBlobStorage> mBlobStorage;
72 : RefPtr<MutableBlobStorageCallback> mCallback;
73 : RefPtr<Blob> mBlob;
74 : nsresult mRv;
75 : };
76 :
77 : // This runnable goes back to the main-thread and informs the BlobStorage about
78 : // the temporary file.
79 : class FileCreatedRunnable final : public Runnable
80 : {
81 : public:
82 0 : FileCreatedRunnable(MutableBlobStorage* aBlobStorage, PRFileDesc* aFD)
83 0 : : Runnable("dom::FileCreatedRunnable")
84 : , mBlobStorage(aBlobStorage)
85 0 : , mFD(aFD)
86 : {
87 0 : MOZ_ASSERT(aBlobStorage);
88 0 : MOZ_ASSERT(aFD);
89 0 : }
90 :
91 : NS_IMETHOD
92 0 : Run() override
93 : {
94 0 : MOZ_ASSERT(NS_IsMainThread());
95 0 : mBlobStorage->TemporaryFileCreated(mFD);
96 0 : mFD = nullptr;
97 0 : return NS_OK;
98 : }
99 :
100 : private:
101 0 : ~FileCreatedRunnable()
102 0 : {
103 : // If something when wrong, we still have to close the FileDescriptor.
104 0 : if (mFD) {
105 0 : PR_Close(mFD);
106 : }
107 0 : }
108 :
109 : RefPtr<MutableBlobStorage> mBlobStorage;
110 : PRFileDesc* mFD;
111 : };
112 :
113 : // This runnable creates the temporary file. When done, FileCreatedRunnable is
114 : // dispatched back to the main-thread.
115 0 : class CreateTemporaryFileRunnable final : public Runnable
116 : {
117 : public:
118 0 : explicit CreateTemporaryFileRunnable(MutableBlobStorage* aBlobStorage)
119 0 : : Runnable("dom::CreateTemporaryFileRunnable")
120 0 : , mBlobStorage(aBlobStorage)
121 : {
122 0 : MOZ_ASSERT(NS_IsMainThread());
123 0 : MOZ_ASSERT(XRE_IsParentProcess());
124 0 : MOZ_ASSERT(aBlobStorage);
125 0 : }
126 :
127 : NS_IMETHOD
128 0 : Run() override
129 : {
130 0 : MOZ_ASSERT(!NS_IsMainThread());
131 0 : MOZ_ASSERT(XRE_IsParentProcess());
132 0 : MOZ_ASSERT(mBlobStorage);
133 :
134 0 : PRFileDesc* tempFD = nullptr;
135 0 : nsresult rv = NS_OpenAnonymousTemporaryFile(&tempFD);
136 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
137 0 : return NS_OK;
138 : }
139 :
140 : // The ownership of the tempFD is moved to the FileCreatedRunnable.
141 0 : return mBlobStorage->EventTarget()->Dispatch(
142 0 : new FileCreatedRunnable(mBlobStorage, tempFD), NS_DISPATCH_NORMAL);
143 : }
144 :
145 : private:
146 : RefPtr<MutableBlobStorage> mBlobStorage;
147 : };
148 :
149 : // Simple runnable to propagate the error to the BlobStorage.
150 0 : class ErrorPropagationRunnable final : public Runnable
151 : {
152 : public:
153 0 : ErrorPropagationRunnable(MutableBlobStorage* aBlobStorage, nsresult aRv)
154 0 : : Runnable("dom::ErrorPropagationRunnable")
155 : , mBlobStorage(aBlobStorage)
156 0 : , mRv(aRv)
157 0 : {}
158 :
159 : NS_IMETHOD
160 0 : Run() override
161 : {
162 0 : mBlobStorage->ErrorPropagated(mRv);
163 0 : return NS_OK;
164 : }
165 :
166 : private:
167 : RefPtr<MutableBlobStorage> mBlobStorage;
168 : nsresult mRv;
169 : };
170 :
171 : // This runnable moves a buffer to the IO thread and there, it writes it into
172 : // the temporary file.
173 : class WriteRunnable final : public Runnable
174 : {
175 : public:
176 : static WriteRunnable*
177 0 : CopyBuffer(MutableBlobStorage* aBlobStorage, PRFileDesc* aFD,
178 : const void* aData, uint32_t aLength)
179 : {
180 0 : MOZ_ASSERT(NS_IsMainThread());
181 0 : MOZ_ASSERT(aBlobStorage);
182 0 : MOZ_ASSERT(aFD);
183 0 : MOZ_ASSERT(aData);
184 :
185 : // We have to take a copy of this buffer.
186 0 : void* data = malloc(aLength);
187 0 : if (!data) {
188 0 : return nullptr;
189 : }
190 :
191 0 : memcpy((char*)data, aData, aLength);
192 0 : return new WriteRunnable(aBlobStorage, aFD, data, aLength);
193 : }
194 :
195 : static WriteRunnable*
196 0 : AdoptBuffer(MutableBlobStorage* aBlobStorage, PRFileDesc* aFD,
197 : void* aData, uint32_t aLength)
198 : {
199 0 : MOZ_ASSERT(NS_IsMainThread());
200 0 : MOZ_ASSERT(aBlobStorage);
201 0 : MOZ_ASSERT(aFD);
202 0 : MOZ_ASSERT(aData);
203 :
204 0 : return new WriteRunnable(aBlobStorage, aFD, aData, aLength);
205 : }
206 :
207 : NS_IMETHOD
208 0 : Run() override
209 : {
210 0 : MOZ_ASSERT(!NS_IsMainThread());
211 0 : MOZ_ASSERT(mBlobStorage);
212 :
213 0 : int32_t written = PR_Write(mFD, mData, mLength);
214 0 : if (NS_WARN_IF(written < 0 || uint32_t(written) != mLength)) {
215 0 : return mBlobStorage->EventTarget()->Dispatch(
216 0 : new ErrorPropagationRunnable(mBlobStorage, NS_ERROR_FAILURE),
217 0 : NS_DISPATCH_NORMAL);
218 : }
219 :
220 0 : return NS_OK;
221 : }
222 :
223 : private:
224 0 : WriteRunnable(MutableBlobStorage* aBlobStorage,
225 : PRFileDesc* aFD,
226 : void* aData,
227 : uint32_t aLength)
228 0 : : Runnable("dom::WriteRunnable")
229 : , mBlobStorage(aBlobStorage)
230 : , mFD(aFD)
231 : , mData(aData)
232 0 : , mLength(aLength)
233 : {
234 0 : MOZ_ASSERT(NS_IsMainThread());
235 0 : MOZ_ASSERT(mBlobStorage);
236 0 : MOZ_ASSERT(aFD);
237 0 : MOZ_ASSERT(aData);
238 0 : }
239 :
240 0 : ~WriteRunnable()
241 0 : {
242 0 : free(mData);
243 0 : }
244 :
245 : RefPtr<MutableBlobStorage> mBlobStorage;
246 : PRFileDesc* mFD;
247 : void* mData;
248 : uint32_t mLength;
249 : };
250 :
251 : // This runnable closes the FD in case something goes wrong or the temporary
252 : // file is not needed anymore.
253 : class CloseFileRunnable final : public Runnable
254 : {
255 : public:
256 0 : explicit CloseFileRunnable(PRFileDesc* aFD)
257 0 : : Runnable("dom::CloseFileRunnable")
258 0 : , mFD(aFD)
259 0 : {}
260 :
261 : NS_IMETHOD
262 0 : Run() override
263 : {
264 0 : MOZ_ASSERT(!NS_IsMainThread());
265 0 : PR_Close(mFD);
266 0 : mFD = nullptr;
267 0 : return NS_OK;
268 : }
269 :
270 : private:
271 0 : ~CloseFileRunnable()
272 0 : {
273 0 : if (mFD) {
274 0 : PR_Close(mFD);
275 : }
276 0 : }
277 :
278 : PRFileDesc* mFD;
279 : };
280 :
281 : // This runnable is dispatched to the main-thread from the IO thread and its
282 : // task is to create the blob and inform the callback.
283 : class CreateBlobRunnable final : public Runnable
284 : {
285 : public:
286 0 : CreateBlobRunnable(MutableBlobStorage* aBlobStorage,
287 : already_AddRefed<nsISupports> aParent,
288 : const nsACString& aContentType,
289 : already_AddRefed<MutableBlobStorageCallback> aCallback)
290 0 : : Runnable("dom::CreateBlobRunnable")
291 : , mBlobStorage(aBlobStorage)
292 : , mParent(aParent)
293 : , mContentType(aContentType)
294 0 : , mCallback(aCallback)
295 : {
296 0 : MOZ_ASSERT(!NS_IsMainThread());
297 0 : MOZ_ASSERT(aBlobStorage);
298 0 : }
299 :
300 : NS_IMETHOD
301 0 : Run() override
302 : {
303 0 : MOZ_ASSERT(NS_IsMainThread());
304 0 : MOZ_ASSERT(mBlobStorage);
305 0 : mBlobStorage->CreateBlobAndRespond(mParent.forget(), mContentType,
306 0 : mCallback.forget());
307 0 : return NS_OK;
308 : }
309 :
310 : private:
311 0 : ~CreateBlobRunnable()
312 0 : {
313 0 : MOZ_ASSERT(mBlobStorage);
314 : // If something when wrong, we still have to release data in the correct
315 : // thread.
316 0 : NS_ProxyRelease(
317 : "CreateBlobRunnable::mParent",
318 0 : mBlobStorage->EventTarget(), mParent.forget());
319 0 : NS_ProxyRelease(
320 : "CreateBlobRunnable::mCallback",
321 0 : mBlobStorage->EventTarget(), mCallback.forget());
322 0 : }
323 :
324 : RefPtr<MutableBlobStorage> mBlobStorage;
325 : nsCOMPtr<nsISupports> mParent;
326 : nsCString mContentType;
327 : RefPtr<MutableBlobStorageCallback> mCallback;
328 : };
329 :
330 : // This task is used to know when the writing is completed. From the IO thread
331 : // it dispatches a CreateBlobRunnable to the main-thread.
332 : class LastRunnable final : public Runnable
333 : {
334 : public:
335 0 : LastRunnable(MutableBlobStorage* aBlobStorage,
336 : nsISupports* aParent,
337 : const nsACString& aContentType,
338 : MutableBlobStorageCallback* aCallback)
339 0 : : Runnable("dom::LastRunnable")
340 : , mBlobStorage(aBlobStorage)
341 : , mParent(aParent)
342 : , mContentType(aContentType)
343 0 : , mCallback(aCallback)
344 : {
345 0 : MOZ_ASSERT(NS_IsMainThread());
346 0 : MOZ_ASSERT(mBlobStorage);
347 0 : MOZ_ASSERT(aCallback);
348 0 : }
349 :
350 : NS_IMETHOD
351 0 : Run() override
352 : {
353 0 : MOZ_ASSERT(!NS_IsMainThread());
354 0 : MOZ_ASSERT(mBlobStorage);
355 : RefPtr<Runnable> runnable =
356 0 : new CreateBlobRunnable(mBlobStorage, mParent.forget(),
357 0 : mContentType, mCallback.forget());
358 0 : return mBlobStorage->EventTarget()->Dispatch(runnable, NS_DISPATCH_NORMAL);
359 : }
360 :
361 : private:
362 0 : ~LastRunnable()
363 0 : {
364 0 : MOZ_ASSERT(mBlobStorage);
365 : // If something when wrong, we still have to release data in the correct
366 : // thread.
367 0 : NS_ProxyRelease(
368 : "LastRunnable::mParent",
369 0 : mBlobStorage->EventTarget(), mParent.forget());
370 0 : NS_ProxyRelease(
371 : "LastRunnable::mCallback",
372 0 : mBlobStorage->EventTarget(), mCallback.forget());
373 0 : }
374 :
375 : RefPtr<MutableBlobStorage> mBlobStorage;
376 : nsCOMPtr<nsISupports> mParent;
377 : nsCString mContentType;
378 : RefPtr<MutableBlobStorageCallback> mCallback;
379 : };
380 :
381 : } // anonymous namespace
382 :
383 0 : MutableBlobStorage::MutableBlobStorage(MutableBlobStorageType aType,
384 0 : nsIEventTarget* aEventTarget)
385 : : mData(nullptr)
386 : , mDataLen(0)
387 : , mDataBufferLen(0)
388 0 : , mStorageState(aType == eOnlyInMemory ? eKeepInMemory : eInMemory)
389 : , mFD(nullptr)
390 : , mErrorResult(NS_OK)
391 0 : , mEventTarget(aEventTarget)
392 : {
393 0 : MOZ_ASSERT(NS_IsMainThread());
394 :
395 0 : if (!mEventTarget) {
396 0 : mEventTarget = GetMainThreadEventTarget();
397 : }
398 :
399 0 : MOZ_ASSERT(mEventTarget);
400 0 : }
401 :
402 0 : MutableBlobStorage::~MutableBlobStorage()
403 : {
404 0 : free(mData);
405 :
406 0 : if (mFD) {
407 0 : RefPtr<Runnable> runnable = new CloseFileRunnable(mFD);
408 0 : DispatchToIOThread(runnable.forget());
409 : }
410 :
411 0 : if (mTaskQueue) {
412 0 : mTaskQueue->BeginShutdown();
413 : }
414 0 : }
415 :
416 : uint64_t
417 0 : MutableBlobStorage::GetBlobWhenReady(nsISupports* aParent,
418 : const nsACString& aContentType,
419 : MutableBlobStorageCallback* aCallback)
420 : {
421 0 : MOZ_ASSERT(NS_IsMainThread());
422 0 : MOZ_ASSERT(aCallback);
423 :
424 : // GetBlob can be called just once.
425 0 : MOZ_ASSERT(mStorageState != eClosed);
426 0 : StorageState previousState = mStorageState;
427 0 : mStorageState = eClosed;
428 :
429 0 : if (previousState == eInTemporaryFile) {
430 0 : MOZ_ASSERT(mFD);
431 :
432 0 : if (NS_FAILED(mErrorResult)) {
433 : RefPtr<Runnable> runnable =
434 0 : new BlobCreationDoneRunnable(this, aCallback, nullptr, mErrorResult);
435 0 : EventTarget()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
436 0 : return 0;
437 : }
438 :
439 : // We want to wait until all the WriteRunnable are completed. The way we do
440 : // this is to go to the I/O thread and then we come back: the runnables are
441 : // executed in order and this LastRunnable will be... the last one.
442 : RefPtr<Runnable> runnable =
443 0 : new LastRunnable(this, aParent, aContentType, aCallback);
444 0 : DispatchToIOThread(runnable.forget());
445 0 : return mDataLen;
446 : }
447 :
448 : // If we are waiting for the temporary file, it's better to wait...
449 0 : if (previousState == eWaitingForTemporaryFile) {
450 0 : mPendingParent = aParent;
451 0 : mPendingContentType = aContentType;
452 0 : mPendingCallback = aCallback;
453 0 : return mDataLen;
454 : }
455 :
456 0 : RefPtr<BlobImpl> blobImpl;
457 :
458 0 : if (mData) {
459 : blobImpl = new MemoryBlobImpl(mData, mDataLen,
460 0 : NS_ConvertUTF8toUTF16(aContentType));
461 :
462 0 : mData = nullptr; // The MemoryBlobImpl takes ownership of the buffer
463 0 : mDataLen = 0;
464 0 : mDataBufferLen = 0;
465 : } else {
466 0 : blobImpl = new EmptyBlobImpl(NS_ConvertUTF8toUTF16(aContentType));
467 : }
468 :
469 0 : RefPtr<Blob> blob = Blob::Create(aParent, blobImpl);
470 : RefPtr<BlobCreationDoneRunnable> runnable =
471 0 : new BlobCreationDoneRunnable(this, aCallback, blob, NS_OK);
472 :
473 0 : nsresult error = EventTarget()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
474 0 : if (NS_WARN_IF(NS_FAILED(error))) {
475 0 : return 0;
476 : }
477 :
478 0 : return mDataLen;
479 : }
480 :
481 : nsresult
482 0 : MutableBlobStorage::Append(const void* aData, uint32_t aLength)
483 : {
484 0 : MOZ_ASSERT(NS_IsMainThread());
485 0 : MOZ_ASSERT(mStorageState != eClosed);
486 0 : NS_ENSURE_ARG_POINTER(aData);
487 :
488 0 : if (!aLength) {
489 0 : return NS_OK;
490 : }
491 :
492 : // If eInMemory is the current Storage state, we could maybe migrate to
493 : // a temporary file.
494 0 : if (mStorageState == eInMemory && ShouldBeTemporaryStorage(aLength)) {
495 0 : nsresult rv = MaybeCreateTemporaryFile();
496 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
497 0 : return rv;
498 : }
499 : }
500 :
501 : // If we are already in the temporaryFile mode, we have to dispatch a
502 : // runnable.
503 0 : if (mStorageState == eInTemporaryFile) {
504 0 : MOZ_ASSERT(mFD);
505 :
506 : RefPtr<WriteRunnable> runnable =
507 0 : WriteRunnable::CopyBuffer(this, mFD, aData, aLength);
508 0 : if (NS_WARN_IF(!runnable)) {
509 0 : return NS_ERROR_OUT_OF_MEMORY;
510 : }
511 :
512 0 : DispatchToIOThread(runnable.forget());
513 :
514 0 : mDataLen += aLength;
515 0 : return NS_OK;
516 : }
517 :
518 : // By default, we store in memory.
519 :
520 0 : uint64_t offset = mDataLen;
521 :
522 0 : if (!ExpandBufferSize(aLength)) {
523 0 : return NS_ERROR_OUT_OF_MEMORY;
524 : }
525 :
526 0 : memcpy((char*)mData + offset, aData, aLength);
527 0 : return NS_OK;
528 : }
529 :
530 : bool
531 0 : MutableBlobStorage::ExpandBufferSize(uint64_t aSize)
532 : {
533 0 : MOZ_ASSERT(NS_IsMainThread());
534 0 : MOZ_ASSERT(mStorageState < eInTemporaryFile);
535 :
536 0 : if (mDataBufferLen >= mDataLen + aSize) {
537 0 : mDataLen += aSize;
538 0 : return true;
539 : }
540 :
541 : // Start at 1 or we'll loop forever.
542 : CheckedUint32 bufferLen =
543 0 : std::max<uint32_t>(static_cast<uint32_t>(mDataBufferLen), 1);
544 0 : while (bufferLen.isValid() && bufferLen.value() < mDataLen + aSize) {
545 0 : bufferLen *= 2;
546 : }
547 :
548 0 : if (!bufferLen.isValid()) {
549 0 : return false;
550 : }
551 :
552 0 : void* data = realloc(mData, bufferLen.value());
553 0 : if (!data) {
554 0 : return false;
555 : }
556 :
557 0 : mData = data;
558 0 : mDataBufferLen = bufferLen.value();
559 0 : mDataLen += aSize;
560 0 : return true;
561 : }
562 :
563 : bool
564 0 : MutableBlobStorage::ShouldBeTemporaryStorage(uint64_t aSize) const
565 : {
566 0 : MOZ_ASSERT(mStorageState == eInMemory);
567 :
568 0 : CheckedUint32 bufferSize = mDataLen;
569 0 : bufferSize += aSize;
570 :
571 0 : if (!bufferSize.isValid()) {
572 0 : return false;
573 : }
574 :
575 0 : return bufferSize.value() >= Preferences::GetUint("dom.blob.memoryToTemporaryFile",
576 0 : BLOB_MEMORY_TEMPORARY_FILE);
577 : }
578 :
579 : nsresult
580 0 : MutableBlobStorage::MaybeCreateTemporaryFile()
581 : {
582 0 : if (XRE_IsParentProcess()) {
583 0 : RefPtr<Runnable> runnable = new CreateTemporaryFileRunnable(this);
584 0 : DispatchToIOThread(runnable.forget());
585 : } else {
586 0 : RefPtr<MutableBlobStorage> self(this);
587 : ContentChild::GetSingleton()->
588 0 : AsyncOpenAnonymousTemporaryFile([self](PRFileDesc* prfile) {
589 0 : if (prfile) {
590 : // The ownership of the prfile is moved to the FileCreatedRunnable.
591 0 : self->EventTarget()->Dispatch(
592 0 : new FileCreatedRunnable(self, prfile), NS_DISPATCH_NORMAL);
593 : }
594 0 : });
595 : }
596 :
597 0 : mStorageState = eWaitingForTemporaryFile;
598 0 : return NS_OK;
599 : }
600 :
601 : void
602 0 : MutableBlobStorage::TemporaryFileCreated(PRFileDesc* aFD)
603 : {
604 0 : MOZ_ASSERT(NS_IsMainThread());
605 0 : MOZ_ASSERT(mStorageState == eWaitingForTemporaryFile ||
606 : mStorageState == eClosed);
607 0 : MOZ_ASSERT_IF(mPendingCallback, mStorageState == eClosed);
608 :
609 : // If the object has been already closed and we don't need to execute a
610 : // callback, we need just to close the file descriptor in the correct thread.
611 0 : if (mStorageState == eClosed && !mPendingCallback) {
612 0 : RefPtr<Runnable> runnable = new CloseFileRunnable(aFD);
613 0 : DispatchToIOThread(runnable.forget());
614 0 : return;
615 : }
616 :
617 : // If we still receiving data, we can proceed in temporary-file mode.
618 0 : if (mStorageState == eWaitingForTemporaryFile) {
619 0 : mStorageState = eInTemporaryFile;
620 : }
621 :
622 0 : mFD = aFD;
623 :
624 : // This runnable takes the ownership of mData and it will write this buffer
625 : // into the temporary file.
626 : RefPtr<WriteRunnable> runnable =
627 0 : WriteRunnable::AdoptBuffer(this, mFD, mData, mDataLen);
628 0 : MOZ_ASSERT(runnable);
629 :
630 0 : mData = nullptr;
631 :
632 0 : DispatchToIOThread(runnable.forget());
633 :
634 : // If we are closed, it means that GetBlobWhenReady() has been called when we
635 : // were already waiting for a temporary file-descriptor. Finally we are here,
636 : // AdoptBuffer runnable is going to write the current buffer into this file.
637 : // After that, there is nothing else to write, and we dispatch LastRunnable
638 : // which ends up calling mPendingCallback via CreateBlobRunnable.
639 0 : if (mStorageState == eClosed) {
640 0 : MOZ_ASSERT(mPendingCallback);
641 :
642 : RefPtr<Runnable> runnable =
643 : new LastRunnable(this, mPendingParent, mPendingContentType,
644 0 : mPendingCallback);
645 0 : DispatchToIOThread(runnable.forget());
646 :
647 0 : mPendingParent = nullptr;
648 0 : mPendingCallback = nullptr;
649 : }
650 : }
651 :
652 : void
653 0 : MutableBlobStorage::CreateBlobAndRespond(already_AddRefed<nsISupports> aParent,
654 : const nsACString& aContentType,
655 : already_AddRefed<MutableBlobStorageCallback> aCallback)
656 : {
657 0 : MOZ_ASSERT(NS_IsMainThread());
658 0 : MOZ_ASSERT(mStorageState == eClosed);
659 0 : MOZ_ASSERT(mFD);
660 :
661 0 : nsCOMPtr<nsISupports> parent(aParent);
662 0 : RefPtr<MutableBlobStorageCallback> callback(aCallback);
663 :
664 : RefPtr<Blob> blob =
665 0 : File::CreateTemporaryBlob(parent, mFD, 0, mDataLen,
666 0 : NS_ConvertUTF8toUTF16(aContentType));
667 0 : callback->BlobStoreCompleted(this, blob, NS_OK);
668 :
669 : // ownership of this FD is moved to the BlobImpl.
670 0 : mFD = nullptr;
671 0 : }
672 :
673 : void
674 0 : MutableBlobStorage::ErrorPropagated(nsresult aRv)
675 : {
676 0 : MOZ_ASSERT(NS_IsMainThread());
677 0 : mErrorResult = aRv;
678 0 : }
679 :
680 : void
681 0 : MutableBlobStorage::DispatchToIOThread(already_AddRefed<nsIRunnable> aRunnable)
682 : {
683 0 : if (!mTaskQueue) {
684 : nsCOMPtr<nsIEventTarget> target
685 0 : = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
686 0 : MOZ_ASSERT(target);
687 :
688 0 : mTaskQueue = new TaskQueue(target.forget());
689 : }
690 :
691 0 : nsCOMPtr<nsIRunnable> runnable(aRunnable);
692 0 : mTaskQueue->Dispatch(runnable.forget());
693 0 : }
694 :
695 : } // dom namespace
696 : } // mozilla namespace
|