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/. */
7 #include "InputStreamLengthHelper.h"
8 #include "mozilla/dom/WorkerCommon.h"
9 #include "nsIAsyncInputStream.h"
10 #include "nsIInputStream.h"
12 #include "nsServiceManagerUtils.h"
18 class AvailableEvent final
: public Runnable
{
20 AvailableEvent(nsIInputStream
* stream
,
21 const std::function
<void(int64_t aLength
)>& aCallback
)
22 : Runnable("mozilla::AvailableEvent"),
26 mCallbackTarget
= GetCurrentSerialEventTarget();
27 MOZ_ASSERT(NS_IsMainThread());
33 if (!NS_IsMainThread()) {
35 if (NS_WARN_IF(NS_FAILED(mStream
->Available(&size
)))) {
38 mSize
= (int64_t)size
;
43 nsCOMPtr
<nsIRunnable
> self(this); // overly cute
44 mCallbackTarget
->Dispatch(self
.forget(), NS_DISPATCH_NORMAL
);
45 mCallbackTarget
= nullptr;
50 std::function
<void(int64_t aLength
)> callback
;
51 callback
.swap(mCallback
);
57 nsCOMPtr
<nsIInputStream
> mStream
;
58 std::function
<void(int64_t aLength
)> mCallback
;
59 nsCOMPtr
<nsIEventTarget
> mCallbackTarget
;
67 bool InputStreamLengthHelper::GetSyncLength(nsIInputStream
* aStream
,
74 // Sync length access.
75 nsCOMPtr
<nsIInputStreamLength
> streamLength
= do_QueryInterface(aStream
);
78 nsresult rv
= streamLength
->Length(&length
);
81 if (NS_SUCCEEDED(rv
)) {
86 // Already closed stream or an error occurred.
87 if (rv
== NS_BASE_STREAM_CLOSED
||
88 NS_WARN_IF(rv
== NS_ERROR_NOT_AVAILABLE
) ||
89 NS_WARN_IF(rv
!= NS_BASE_STREAM_WOULD_BLOCK
)) {
94 nsCOMPtr
<nsIAsyncInputStreamLength
> asyncStreamLength
=
95 do_QueryInterface(aStream
);
96 if (asyncStreamLength
) {
97 // GetAsyncLength should be used.
101 // We cannot calculate the length of an async stream.
102 nsCOMPtr
<nsIAsyncInputStream
> asyncStream
= do_QueryInterface(aStream
);
107 // For main-thread only, we want to avoid calling ::Available() for blocking
109 if (NS_IsMainThread()) {
110 bool nonBlocking
= false;
111 if (NS_WARN_IF(NS_FAILED(aStream
->IsNonBlocking(&nonBlocking
)))) {
112 // Let's return -1. There is nothing else we can do here.
121 // Fallback using available().
122 uint64_t available
= 0;
123 nsresult rv
= aStream
->Available(&available
);
124 if (NS_WARN_IF(NS_FAILED(rv
))) {
125 // Let's return -1. There is nothing else we can do here.
129 *aLength
= (int64_t)available
;
134 void InputStreamLengthHelper::GetAsyncLength(
135 nsIInputStream
* aStream
,
136 const std::function
<void(int64_t aLength
)>& aCallback
) {
138 MOZ_ASSERT(aCallback
);
140 // We don't want to allow this class to be used on workers because we are not
141 // using the correct Runnable types.
142 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread() ||
143 !dom::IsCurrentThreadRunningWorker());
145 RefPtr
<InputStreamLengthHelper
> helper
=
146 new InputStreamLengthHelper(aStream
, aCallback
);
148 // Let's be sure that we don't call ::Available() on main-thread.
149 if (NS_IsMainThread()) {
150 nsCOMPtr
<nsIInputStreamLength
> streamLength
= do_QueryInterface(aStream
);
151 nsCOMPtr
<nsIAsyncInputStreamLength
> asyncStreamLength
=
152 do_QueryInterface(aStream
);
153 if (!streamLength
&& !asyncStreamLength
) {
154 // We cannot calculate the length of an async stream. We must fix the
155 // caller if this happens.
157 nsCOMPtr
<nsIAsyncInputStream
> asyncStream
= do_QueryInterface(aStream
);
158 MOZ_DIAGNOSTIC_ASSERT(!asyncStream
);
161 bool nonBlocking
= false;
162 if (NS_SUCCEEDED(aStream
->IsNonBlocking(&nonBlocking
)) && !nonBlocking
) {
163 nsCOMPtr
<nsIEventTarget
> target
=
164 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID
);
167 RefPtr
<AvailableEvent
> event
= new AvailableEvent(aStream
, aCallback
);
168 target
->Dispatch(event
.forget(), NS_DISPATCH_NORMAL
);
174 // Let's go async in order to have similar behaviors for sync and async
175 // nsIInputStreamLength implementations.
176 GetCurrentSerialEventTarget()->Dispatch(helper
, NS_DISPATCH_NORMAL
);
179 InputStreamLengthHelper::InputStreamLengthHelper(
180 nsIInputStream
* aStream
,
181 const std::function
<void(int64_t aLength
)>& aCallback
)
182 : Runnable("InputStreamLengthHelper"),
184 mCallback(aCallback
) {
186 MOZ_ASSERT(aCallback
);
189 InputStreamLengthHelper::~InputStreamLengthHelper() = default;
192 InputStreamLengthHelper::Run() {
193 // Sync length access.
194 nsCOMPtr
<nsIInputStreamLength
> streamLength
= do_QueryInterface(mStream
);
197 nsresult rv
= streamLength
->Length(&length
);
200 if (NS_SUCCEEDED(rv
)) {
201 ExecCallback(length
);
205 // Already closed stream or an error occurred.
206 if (rv
== NS_BASE_STREAM_CLOSED
||
207 NS_WARN_IF(rv
== NS_ERROR_NOT_AVAILABLE
) ||
208 NS_WARN_IF(rv
!= NS_BASE_STREAM_WOULD_BLOCK
)) {
214 // Async length access.
215 nsCOMPtr
<nsIAsyncInputStreamLength
> asyncStreamLength
=
216 do_QueryInterface(mStream
);
217 if (asyncStreamLength
) {
219 asyncStreamLength
->AsyncLengthWait(this, GetCurrentSerialEventTarget());
220 if (NS_WARN_IF(NS_FAILED(rv
))) {
227 // Fallback using available().
228 uint64_t available
= 0;
229 nsresult rv
= mStream
->Available(&available
);
230 if (NS_WARN_IF(NS_FAILED(rv
))) {
235 ExecCallback((int64_t)available
);
240 InputStreamLengthHelper::OnInputStreamLengthReady(
241 nsIAsyncInputStreamLength
* aStream
, int64_t aLength
) {
242 ExecCallback(aLength
);
246 void InputStreamLengthHelper::ExecCallback(int64_t aLength
) {
247 MOZ_ASSERT(mCallback
);
249 std::function
<void(int64_t aLength
)> callback
;
250 callback
.swap(mCallback
);
255 NS_IMPL_ISUPPORTS_INHERITED(InputStreamLengthHelper
, Runnable
,
256 nsIInputStreamLengthCallback
)
258 } // namespace mozilla