Bug 1932613 - temporarily disable browser_ml_end_to_end.js for permanent failures...
[gecko.git] / xpcom / io / InputStreamLengthHelper.cpp
blob9a767f9229de1342ce3f7d5e6e89439daf53aabd
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"
11 #include "nsNetCID.h"
12 #include "nsServiceManagerUtils.h"
14 namespace mozilla {
16 namespace {
18 class AvailableEvent final : public Runnable {
19 public:
20 AvailableEvent(nsIInputStream* stream,
21 const std::function<void(int64_t aLength)>& aCallback)
22 : Runnable("mozilla::AvailableEvent"),
23 mStream(stream),
24 mCallback(aCallback),
25 mSize(-1) {
26 mCallbackTarget = GetCurrentSerialEventTarget();
27 MOZ_ASSERT(NS_IsMainThread());
30 NS_IMETHOD
31 Run() override {
32 // ping
33 if (!NS_IsMainThread()) {
34 uint64_t size = 0;
35 if (NS_WARN_IF(NS_FAILED(mStream->Available(&size)))) {
36 mSize = -1;
37 } else {
38 mSize = (int64_t)size;
41 mStream = nullptr;
43 nsCOMPtr<nsIRunnable> self(this); // overly cute
44 mCallbackTarget->Dispatch(self.forget(), NS_DISPATCH_NORMAL);
45 mCallbackTarget = nullptr;
46 return NS_OK;
49 // pong
50 std::function<void(int64_t aLength)> callback;
51 callback.swap(mCallback);
52 callback(mSize);
53 return NS_OK;
56 private:
57 nsCOMPtr<nsIInputStream> mStream;
58 std::function<void(int64_t aLength)> mCallback;
59 nsCOMPtr<nsIEventTarget> mCallbackTarget;
61 int64_t mSize;
64 } // namespace
66 /* static */
67 bool InputStreamLengthHelper::GetSyncLength(nsIInputStream* aStream,
68 int64_t* aLength) {
69 MOZ_ASSERT(aStream);
70 MOZ_ASSERT(aLength);
72 *aLength = -1;
74 // Sync length access.
75 nsCOMPtr<nsIInputStreamLength> streamLength = do_QueryInterface(aStream);
76 if (streamLength) {
77 int64_t length = -1;
78 nsresult rv = streamLength->Length(&length);
80 // All good!
81 if (NS_SUCCEEDED(rv)) {
82 *aLength = length;
83 return true;
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)) {
90 return true;
94 nsCOMPtr<nsIAsyncInputStreamLength> asyncStreamLength =
95 do_QueryInterface(aStream);
96 if (asyncStreamLength) {
97 // GetAsyncLength should be used.
98 return false;
101 // We cannot calculate the length of an async stream.
102 nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(aStream);
103 if (asyncStream) {
104 return false;
107 // For main-thread only, we want to avoid calling ::Available() for blocking
108 // streams.
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.
113 return true;
116 if (!nonBlocking) {
117 return false;
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.
126 return true;
129 *aLength = (int64_t)available;
130 return true;
133 /* static */
134 void InputStreamLengthHelper::GetAsyncLength(
135 nsIInputStream* aStream,
136 const std::function<void(int64_t aLength)>& aCallback) {
137 MOZ_ASSERT(aStream);
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.
156 #ifdef DEBUG
157 nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(aStream);
158 MOZ_DIAGNOSTIC_ASSERT(!asyncStream);
159 #endif
161 bool nonBlocking = false;
162 if (NS_SUCCEEDED(aStream->IsNonBlocking(&nonBlocking)) && !nonBlocking) {
163 nsCOMPtr<nsIEventTarget> target =
164 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
165 MOZ_ASSERT(target);
167 RefPtr<AvailableEvent> event = new AvailableEvent(aStream, aCallback);
168 target->Dispatch(event.forget(), NS_DISPATCH_NORMAL);
169 return;
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"),
183 mStream(aStream),
184 mCallback(aCallback) {
185 MOZ_ASSERT(aStream);
186 MOZ_ASSERT(aCallback);
189 InputStreamLengthHelper::~InputStreamLengthHelper() = default;
191 NS_IMETHODIMP
192 InputStreamLengthHelper::Run() {
193 // Sync length access.
194 nsCOMPtr<nsIInputStreamLength> streamLength = do_QueryInterface(mStream);
195 if (streamLength) {
196 int64_t length = -1;
197 nsresult rv = streamLength->Length(&length);
199 // All good!
200 if (NS_SUCCEEDED(rv)) {
201 ExecCallback(length);
202 return NS_OK;
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)) {
209 ExecCallback(-1);
210 return NS_OK;
214 // Async length access.
215 nsCOMPtr<nsIAsyncInputStreamLength> asyncStreamLength =
216 do_QueryInterface(mStream);
217 if (asyncStreamLength) {
218 nsresult rv =
219 asyncStreamLength->AsyncLengthWait(this, GetCurrentSerialEventTarget());
220 if (NS_WARN_IF(NS_FAILED(rv))) {
221 ExecCallback(-1);
224 return NS_OK;
227 // Fallback using available().
228 uint64_t available = 0;
229 nsresult rv = mStream->Available(&available);
230 if (NS_WARN_IF(NS_FAILED(rv))) {
231 ExecCallback(-1);
232 return NS_OK;
235 ExecCallback((int64_t)available);
236 return NS_OK;
239 NS_IMETHODIMP
240 InputStreamLengthHelper::OnInputStreamLengthReady(
241 nsIAsyncInputStreamLength* aStream, int64_t aLength) {
242 ExecCallback(aLength);
243 return NS_OK;
246 void InputStreamLengthHelper::ExecCallback(int64_t aLength) {
247 MOZ_ASSERT(mCallback);
249 std::function<void(int64_t aLength)> callback;
250 callback.swap(mCallback);
252 callback(aLength);
255 NS_IMPL_ISUPPORTS_INHERITED(InputStreamLengthHelper, Runnable,
256 nsIInputStreamLengthCallback)
258 } // namespace mozilla