/src/mozilla-central/dom/fetch/FetchStream.cpp
Line | Count | Source (jump to first uncovered line) |
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 "FetchStream.h" |
8 | | #include "mozilla/dom/DOMException.h" |
9 | | #include "mozilla/dom/WorkerCommon.h" |
10 | | #include "mozilla/dom/WorkerPrivate.h" |
11 | | #include "nsProxyRelease.h" |
12 | | #include "nsStreamUtils.h" |
13 | | |
14 | 0 | #define FETCH_STREAM_FLAG 0 |
15 | | |
16 | | static NS_DEFINE_CID(kStreamTransportServiceCID, |
17 | | NS_STREAMTRANSPORTSERVICE_CID); |
18 | | |
19 | | namespace mozilla { |
20 | | namespace dom { |
21 | | |
22 | | class FetchStream::WorkerShutdown final : public WorkerControlRunnable |
23 | | { |
24 | | public: |
25 | | WorkerShutdown(WorkerPrivate* aWorkerPrivate, RefPtr<FetchStream> aStream) |
26 | | : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount) |
27 | | , mStream(aStream) |
28 | 0 | {} |
29 | | |
30 | | bool |
31 | | WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override |
32 | 0 | { |
33 | 0 | mStream->ReleaseObjects(); |
34 | 0 | return true; |
35 | 0 | } |
36 | | |
37 | | // This runnable starts from a JS Thread. We need to disable a couple of |
38 | | // assertions overring the following methods. |
39 | | |
40 | | bool |
41 | | PreDispatch(WorkerPrivate* aWorkerPrivate) override |
42 | 0 | { |
43 | 0 | return true; |
44 | 0 | } |
45 | | |
46 | | void |
47 | | PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override |
48 | 0 | {} |
49 | | |
50 | | private: |
51 | | RefPtr<FetchStream> mStream; |
52 | | }; |
53 | | |
54 | | NS_IMPL_ISUPPORTS(FetchStream, nsIInputStreamCallback, nsIObserver, |
55 | | nsISupportsWeakReference) |
56 | | |
57 | | /* static */ void |
58 | | FetchStream::Create(JSContext* aCx, FetchStreamHolder* aStreamHolder, |
59 | | nsIGlobalObject* aGlobal, nsIInputStream* aInputStream, |
60 | | JS::MutableHandle<JSObject*> aStream, ErrorResult& aRv) |
61 | 0 | { |
62 | 0 | MOZ_DIAGNOSTIC_ASSERT(aCx); |
63 | 0 | MOZ_DIAGNOSTIC_ASSERT(aStreamHolder); |
64 | 0 | MOZ_DIAGNOSTIC_ASSERT(aInputStream); |
65 | 0 |
|
66 | 0 | RefPtr<FetchStream> stream = |
67 | 0 | new FetchStream(aGlobal, aStreamHolder, aInputStream); |
68 | 0 |
|
69 | 0 | if (NS_IsMainThread()) { |
70 | 0 | nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); |
71 | 0 | if (NS_WARN_IF(!os)) { |
72 | 0 | aRv.Throw(NS_ERROR_FAILURE); |
73 | 0 | return; |
74 | 0 | } |
75 | 0 | |
76 | 0 | aRv = os->AddObserver(stream, DOM_WINDOW_DESTROYED_TOPIC, true); |
77 | 0 | if (NS_WARN_IF(aRv.Failed())) { |
78 | 0 | return; |
79 | 0 | } |
80 | 0 | |
81 | 0 | } else { |
82 | 0 | WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx); |
83 | 0 | MOZ_ASSERT(workerPrivate); |
84 | 0 |
|
85 | 0 | RefPtr<WeakWorkerRef> workerRef = |
86 | 0 | WeakWorkerRef::Create(workerPrivate, [stream]() { |
87 | 0 | stream->Close(); |
88 | 0 | }); |
89 | 0 |
|
90 | 0 | if (NS_WARN_IF(!workerRef)) { |
91 | 0 | aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); |
92 | 0 | return; |
93 | 0 | } |
94 | 0 | |
95 | 0 | // Note, this will create a ref-cycle between the holder and the stream. |
96 | 0 | // The cycle is broken when the stream is closed or the worker begins |
97 | 0 | // shutting down. |
98 | 0 | stream->mWorkerRef = workerRef.forget(); |
99 | 0 | } |
100 | 0 |
|
101 | 0 | if (!JS::HasReadableStreamCallbacks(aCx)) { |
102 | 0 | JS::SetReadableStreamCallbacks(aCx, |
103 | 0 | &FetchStream::RequestDataCallback, |
104 | 0 | &FetchStream::WriteIntoReadRequestCallback, |
105 | 0 | &FetchStream::CancelCallback, |
106 | 0 | &FetchStream::ClosedCallback, |
107 | 0 | &FetchStream::ErroredCallback, |
108 | 0 | &FetchStream::FinalizeCallback); |
109 | 0 | } |
110 | 0 |
|
111 | 0 | JS::Rooted<JSObject*> body(aCx, |
112 | 0 | JS::NewReadableExternalSourceStreamObject(aCx, stream, FETCH_STREAM_FLAG)); |
113 | 0 | if (!body) { |
114 | 0 | aRv.StealExceptionFromJSContext(aCx); |
115 | 0 | return; |
116 | 0 | } |
117 | 0 | |
118 | 0 | // This will be released in FetchStream::FinalizeCallback(). We are |
119 | 0 | // guaranteed the jsapi will call FinalizeCallback when ReadableStream |
120 | 0 | // js object is finalized. |
121 | 0 | NS_ADDREF(stream.get()); |
122 | 0 |
|
123 | 0 | aStream.set(body); |
124 | 0 | } |
125 | | |
126 | | /* static */ void |
127 | | FetchStream::RequestDataCallback(JSContext* aCx, |
128 | | JS::HandleObject aStream, |
129 | | void* aUnderlyingSource, |
130 | | uint8_t aFlags, |
131 | | size_t aDesiredSize) |
132 | 0 | { |
133 | 0 | MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource); |
134 | 0 | MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG); |
135 | 0 | MOZ_DIAGNOSTIC_ASSERT(JS::ReadableStreamIsDisturbed(aStream)); |
136 | 0 |
|
137 | 0 | RefPtr<FetchStream> stream = static_cast<FetchStream*>(aUnderlyingSource); |
138 | 0 | stream->AssertIsOnOwningThread(); |
139 | 0 |
|
140 | 0 | MutexAutoLock lock(stream->mMutex); |
141 | 0 |
|
142 | 0 | MOZ_DIAGNOSTIC_ASSERT(stream->mState == eInitializing || |
143 | 0 | stream->mState == eWaiting || |
144 | 0 | stream->mState == eChecking || |
145 | 0 | stream->mState == eReading); |
146 | 0 |
|
147 | 0 | if (stream->mState == eReading) { |
148 | 0 | // We are already reading data. |
149 | 0 | return; |
150 | 0 | } |
151 | 0 | |
152 | 0 | if (stream->mState == eChecking) { |
153 | 0 | // If we are looking for more data, there is nothing else we should do: |
154 | 0 | // let's move this checking operation in a reading. |
155 | 0 | MOZ_ASSERT(stream->mInputStream); |
156 | 0 | stream->mState = eReading; |
157 | 0 | return; |
158 | 0 | } |
159 | 0 |
|
160 | 0 | if (stream->mState == eInitializing) { |
161 | 0 | // The stream has been used for the first time. |
162 | 0 | stream->mStreamHolder->MarkAsRead(); |
163 | 0 | } |
164 | 0 |
|
165 | 0 | stream->mState = eReading; |
166 | 0 |
|
167 | 0 | if (!stream->mInputStream) { |
168 | 0 | // This is the first use of the stream. Let's convert the |
169 | 0 | // mOriginalInputStream into an nsIAsyncInputStream. |
170 | 0 | MOZ_ASSERT(stream->mOriginalInputStream); |
171 | 0 |
|
172 | 0 | nsCOMPtr<nsIAsyncInputStream> asyncStream; |
173 | 0 | nsresult rv = |
174 | 0 | NS_MakeAsyncNonBlockingInputStream(stream->mOriginalInputStream.forget(), |
175 | 0 | getter_AddRefs(asyncStream)); |
176 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
177 | 0 | stream->ErrorPropagation(aCx, lock, aStream, rv); |
178 | 0 | return; |
179 | 0 | } |
180 | 0 | |
181 | 0 | stream->mInputStream = asyncStream; |
182 | 0 | stream->mOriginalInputStream = nullptr; |
183 | 0 | } |
184 | 0 |
|
185 | 0 | MOZ_DIAGNOSTIC_ASSERT(stream->mInputStream); |
186 | 0 | MOZ_DIAGNOSTIC_ASSERT(!stream->mOriginalInputStream); |
187 | 0 |
|
188 | 0 | nsresult rv = |
189 | 0 | stream->mInputStream->AsyncWait(stream, 0, 0, |
190 | 0 | stream->mOwningEventTarget); |
191 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
192 | 0 | stream->ErrorPropagation(aCx, lock, aStream, rv); |
193 | 0 | return; |
194 | 0 | } |
195 | 0 |
|
196 | 0 | // All good. |
197 | 0 | } |
198 | | |
199 | | /* static */ void |
200 | | FetchStream::WriteIntoReadRequestCallback(JSContext* aCx, |
201 | | JS::HandleObject aStream, |
202 | | void* aUnderlyingSource, |
203 | | uint8_t aFlags, void* aBuffer, |
204 | | size_t aLength, size_t* aByteWritten) |
205 | 0 | { |
206 | 0 | MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource); |
207 | 0 | MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG); |
208 | 0 | MOZ_DIAGNOSTIC_ASSERT(aBuffer); |
209 | 0 | MOZ_DIAGNOSTIC_ASSERT(aByteWritten); |
210 | 0 |
|
211 | 0 | RefPtr<FetchStream> stream = static_cast<FetchStream*>(aUnderlyingSource); |
212 | 0 | stream->AssertIsOnOwningThread(); |
213 | 0 |
|
214 | 0 | MutexAutoLock lock(stream->mMutex); |
215 | 0 |
|
216 | 0 | MOZ_DIAGNOSTIC_ASSERT(stream->mInputStream); |
217 | 0 | MOZ_DIAGNOSTIC_ASSERT(stream->mState == eWriting); |
218 | 0 | stream->mState = eChecking; |
219 | 0 |
|
220 | 0 | uint32_t written; |
221 | 0 | nsresult rv = |
222 | 0 | stream->mInputStream->Read(static_cast<char*>(aBuffer), aLength, &written); |
223 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
224 | 0 | stream->ErrorPropagation(aCx, lock, aStream, rv); |
225 | 0 | return; |
226 | 0 | } |
227 | 0 | |
228 | 0 | *aByteWritten = written; |
229 | 0 |
|
230 | 0 | if (written == 0) { |
231 | 0 | stream->CloseAndReleaseObjects(aCx, lock, aStream); |
232 | 0 | return; |
233 | 0 | } |
234 | 0 | |
235 | 0 | rv = stream->mInputStream->AsyncWait(stream, 0, 0, |
236 | 0 | stream->mOwningEventTarget); |
237 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
238 | 0 | stream->ErrorPropagation(aCx, lock, aStream, rv); |
239 | 0 | return; |
240 | 0 | } |
241 | 0 |
|
242 | 0 | // All good. |
243 | 0 | } |
244 | | |
245 | | /* static */ JS::Value |
246 | | FetchStream::CancelCallback(JSContext* aCx, JS::HandleObject aStream, |
247 | | void* aUnderlyingSource, uint8_t aFlags, |
248 | | JS::HandleValue aReason) |
249 | 0 | { |
250 | 0 | MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource); |
251 | 0 | MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG); |
252 | 0 |
|
253 | 0 | // This is safe because we created an extra reference in FetchStream::Create() |
254 | 0 | // that won't be released until FetchStream::FinalizeCallback() is called. |
255 | 0 | // We are guaranteed that won't happen until the js ReadableStream object |
256 | 0 | // is finalized. |
257 | 0 | FetchStream* stream = static_cast<FetchStream*>(aUnderlyingSource); |
258 | 0 | stream->AssertIsOnOwningThread(); |
259 | 0 |
|
260 | 0 | if (stream->mState == eInitializing) { |
261 | 0 | // The stream has been used for the first time. |
262 | 0 | stream->mStreamHolder->MarkAsRead(); |
263 | 0 | } |
264 | 0 |
|
265 | 0 | if (stream->mInputStream) { |
266 | 0 | stream->mInputStream->CloseWithStatus(NS_BASE_STREAM_CLOSED); |
267 | 0 | } |
268 | 0 |
|
269 | 0 | // It could be that we don't have mInputStream yet, but we still have the |
270 | 0 | // original stream. We need to close that too. |
271 | 0 | if (stream->mOriginalInputStream) { |
272 | 0 | MOZ_ASSERT(!stream->mInputStream); |
273 | 0 | stream->mOriginalInputStream->Close(); |
274 | 0 | } |
275 | 0 |
|
276 | 0 | stream->ReleaseObjects(); |
277 | 0 | return JS::UndefinedValue(); |
278 | 0 | } |
279 | | |
280 | | /* static */ void |
281 | | FetchStream::ClosedCallback(JSContext* aCx, JS::HandleObject aStream, |
282 | | void* aUnderlyingSource, uint8_t aFlags) |
283 | 0 | { |
284 | 0 | MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource); |
285 | 0 | MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG); |
286 | 0 | } |
287 | | |
288 | | /* static */ void |
289 | | FetchStream::ErroredCallback(JSContext* aCx, JS::HandleObject aStream, |
290 | | void* aUnderlyingSource, uint8_t aFlags, |
291 | | JS::HandleValue aReason) |
292 | 0 | { |
293 | 0 | MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource); |
294 | 0 | MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG); |
295 | 0 |
|
296 | 0 | // This is safe because we created an extra reference in FetchStream::Create() |
297 | 0 | // that won't be released until FetchStream::FinalizeCallback() is called. |
298 | 0 | // We are guaranteed that won't happen until the js ReadableStream object |
299 | 0 | // is finalized. |
300 | 0 | FetchStream* stream = static_cast<FetchStream*>(aUnderlyingSource); |
301 | 0 | stream->AssertIsOnOwningThread(); |
302 | 0 |
|
303 | 0 | if (stream->mState == eInitializing) { |
304 | 0 | // The stream has been used for the first time. |
305 | 0 | stream->mStreamHolder->MarkAsRead(); |
306 | 0 | } |
307 | 0 |
|
308 | 0 | if (stream->mInputStream) { |
309 | 0 | stream->mInputStream->CloseWithStatus(NS_BASE_STREAM_CLOSED); |
310 | 0 | } |
311 | 0 |
|
312 | 0 | stream->ReleaseObjects(); |
313 | 0 | } |
314 | | |
315 | | void |
316 | | FetchStream::FinalizeCallback(void* aUnderlyingSource, uint8_t aFlags) |
317 | 0 | { |
318 | 0 | MOZ_DIAGNOSTIC_ASSERT(aUnderlyingSource); |
319 | 0 | MOZ_DIAGNOSTIC_ASSERT(aFlags == FETCH_STREAM_FLAG); |
320 | 0 |
|
321 | 0 | // This can be called in any thread. |
322 | 0 |
|
323 | 0 | // This takes ownership of the ref created in FetchStream::Create(). |
324 | 0 | RefPtr<FetchStream> stream = |
325 | 0 | dont_AddRef(static_cast<FetchStream*>(aUnderlyingSource)); |
326 | 0 |
|
327 | 0 | stream->ReleaseObjects(); |
328 | 0 | } |
329 | | |
330 | | FetchStream::FetchStream(nsIGlobalObject* aGlobal, |
331 | | FetchStreamHolder* aStreamHolder, |
332 | | nsIInputStream* aInputStream) |
333 | | : mMutex("FetchStream::mMutex") |
334 | | , mState(eInitializing) |
335 | | , mGlobal(aGlobal) |
336 | | , mStreamHolder(aStreamHolder) |
337 | | , mOwningEventTarget(aGlobal->EventTargetFor(TaskCategory::Other)) |
338 | | , mOriginalInputStream(aInputStream) |
339 | 0 | { |
340 | 0 | MOZ_DIAGNOSTIC_ASSERT(aInputStream); |
341 | 0 | MOZ_DIAGNOSTIC_ASSERT(aStreamHolder); |
342 | 0 | } |
343 | | |
344 | | FetchStream::~FetchStream() |
345 | 0 | { |
346 | 0 | } |
347 | | |
348 | | void |
349 | | FetchStream::ErrorPropagation(JSContext* aCx, |
350 | | const MutexAutoLock& aProofOfLock, |
351 | | JS::HandleObject aStream, |
352 | | nsresult aError) |
353 | 0 | { |
354 | 0 | AssertIsOnOwningThread(); |
355 | 0 |
|
356 | 0 | // Nothing to do. |
357 | 0 | if (mState == eClosed) { |
358 | 0 | return; |
359 | 0 | } |
360 | 0 | |
361 | 0 | // Let's close the stream. |
362 | 0 | if (aError == NS_BASE_STREAM_CLOSED) { |
363 | 0 | CloseAndReleaseObjects(aCx, aProofOfLock, aStream); |
364 | 0 | return; |
365 | 0 | } |
366 | 0 | |
367 | 0 | // Let's use a generic error. |
368 | 0 | RefPtr<DOMException> error = DOMException::Create(NS_ERROR_DOM_TYPE_ERR); |
369 | 0 |
|
370 | 0 | JS::Rooted<JS::Value> errorValue(aCx); |
371 | 0 | if (ToJSValue(aCx, error, &errorValue)) { |
372 | 0 | MutexAutoUnlock unlock(mMutex); |
373 | 0 | JS::ReadableStreamError(aCx, aStream, errorValue); |
374 | 0 | } |
375 | 0 |
|
376 | 0 | ReleaseObjects(aProofOfLock); |
377 | 0 | } |
378 | | |
379 | | NS_IMETHODIMP |
380 | | FetchStream::OnInputStreamReady(nsIAsyncInputStream* aStream) |
381 | 0 | { |
382 | 0 | AssertIsOnOwningThread(); |
383 | 0 | MOZ_DIAGNOSTIC_ASSERT(aStream); |
384 | 0 |
|
385 | 0 | MutexAutoLock lock(mMutex); |
386 | 0 |
|
387 | 0 | // Already closed. We have nothing else to do here. |
388 | 0 | if (mState == eClosed) { |
389 | 0 | return NS_OK; |
390 | 0 | } |
391 | 0 | |
392 | 0 | MOZ_DIAGNOSTIC_ASSERT(mInputStream); |
393 | 0 | MOZ_DIAGNOSTIC_ASSERT(mState == eReading || mState == eChecking); |
394 | 0 |
|
395 | 0 | AutoJSAPI jsapi; |
396 | 0 | if (NS_WARN_IF(!jsapi.Init(mGlobal))) { |
397 | 0 | // Without JSContext we are not able to close the stream or to propagate the |
398 | 0 | // error. |
399 | 0 | return NS_ERROR_FAILURE; |
400 | 0 | } |
401 | 0 | |
402 | 0 | JSContext* cx = jsapi.cx(); |
403 | 0 | JS::Rooted<JSObject*> stream(cx, mStreamHolder->ReadableStreamBody()); |
404 | 0 |
|
405 | 0 | uint64_t size = 0; |
406 | 0 | nsresult rv = mInputStream->Available(&size); |
407 | 0 | if (NS_SUCCEEDED(rv) && size == 0) { |
408 | 0 | // In theory this should not happen. If size is 0, the stream should be |
409 | 0 | // considered closed. |
410 | 0 | rv = NS_BASE_STREAM_CLOSED; |
411 | 0 | } |
412 | 0 |
|
413 | 0 | // No warning for stream closed. |
414 | 0 | if (rv == NS_BASE_STREAM_CLOSED || NS_WARN_IF(NS_FAILED(rv))) { |
415 | 0 | ErrorPropagation(cx, lock, stream, rv); |
416 | 0 | return NS_OK; |
417 | 0 | } |
418 | 0 | |
419 | 0 | // This extra checking is completed. Let's wait for the next read request. |
420 | 0 | if (mState == eChecking) { |
421 | 0 | mState = eWaiting; |
422 | 0 | return NS_OK; |
423 | 0 | } |
424 | 0 | |
425 | 0 | mState = eWriting; |
426 | 0 |
|
427 | 0 | { |
428 | 0 | MutexAutoUnlock unlock(mMutex); |
429 | 0 | JS::ReadableStreamUpdateDataAvailableFromSource(cx, stream, size); |
430 | 0 | } |
431 | 0 |
|
432 | 0 | // The WriteInto callback changes mState to eChecking. |
433 | 0 | MOZ_DIAGNOSTIC_ASSERT(mState == eChecking); |
434 | 0 |
|
435 | 0 | return NS_OK; |
436 | 0 | } |
437 | | |
438 | | /* static */ nsresult |
439 | | FetchStream::RetrieveInputStream(void* aUnderlyingReadableStreamSource, |
440 | | nsIInputStream** aInputStream) |
441 | 0 | { |
442 | 0 | MOZ_ASSERT(aUnderlyingReadableStreamSource); |
443 | 0 | MOZ_ASSERT(aInputStream); |
444 | 0 |
|
445 | 0 | RefPtr<FetchStream> stream = |
446 | 0 | static_cast<FetchStream*>(aUnderlyingReadableStreamSource); |
447 | 0 | stream->AssertIsOnOwningThread(); |
448 | 0 |
|
449 | 0 | // if mOriginalInputStream is null, the reading already started. We don't want |
450 | 0 | // to expose the internal inputStream. |
451 | 0 | if (NS_WARN_IF(!stream->mOriginalInputStream)) { |
452 | 0 | return NS_ERROR_DOM_INVALID_STATE_ERR; |
453 | 0 | } |
454 | 0 | |
455 | 0 | nsCOMPtr<nsIInputStream> inputStream = stream->mOriginalInputStream; |
456 | 0 | inputStream.forget(aInputStream); |
457 | 0 | return NS_OK; |
458 | 0 | } |
459 | | |
460 | | void |
461 | | FetchStream::Close() |
462 | 0 | { |
463 | 0 | AssertIsOnOwningThread(); |
464 | 0 |
|
465 | 0 | MutexAutoLock lock(mMutex); |
466 | 0 |
|
467 | 0 | if (mState == eClosed) { |
468 | 0 | return; |
469 | 0 | } |
470 | 0 | |
471 | 0 | AutoJSAPI jsapi; |
472 | 0 | if (NS_WARN_IF(!jsapi.Init(mGlobal))) { |
473 | 0 | ReleaseObjects(lock); |
474 | 0 | return; |
475 | 0 | } |
476 | 0 | |
477 | 0 | JSContext* cx = jsapi.cx(); |
478 | 0 | JS::Rooted<JSObject*> stream(cx, mStreamHolder->ReadableStreamBody()); |
479 | 0 | CloseAndReleaseObjects(cx, lock, stream); |
480 | 0 | } |
481 | | |
482 | | void |
483 | | FetchStream::CloseAndReleaseObjects(JSContext* aCx, |
484 | | const MutexAutoLock& aProofOfLock, |
485 | | JS::HandleObject aStream) |
486 | 0 | { |
487 | 0 | AssertIsOnOwningThread(); |
488 | 0 | MOZ_DIAGNOSTIC_ASSERT(mState != eClosed); |
489 | 0 |
|
490 | 0 | ReleaseObjects(aProofOfLock); |
491 | 0 |
|
492 | 0 | MutexAutoUnlock unlock(mMutex); |
493 | 0 | if (JS::ReadableStreamIsReadable(aStream)) { |
494 | 0 | JS::ReadableStreamClose(aCx, aStream); |
495 | 0 | } |
496 | 0 | } |
497 | | |
498 | | void |
499 | | FetchStream::ReleaseObjects() |
500 | 0 | { |
501 | 0 | MutexAutoLock lock(mMutex); |
502 | 0 | ReleaseObjects(lock); |
503 | 0 | } |
504 | | |
505 | | void |
506 | | FetchStream::ReleaseObjects(const MutexAutoLock& aProofOfLock) |
507 | 0 | { |
508 | 0 | // This method can be called on 2 possible threads: the owning one and a JS |
509 | 0 | // thread used to release resources. If we are on the JS thread, we need to |
510 | 0 | // dispatch a runnable to go back to the owning thread in order to release |
511 | 0 | // resources correctly. |
512 | 0 |
|
513 | 0 | if (mState == eClosed) { |
514 | 0 | // Already gone. Nothing to do. |
515 | 0 | return; |
516 | 0 | } |
517 | 0 | |
518 | 0 | mState = eClosed; |
519 | 0 |
|
520 | 0 | if (!NS_IsMainThread() && !IsCurrentThreadRunningWorker()) { |
521 | 0 | // Let's dispatch a WorkerControlRunnable if the owning thread is a worker. |
522 | 0 | if (mWorkerRef) { |
523 | 0 | RefPtr<WorkerShutdown> r = |
524 | 0 | new WorkerShutdown(mWorkerRef->GetUnsafePrivate(), this); |
525 | 0 | Unused << NS_WARN_IF(!r->Dispatch()); |
526 | 0 | return; |
527 | 0 | } |
528 | 0 |
|
529 | 0 | // A normal runnable of the owning thread is the main-thread. |
530 | 0 | RefPtr<FetchStream> self = this; |
531 | 0 | RefPtr<Runnable> r = |
532 | 0 | NS_NewRunnableFunction("FetchStream::ReleaseObjects", |
533 | 0 | [self] () { self->ReleaseObjects(); }); |
534 | 0 | mOwningEventTarget->Dispatch(r.forget()); |
535 | 0 | return; |
536 | 0 | } |
537 | 0 |
|
538 | 0 | AssertIsOnOwningThread(); |
539 | 0 |
|
540 | 0 | if (NS_IsMainThread()) { |
541 | 0 | nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); |
542 | 0 | if (obs) { |
543 | 0 | obs->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC); |
544 | 0 | } |
545 | 0 | } |
546 | 0 |
|
547 | 0 | mWorkerRef = nullptr; |
548 | 0 | mGlobal = nullptr; |
549 | 0 |
|
550 | 0 | mStreamHolder->NullifyStream(); |
551 | 0 | mStreamHolder = nullptr; |
552 | 0 | } |
553 | | |
554 | | #ifdef DEBUG |
555 | | void |
556 | | FetchStream::AssertIsOnOwningThread() |
557 | | { |
558 | | NS_ASSERT_OWNINGTHREAD(FetchStream); |
559 | | } |
560 | | #endif |
561 | | |
562 | | // nsIObserver |
563 | | // ----------- |
564 | | |
565 | | NS_IMETHODIMP |
566 | | FetchStream::Observe(nsISupports* aSubject, const char* aTopic, |
567 | | const char16_t* aData) |
568 | 0 | { |
569 | 0 | AssertIsOnMainThread(); |
570 | 0 | AssertIsOnOwningThread(); |
571 | 0 |
|
572 | 0 | MOZ_ASSERT(strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC) == 0); |
573 | 0 |
|
574 | 0 | nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mGlobal); |
575 | 0 | if (SameCOMIdentity(aSubject, window)) { |
576 | 0 | Close(); |
577 | 0 | } |
578 | 0 |
|
579 | 0 | return NS_OK; |
580 | 0 | } |
581 | | |
582 | | } // dom namespace |
583 | | } // mozilla namespace |