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 "Fetch.h"
8 : #include "FetchConsumer.h"
9 :
10 : #include "nsIInputStreamPump.h"
11 : #include "nsProxyRelease.h"
12 : #include "WorkerPrivate.h"
13 : #include "WorkerRunnable.h"
14 : #include "WorkerScope.h"
15 : #include "Workers.h"
16 :
17 : namespace mozilla {
18 : namespace dom {
19 :
20 : using namespace workers;
21 :
22 : namespace {
23 :
24 : template <class Derived>
25 : class FetchBodyWorkerHolder final : public workers::WorkerHolder
26 : {
27 : RefPtr<FetchBodyConsumer<Derived>> mConsumer;
28 : bool mWasNotified;
29 :
30 : public:
31 0 : explicit FetchBodyWorkerHolder(FetchBodyConsumer<Derived>* aConsumer)
32 : : mConsumer(aConsumer)
33 0 : , mWasNotified(false)
34 : {
35 0 : MOZ_ASSERT(aConsumer);
36 0 : }
37 :
38 0 : ~FetchBodyWorkerHolder() = default;
39 :
40 0 : bool Notify(workers::Status aStatus) override
41 : {
42 0 : MOZ_ASSERT(aStatus > workers::Running);
43 0 : if (!mWasNotified) {
44 0 : mWasNotified = true;
45 : // This will probably cause the releasing of the consumer.
46 : // The WorkerHolder will be released as well.
47 0 : mConsumer->ContinueConsumeBody(NS_BINDING_ABORTED, 0, nullptr);
48 : }
49 :
50 0 : return true;
51 : }
52 : };
53 :
54 : template <class Derived>
55 3 : class BeginConsumeBodyRunnable final : public Runnable
56 : {
57 : RefPtr<FetchBodyConsumer<Derived>> mFetchBodyConsumer;
58 :
59 : public:
60 1 : explicit BeginConsumeBodyRunnable(FetchBodyConsumer<Derived>* aConsumer)
61 : : Runnable("BeginConsumeBodyRunnable")
62 1 : , mFetchBodyConsumer(aConsumer)
63 1 : { }
64 :
65 : NS_IMETHOD
66 1 : Run() override
67 : {
68 1 : mFetchBodyConsumer->BeginConsumeBodyMainThread();
69 1 : return NS_OK;
70 : }
71 : };
72 :
73 : /*
74 : * Called on successfully reading the complete stream.
75 : */
76 : template <class Derived>
77 0 : class ContinueConsumeBodyRunnable final : public MainThreadWorkerRunnable
78 : {
79 : RefPtr<FetchBodyConsumer<Derived>> mFetchBodyConsumer;
80 : nsresult mStatus;
81 : uint32_t mLength;
82 : uint8_t* mResult;
83 :
84 : public:
85 0 : ContinueConsumeBodyRunnable(FetchBodyConsumer<Derived>* aFetchBodyConsumer,
86 : nsresult aStatus, uint32_t aLength,
87 : uint8_t* aResult)
88 : : MainThreadWorkerRunnable(aFetchBodyConsumer->GetWorkerPrivate())
89 : , mFetchBodyConsumer(aFetchBodyConsumer)
90 : , mStatus(aStatus)
91 : , mLength(aLength)
92 0 : , mResult(aResult)
93 : {
94 0 : MOZ_ASSERT(NS_IsMainThread());
95 0 : }
96 :
97 : bool
98 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
99 : {
100 0 : mFetchBodyConsumer->ContinueConsumeBody(mStatus, mLength, mResult);
101 0 : return true;
102 : }
103 : };
104 :
105 : template <class Derived>
106 0 : class FailConsumeBodyWorkerRunnable : public MainThreadWorkerControlRunnable
107 : {
108 : RefPtr<FetchBodyConsumer<Derived>> mBodyConsumer;
109 :
110 : public:
111 0 : explicit FailConsumeBodyWorkerRunnable(FetchBodyConsumer<Derived>* aBodyConsumer)
112 : : MainThreadWorkerControlRunnable(aBodyConsumer->GetWorkerPrivate())
113 0 : , mBodyConsumer(aBodyConsumer)
114 : {
115 0 : AssertIsOnMainThread();
116 0 : }
117 :
118 : bool
119 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
120 : {
121 0 : mBodyConsumer->ContinueConsumeBody(NS_ERROR_FAILURE, 0, nullptr);
122 0 : return true;
123 : }
124 : };
125 :
126 : /*
127 : * In case of failure to create a stream pump or dispatch stream completion to
128 : * worker, ensure we cleanup properly. Thread agnostic.
129 : */
130 : template <class Derived>
131 : class MOZ_STACK_CLASS AutoFailConsumeBody final
132 : {
133 : RefPtr<FetchBodyConsumer<Derived>> mBodyConsumer;
134 :
135 : public:
136 1 : explicit AutoFailConsumeBody(FetchBodyConsumer<Derived>* aBodyConsumer)
137 1 : : mBodyConsumer(aBodyConsumer)
138 1 : {}
139 :
140 1 : ~AutoFailConsumeBody()
141 : {
142 1 : AssertIsOnMainThread();
143 :
144 1 : if (mBodyConsumer) {
145 0 : if (mBodyConsumer->GetWorkerPrivate()) {
146 : RefPtr<FailConsumeBodyWorkerRunnable<Derived>> r =
147 0 : new FailConsumeBodyWorkerRunnable<Derived>(mBodyConsumer);
148 0 : if (!r->Dispatch()) {
149 0 : MOZ_CRASH("We are going to leak");
150 : }
151 : } else {
152 0 : mBodyConsumer->ContinueConsumeBody(NS_ERROR_FAILURE, 0, nullptr);
153 : }
154 : }
155 1 : }
156 :
157 : void
158 1 : DontFail()
159 : {
160 1 : mBodyConsumer = nullptr;
161 1 : }
162 : };
163 :
164 : /*
165 : * Called on successfully reading the complete stream for Blob.
166 : */
167 : template <class Derived>
168 0 : class ContinueConsumeBlobBodyRunnable final : public MainThreadWorkerRunnable
169 : {
170 : RefPtr<FetchBodyConsumer<Derived>> mFetchBodyConsumer;
171 : RefPtr<BlobImpl> mBlobImpl;
172 :
173 : public:
174 0 : ContinueConsumeBlobBodyRunnable(FetchBodyConsumer<Derived>* aFetchBodyConsumer,
175 : BlobImpl* aBlobImpl)
176 : : MainThreadWorkerRunnable(aFetchBodyConsumer->GetWorkerPrivate())
177 : , mFetchBodyConsumer(aFetchBodyConsumer)
178 0 : , mBlobImpl(aBlobImpl)
179 : {
180 0 : MOZ_ASSERT(NS_IsMainThread());
181 0 : MOZ_ASSERT(mBlobImpl);
182 0 : }
183 :
184 : bool
185 0 : WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
186 : {
187 0 : mFetchBodyConsumer->ContinueConsumeBlobBody(mBlobImpl);
188 0 : return true;
189 : }
190 : };
191 :
192 : template <class Derived>
193 : class ConsumeBodyDoneObserver : public nsIStreamLoaderObserver
194 : , public MutableBlobStorageCallback
195 : {
196 : RefPtr<FetchBodyConsumer<Derived>> mFetchBodyConsumer;
197 :
198 : public:
199 : NS_DECL_THREADSAFE_ISUPPORTS
200 :
201 1 : explicit ConsumeBodyDoneObserver(FetchBodyConsumer<Derived>* aFetchBodyConsumer)
202 1 : : mFetchBodyConsumer(aFetchBodyConsumer)
203 1 : { }
204 :
205 : NS_IMETHOD
206 1 : OnStreamComplete(nsIStreamLoader* aLoader,
207 : nsISupports* aCtxt,
208 : nsresult aStatus,
209 : uint32_t aResultLength,
210 : const uint8_t* aResult) override
211 : {
212 1 : MOZ_ASSERT(NS_IsMainThread());
213 :
214 : // The loading is completed. Let's nullify the pump before continuing the
215 : // consuming of the body.
216 1 : mFetchBodyConsumer->NullifyConsumeBodyPump();
217 :
218 : // If the binding requested cancel, we don't need to call
219 : // ContinueConsumeBody, since that is the originator.
220 1 : if (aStatus == NS_BINDING_ABORTED) {
221 0 : return NS_OK;
222 : }
223 :
224 1 : uint8_t* nonconstResult = const_cast<uint8_t*>(aResult);
225 1 : if (mFetchBodyConsumer->GetWorkerPrivate()) {
226 : RefPtr<ContinueConsumeBodyRunnable<Derived>> r =
227 0 : new ContinueConsumeBodyRunnable<Derived>(mFetchBodyConsumer,
228 : aStatus,
229 : aResultLength,
230 0 : nonconstResult);
231 0 : if (!r->Dispatch()) {
232 0 : NS_WARNING("Could not dispatch ConsumeBodyRunnable");
233 : // Return failure so that aResult is freed.
234 0 : return NS_ERROR_FAILURE;
235 : }
236 : } else {
237 1 : mFetchBodyConsumer->ContinueConsumeBody(aStatus, aResultLength,
238 : nonconstResult);
239 : }
240 :
241 : // FetchBody is responsible for data.
242 1 : return NS_SUCCESS_ADOPTED_DATA;
243 : }
244 :
245 0 : virtual void BlobStoreCompleted(MutableBlobStorage* aBlobStorage,
246 : Blob* aBlob,
247 : nsresult aRv) override
248 : {
249 : // On error.
250 0 : if (NS_FAILED(aRv)) {
251 0 : OnStreamComplete(nullptr, nullptr, aRv, 0, nullptr);
252 0 : return;
253 : }
254 :
255 0 : MOZ_ASSERT(aBlob);
256 :
257 0 : if (mFetchBodyConsumer->GetWorkerPrivate()) {
258 : RefPtr<ContinueConsumeBlobBodyRunnable<Derived>> r =
259 0 : new ContinueConsumeBlobBodyRunnable<Derived>(mFetchBodyConsumer,
260 0 : aBlob->Impl());
261 :
262 0 : if (!r->Dispatch()) {
263 0 : NS_WARNING("Could not dispatch ConsumeBlobBodyRunnable");
264 0 : return;
265 : }
266 : } else {
267 0 : mFetchBodyConsumer->ContinueConsumeBlobBody(aBlob->Impl());
268 : }
269 : }
270 :
271 : private:
272 2 : virtual ~ConsumeBodyDoneObserver()
273 3 : { }
274 : };
275 :
276 : template <class Derived>
277 3 : NS_IMPL_ADDREF(ConsumeBodyDoneObserver<Derived>)
278 : template <class Derived>
279 3 : NS_IMPL_RELEASE(ConsumeBodyDoneObserver<Derived>)
280 : template <class Derived>
281 1 : NS_INTERFACE_MAP_BEGIN(ConsumeBodyDoneObserver<Derived>)
282 1 : NS_INTERFACE_MAP_ENTRY(nsIStreamLoaderObserver)
283 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamLoaderObserver)
284 0 : NS_INTERFACE_MAP_END
285 :
286 : template <class Derived>
287 0 : class ShutDownMainThreadConsumingRunnable final : public WorkerMainThreadRunnable
288 : {
289 : RefPtr<FetchBodyConsumer<Derived>> mBodyConsumer;
290 :
291 : public:
292 0 : explicit ShutDownMainThreadConsumingRunnable(FetchBodyConsumer<Derived>* aBodyConsumer)
293 : : WorkerMainThreadRunnable(aBodyConsumer->GetWorkerPrivate(),
294 : NS_LITERAL_CSTRING("Fetch :: Cancel Pump"))
295 0 : , mBodyConsumer(aBodyConsumer)
296 0 : {}
297 :
298 : bool
299 0 : MainThreadRun() override
300 : {
301 0 : mBodyConsumer->ShutDownMainThreadConsuming();
302 0 : return true;
303 : }
304 : };
305 :
306 : } // anonymous
307 :
308 : template <class Derived>
309 : /* static */ already_AddRefed<Promise>
310 1 : FetchBodyConsumer<Derived>::Create(nsIGlobalObject* aGlobal,
311 : nsIEventTarget* aMainThreadEventTarget,
312 : FetchBody<Derived>* aBody,
313 : FetchConsumeType aType,
314 : ErrorResult& aRv)
315 : {
316 1 : MOZ_ASSERT(aBody);
317 1 : MOZ_ASSERT(aMainThreadEventTarget);
318 :
319 2 : RefPtr<Promise> promise = Promise::Create(aGlobal, aRv);
320 1 : if (aRv.Failed()) {
321 0 : return nullptr;
322 : }
323 :
324 1 : WorkerPrivate* workerPrivate = nullptr;
325 1 : if (!NS_IsMainThread()) {
326 0 : workerPrivate = GetCurrentThreadWorkerPrivate();
327 0 : MOZ_ASSERT(workerPrivate);
328 : }
329 :
330 : RefPtr<FetchBodyConsumer<Derived>> consumer =
331 2 : new FetchBodyConsumer<Derived>(aMainThreadEventTarget, aGlobal,
332 2 : workerPrivate, aBody, promise, aType);
333 :
334 1 : if (!NS_IsMainThread()) {
335 0 : MOZ_ASSERT(workerPrivate);
336 0 : if (NS_WARN_IF(!consumer->RegisterWorkerHolder(workerPrivate))) {
337 0 : aRv.Throw(NS_ERROR_FAILURE);
338 0 : return nullptr;
339 : }
340 : } else {
341 2 : nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
342 1 : if (NS_WARN_IF(!os)) {
343 0 : aRv.Throw(NS_ERROR_FAILURE);
344 0 : return nullptr;
345 : }
346 :
347 1 : aRv = os->AddObserver(consumer, DOM_WINDOW_DESTROYED_TOPIC, true);
348 1 : if (NS_WARN_IF(aRv.Failed())) {
349 0 : return nullptr;
350 : }
351 :
352 1 : aRv = os->AddObserver(consumer, DOM_WINDOW_FROZEN_TOPIC, true);
353 1 : if (NS_WARN_IF(aRv.Failed())) {
354 0 : return nullptr;
355 : }
356 : }
357 :
358 3 : nsCOMPtr<nsIRunnable> r = new BeginConsumeBodyRunnable<Derived>(consumer);
359 1 : aRv = aMainThreadEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
360 1 : if (NS_WARN_IF(aRv.Failed())) {
361 0 : return nullptr;
362 : }
363 :
364 1 : return promise.forget();
365 : }
366 :
367 : template <class Derived>
368 : void
369 1 : FetchBodyConsumer<Derived>::ReleaseObject()
370 : {
371 1 : AssertIsOnTargetThread();
372 :
373 1 : if (NS_IsMainThread()) {
374 2 : nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
375 1 : if (os) {
376 1 : os->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
377 1 : os->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC);
378 : }
379 : }
380 :
381 1 : mGlobal = nullptr;
382 1 : mWorkerHolder = nullptr;
383 1 : mBody = nullptr;
384 1 : }
385 :
386 : template <class Derived>
387 1 : FetchBodyConsumer<Derived>::FetchBodyConsumer(nsIEventTarget* aMainThreadEventTarget,
388 : nsIGlobalObject* aGlobalObject,
389 : WorkerPrivate* aWorkerPrivate,
390 : FetchBody<Derived>* aBody,
391 : Promise* aPromise,
392 : FetchConsumeType aType)
393 : : mTargetThread(NS_GetCurrentThread())
394 : , mMainThreadEventTarget(aMainThreadEventTarget)
395 : , mBody(aBody)
396 : , mGlobal(aGlobalObject)
397 : , mWorkerPrivate(aWorkerPrivate)
398 : , mConsumeType(aType)
399 : , mConsumePromise(aPromise)
400 : , mBodyConsumed(false)
401 1 : , mShuttingDown(false)
402 : {
403 1 : MOZ_ASSERT(aMainThreadEventTarget);
404 1 : MOZ_ASSERT(aBody);
405 1 : MOZ_ASSERT(aPromise);
406 1 : }
407 :
408 : template <class Derived>
409 1 : FetchBodyConsumer<Derived>::~FetchBodyConsumer()
410 : {
411 1 : }
412 :
413 : template <class Derived>
414 : void
415 2 : FetchBodyConsumer<Derived>::AssertIsOnTargetThread() const
416 : {
417 2 : MOZ_ASSERT(NS_GetCurrentThread() == mTargetThread);
418 2 : }
419 :
420 : template <class Derived>
421 : bool
422 0 : FetchBodyConsumer<Derived>::RegisterWorkerHolder(WorkerPrivate* aWorkerPrivate)
423 : {
424 0 : MOZ_ASSERT(aWorkerPrivate);
425 0 : aWorkerPrivate->AssertIsOnWorkerThread();
426 :
427 0 : MOZ_ASSERT(!mWorkerHolder);
428 0 : mWorkerHolder.reset(new FetchBodyWorkerHolder<Derived>(this));
429 :
430 0 : if (!mWorkerHolder->HoldWorker(aWorkerPrivate, Closing)) {
431 0 : NS_WARNING("Failed to add workerHolder");
432 0 : mWorkerHolder = nullptr;
433 0 : return false;
434 : }
435 :
436 0 : return true;
437 : }
438 :
439 : /*
440 : * BeginConsumeBodyMainThread() will automatically reject the consume promise
441 : * and clean up on any failures, so there is no need for callers to do so,
442 : * reflected in a lack of error return code.
443 : */
444 : template <class Derived>
445 : void
446 1 : FetchBodyConsumer<Derived>::BeginConsumeBodyMainThread()
447 : {
448 1 : AssertIsOnMainThread();
449 :
450 : // Nothing to do.
451 1 : if (mShuttingDown) {
452 0 : return;
453 : }
454 :
455 2 : AutoFailConsumeBody<Derived> autoReject(this);
456 :
457 : nsresult rv;
458 2 : nsCOMPtr<nsIInputStream> stream;
459 1 : mBody->DerivedClass()->GetBody(getter_AddRefs(stream));
460 1 : if (!stream) {
461 0 : rv = NS_NewCStringInputStream(getter_AddRefs(stream), EmptyCString());
462 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
463 0 : return;
464 : }
465 : }
466 :
467 2 : nsCOMPtr<nsIInputStreamPump> pump;
468 1 : rv = NS_NewInputStreamPump(getter_AddRefs(pump),
469 : stream, -1, -1, 0, 0, false,
470 : mMainThreadEventTarget);
471 1 : if (NS_WARN_IF(NS_FAILED(rv))) {
472 0 : return;
473 : }
474 :
475 : RefPtr<ConsumeBodyDoneObserver<Derived>> p =
476 2 : new ConsumeBodyDoneObserver<Derived>(this);
477 :
478 2 : nsCOMPtr<nsIStreamListener> listener;
479 1 : if (mConsumeType == CONSUME_BLOB) {
480 : MutableBlobStorage::MutableBlobStorageType type =
481 0 : MutableBlobStorage::eOnlyInMemory;
482 :
483 : const mozilla::UniquePtr<mozilla::ipc::PrincipalInfo>& principalInfo =
484 0 : mBody->DerivedClass()->GetPrincipalInfo();
485 : // We support temporary file for blobs only if the principal is known and
486 : // it's system or content not in private Browsing.
487 0 : if (principalInfo &&
488 0 : (principalInfo->type() == mozilla::ipc::PrincipalInfo::TSystemPrincipalInfo ||
489 0 : (principalInfo->type() == mozilla::ipc::PrincipalInfo::TContentPrincipalInfo &&
490 0 : principalInfo->get_ContentPrincipalInfo().attrs().mPrivateBrowsingId == 0))) {
491 0 : type = MutableBlobStorage::eCouldBeInTemporaryFile;
492 : }
493 :
494 0 : listener = new MutableBlobStreamListener(type, nullptr, mBody->MimeType(),
495 : p, mMainThreadEventTarget);
496 : } else {
497 2 : nsCOMPtr<nsIStreamLoader> loader;
498 1 : rv = NS_NewStreamLoader(getter_AddRefs(loader), p);
499 1 : if (NS_WARN_IF(NS_FAILED(rv))) {
500 0 : return;
501 : }
502 :
503 1 : listener = loader;
504 : }
505 :
506 1 : rv = pump->AsyncRead(listener, nullptr);
507 1 : if (NS_WARN_IF(NS_FAILED(rv))) {
508 0 : return;
509 : }
510 :
511 : // Now that everything succeeded, we can assign the pump to a pointer that
512 : // stays alive for the lifetime of the FetchConsumer.
513 1 : mConsumeBodyPump = pump;
514 :
515 : // It is ok for retargeting to fail and reads to happen on the main thread.
516 1 : autoReject.DontFail();
517 :
518 : // Try to retarget, otherwise fall back to main thread.
519 2 : nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(pump);
520 1 : if (rr) {
521 2 : nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
522 1 : rv = rr->RetargetDeliveryTo(sts);
523 1 : if (NS_WARN_IF(NS_FAILED(rv))) {
524 0 : NS_WARNING("Retargeting failed");
525 : }
526 : }
527 : }
528 :
529 : template <class Derived>
530 : void
531 1 : FetchBodyConsumer<Derived>::ContinueConsumeBody(nsresult aStatus,
532 : uint32_t aResultLength,
533 : uint8_t* aResult)
534 : {
535 1 : AssertIsOnTargetThread();
536 :
537 1 : if (mBodyConsumed) {
538 0 : return;
539 : }
540 1 : mBodyConsumed = true;
541 :
542 : // Just a precaution to ensure ContinueConsumeBody is not called out of
543 : // sync with a body read.
544 1 : MOZ_ASSERT(mBody->BodyUsed());
545 :
546 1 : auto autoFree = mozilla::MakeScopeExit([&] {
547 1 : free(aResult);
548 3 : });
549 :
550 1 : MOZ_ASSERT(mConsumePromise);
551 2 : RefPtr<Promise> localPromise = mConsumePromise.forget();
552 :
553 2 : RefPtr<FetchBodyConsumer<Derived>> self = this;
554 1 : auto autoReleaseObject = mozilla::MakeScopeExit([&] {
555 1 : self->ReleaseObject();
556 3 : });
557 :
558 1 : if (NS_WARN_IF(NS_FAILED(aStatus))) {
559 0 : localPromise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
560 : }
561 :
562 : // We need to nullify mConsumeBodyPump on the main-thread and, in case it has
563 : // not been created yet, we need to block the creation setting mShuttingDown
564 : // to true.
565 1 : ShutDownMainThreadConsuming();
566 :
567 : // Don't warn here since we warned above.
568 1 : if (NS_FAILED(aStatus)) {
569 0 : return;
570 : }
571 :
572 : // Finish successfully consuming body according to type.
573 1 : MOZ_ASSERT(aResult);
574 :
575 2 : AutoJSAPI jsapi;
576 1 : if (!jsapi.Init(mBody->DerivedClass()->GetParentObject())) {
577 0 : localPromise->MaybeReject(NS_ERROR_UNEXPECTED);
578 0 : return;
579 : }
580 :
581 1 : JSContext* cx = jsapi.cx();
582 2 : ErrorResult error;
583 :
584 1 : switch (mConsumeType) {
585 : case CONSUME_ARRAYBUFFER: {
586 0 : JS::Rooted<JSObject*> arrayBuffer(cx);
587 0 : BodyUtil::ConsumeArrayBuffer(cx, &arrayBuffer, aResultLength, aResult,
588 : error);
589 :
590 0 : if (!error.Failed()) {
591 0 : JS::Rooted<JS::Value> val(cx);
592 0 : val.setObjectOrNull(arrayBuffer);
593 :
594 0 : localPromise->MaybeResolve(cx, val);
595 : // ArrayBuffer takes over ownership.
596 0 : aResult = nullptr;
597 : }
598 0 : break;
599 : }
600 : case CONSUME_BLOB: {
601 0 : MOZ_CRASH("This should not happen.");
602 : break;
603 : }
604 : case CONSUME_FORMDATA: {
605 0 : nsCString data;
606 0 : data.Adopt(reinterpret_cast<char*>(aResult), aResultLength);
607 0 : aResult = nullptr;
608 :
609 : RefPtr<dom::FormData> fd =
610 : BodyUtil::ConsumeFormData(mBody->DerivedClass()->GetParentObject(),
611 0 : mBody->MimeType(), data, error);
612 0 : if (!error.Failed()) {
613 0 : localPromise->MaybeResolve(fd);
614 : }
615 0 : break;
616 : }
617 : case CONSUME_TEXT:
618 : // fall through handles early exit.
619 : case CONSUME_JSON: {
620 2 : nsString decoded;
621 1 : if (NS_SUCCEEDED(BodyUtil::ConsumeText(aResultLength, aResult, decoded))) {
622 1 : if (mConsumeType == CONSUME_TEXT) {
623 0 : localPromise->MaybeResolve(decoded);
624 : } else {
625 2 : JS::Rooted<JS::Value> json(cx);
626 1 : BodyUtil::ConsumeJson(cx, &json, decoded, error);
627 1 : if (!error.Failed()) {
628 1 : localPromise->MaybeResolve(cx, json);
629 : }
630 : }
631 : };
632 1 : break;
633 : }
634 : default:
635 0 : NS_NOTREACHED("Unexpected consume body type");
636 : }
637 :
638 1 : error.WouldReportJSException();
639 1 : if (error.Failed()) {
640 0 : localPromise->MaybeReject(error);
641 : }
642 : }
643 :
644 : template <class Derived>
645 : void
646 0 : FetchBodyConsumer<Derived>::ContinueConsumeBlobBody(BlobImpl* aBlobImpl)
647 : {
648 0 : AssertIsOnTargetThread();
649 0 : MOZ_ASSERT(mConsumeType == CONSUME_BLOB);
650 :
651 0 : if (mBodyConsumed) {
652 0 : return;
653 : }
654 0 : mBodyConsumed = true;
655 :
656 : // Just a precaution to ensure ContinueConsumeBody is not called out of
657 : // sync with a body read.
658 0 : MOZ_ASSERT(mBody->BodyUsed());
659 :
660 0 : MOZ_ASSERT(mConsumePromise);
661 0 : RefPtr<Promise> localPromise = mConsumePromise.forget();
662 :
663 : // Release the pump.
664 0 : ShutDownMainThreadConsuming();
665 :
666 : RefPtr<dom::Blob> blob =
667 0 : dom::Blob::Create(mBody->DerivedClass()->GetParentObject(), aBlobImpl);
668 0 : MOZ_ASSERT(blob);
669 :
670 0 : localPromise->MaybeResolve(blob);
671 :
672 0 : ReleaseObject();
673 : }
674 :
675 : template <class Derived>
676 : void
677 1 : FetchBodyConsumer<Derived>::ShutDownMainThreadConsuming()
678 : {
679 1 : if (!NS_IsMainThread()) {
680 0 : MOZ_ASSERT(mWorkerPrivate);
681 : // In case of worker thread, we block the worker while the request is
682 : // canceled on the main thread. This ensures that OnStreamComplete has a
683 : // valid FetchConsumer around to call ShutDownMainThreadConsuming and we
684 : // don't release the FetchConsumer on the main thread.
685 : RefPtr<ShutDownMainThreadConsumingRunnable<Derived>> r =
686 0 : new ShutDownMainThreadConsumingRunnable<Derived>(this);
687 :
688 0 : IgnoredErrorResult rv;
689 0 : r->Dispatch(Killing, rv);
690 0 : if (NS_WARN_IF(rv.Failed())) {
691 0 : return;
692 : }
693 :
694 0 : MOZ_DIAGNOSTIC_ASSERT(mShuttingDown);
695 0 : return;
696 : }
697 :
698 : // We need this because maybe, mConsumeBodyPump has not been created yet. We
699 : // must be sure that we don't try to do it.
700 1 : mShuttingDown = true;
701 :
702 1 : if (mConsumeBodyPump) {
703 0 : mConsumeBodyPump->Cancel(NS_BINDING_ABORTED);
704 0 : mConsumeBodyPump = nullptr;
705 : }
706 : }
707 :
708 : template <class Derived>
709 : NS_IMETHODIMP
710 0 : FetchBodyConsumer<Derived>::Observe(nsISupports* aSubject,
711 : const char* aTopic,
712 : const char16_t* aData)
713 : {
714 0 : AssertIsOnMainThread();
715 :
716 0 : MOZ_ASSERT((strcmp(aTopic, DOM_WINDOW_FROZEN_TOPIC) == 0) ||
717 : (strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC) == 0));
718 :
719 0 : nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mGlobal);
720 0 : if (SameCOMIdentity(aSubject, window)) {
721 0 : ContinueConsumeBody(NS_BINDING_ABORTED, 0, nullptr);
722 : }
723 :
724 0 : return NS_OK;
725 : }
726 :
727 : template <class Derived>
728 13 : NS_IMPL_ADDREF(FetchBodyConsumer<Derived>)
729 :
730 : template <class Derived>
731 14 : NS_IMPL_RELEASE(FetchBodyConsumer<Derived>)
732 :
733 : template <class Derived>
734 6 : NS_IMPL_QUERY_INTERFACE(FetchBodyConsumer<Derived>,
735 : nsIObserver,
736 : nsISupportsWeakReference)
737 :
738 : } // namespace dom
739 : } // namespace mozilla
|