Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/xpcom/io/InputStreamLengthHelper.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 "InputStreamLengthHelper.h"
8
#include "mozilla/dom/WorkerCommon.h"
9
#include "nsIAsyncInputStream.h"
10
#include "nsIInputStream.h"
11
#include "nsIStreamTransportService.h"
12
13
static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
14
15
namespace mozilla {
16
17
namespace {
18
19
class AvailableEvent final : public Runnable
20
{
21
public:
22
  AvailableEvent(nsIInputStream* stream,
23
                 const std::function<void(int64_t aLength)>& aCallback)
24
    : Runnable("mozilla::AvailableEvent")
25
    , mStream(stream)
26
    , mCallback(aCallback)
27
    , mSize(-1)
28
0
  {
29
0
    mCallbackTarget = GetCurrentThreadSerialEventTarget();
30
0
    MOZ_ASSERT(NS_IsMainThread());
31
0
  }
32
33
  NS_IMETHOD
34
  Run() override
35
0
  {
36
0
    // ping
37
0
    if (!NS_IsMainThread()) {
38
0
      uint64_t size = 0;
39
0
      if (NS_WARN_IF(NS_FAILED(mStream->Available(&size)))) {
40
0
        mSize = -1;
41
0
      } else {
42
0
        mSize = (int64_t)size;
43
0
      }
44
0
45
0
      mStream = nullptr;
46
0
47
0
      nsCOMPtr<nsIRunnable> self(this); // overly cute
48
0
      mCallbackTarget->Dispatch(self.forget(), NS_DISPATCH_NORMAL);
49
0
      mCallbackTarget = nullptr;
50
0
      return NS_OK;
51
0
    }
52
0
53
0
    // pong
54
0
    std::function<void(int64_t aLength)> callback;
55
0
    callback.swap(mCallback);
56
0
    callback(mSize);
57
0
    return NS_OK;
58
0
  }
59
60
private:
61
  nsCOMPtr<nsIInputStream> mStream;
62
  std::function<void(int64_t aLength)> mCallback;
63
  nsCOMPtr<nsIEventTarget> mCallbackTarget;
64
65
  int64_t mSize;
66
};
67
68
} // anonymous
69
70
/* static */ bool
71
InputStreamLengthHelper::GetSyncLength(nsIInputStream* aStream,
72
                                       int64_t* aLength)
73
0
{
74
0
  MOZ_ASSERT(aStream);
75
0
  MOZ_ASSERT(aLength);
76
0
77
0
  *aLength = -1;
78
0
79
0
  // Sync length access.
80
0
  nsCOMPtr<nsIInputStreamLength> streamLength = do_QueryInterface(aStream);
81
0
  if (streamLength) {
82
0
    int64_t length = -1;
83
0
    nsresult rv = streamLength->Length(&length);
84
0
85
0
    // All good!
86
0
    if (NS_SUCCEEDED(rv)) {
87
0
      *aLength = length;
88
0
      return true;
89
0
    }
90
0
91
0
    // Already closed stream or an error occurred.
92
0
    if (rv == NS_BASE_STREAM_CLOSED ||
93
0
        NS_WARN_IF(rv == NS_ERROR_NOT_AVAILABLE) ||
94
0
        NS_WARN_IF(rv != NS_BASE_STREAM_WOULD_BLOCK)) {
95
0
      return true;
96
0
    }
97
0
  }
98
0
99
0
  nsCOMPtr<nsIAsyncInputStreamLength> asyncStreamLength =
100
0
    do_QueryInterface(aStream);
101
0
  if (asyncStreamLength) {
102
0
    // GetAsyncLength should be used.
103
0
    return false;
104
0
  }
105
0
106
0
  // We cannot calculate the length of an async stream.
107
0
  nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(aStream);
108
0
  if (asyncStream) {
109
0
    return false;
110
0
  }
111
0
112
0
  // For main-thread only, we want to avoid calling ::Available() for blocking
113
0
  // streams.
114
0
  if (NS_IsMainThread()) {
115
0
    bool nonBlocking = false;
116
0
    if (NS_WARN_IF(NS_FAILED(aStream->IsNonBlocking(&nonBlocking)))) {
117
0
      // Let's return -1. There is nothing else we can do here.
118
0
      return true;
119
0
    }
120
0
121
0
    if (!nonBlocking) {
122
0
      return false;
123
0
    }
124
0
  }
125
0
126
0
  // Fallback using available().
127
0
  uint64_t available = 0;
128
0
  nsresult rv = aStream->Available(&available);
129
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
130
0
    // Let's return -1. There is nothing else we can do here.
131
0
    return true;
132
0
  }
133
0
134
0
  *aLength = (int64_t)available;
135
0
  return true;
136
0
}
137
138
/* static */ void
139
InputStreamLengthHelper::GetAsyncLength(nsIInputStream* aStream,
140
                                        const std::function<void(int64_t aLength)>& aCallback)
141
0
{
142
0
  MOZ_ASSERT(aStream);
143
0
  MOZ_ASSERT(aCallback);
144
0
145
0
  // We don't want to allow this class to be used on workers because we are not
146
0
  // using the correct Runnable types.
147
0
  MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread() || !dom::IsCurrentThreadRunningWorker());
148
0
149
0
  RefPtr<InputStreamLengthHelper> helper =
150
0
    new InputStreamLengthHelper(aStream, aCallback);
151
0
152
0
  // Let's be sure that we don't call ::Available() on main-thread.
153
0
  if (NS_IsMainThread()) {
154
0
    nsCOMPtr<nsIInputStreamLength> streamLength = do_QueryInterface(aStream);
155
0
    nsCOMPtr<nsIAsyncInputStreamLength> asyncStreamLength =
156
0
      do_QueryInterface(aStream);
157
0
    if (!streamLength && !asyncStreamLength) {
158
0
      // We cannot calculate the length of an async stream. We must fix the
159
0
      // caller if this happens.
160
#ifdef DEBUG
161
      nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(aStream);
162
      MOZ_DIAGNOSTIC_ASSERT(!asyncStream);
163
#endif
164
165
0
      bool nonBlocking = false;
166
0
      if (NS_SUCCEEDED(aStream->IsNonBlocking(&nonBlocking)) && !nonBlocking) {
167
0
        nsCOMPtr<nsIEventTarget> target =
168
0
          do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
169
0
        MOZ_ASSERT(target);
170
0
171
0
        RefPtr<AvailableEvent> event = new AvailableEvent(aStream, aCallback);
172
0
        target->Dispatch(event.forget(), NS_DISPATCH_NORMAL);
173
0
        return;
174
0
      }
175
0
    }
176
0
  }
177
0
178
0
  // Let's go async in order to have similar behaviors for sync and async
179
0
  // nsIInputStreamLength implementations.
180
0
  GetCurrentThreadSerialEventTarget()->Dispatch(helper, NS_DISPATCH_NORMAL);
181
0
}
182
183
InputStreamLengthHelper::InputStreamLengthHelper(nsIInputStream* aStream,
184
                                                 const std::function<void(int64_t aLength)>& aCallback)
185
  : Runnable("InputStreamLengthHelper")
186
  , mStream(aStream)
187
  , mCallback(aCallback)
188
0
{
189
0
  MOZ_ASSERT(aStream);
190
0
  MOZ_ASSERT(aCallback);
191
0
}
192
193
0
InputStreamLengthHelper::~InputStreamLengthHelper() = default;
194
195
NS_IMETHODIMP
196
InputStreamLengthHelper::Run()
197
0
{
198
0
  // Sync length access.
199
0
  nsCOMPtr<nsIInputStreamLength> streamLength = do_QueryInterface(mStream);
200
0
  if (streamLength) {
201
0
    int64_t length = -1;
202
0
    nsresult rv = streamLength->Length(&length);
203
0
204
0
    // All good!
205
0
    if (NS_SUCCEEDED(rv)) {
206
0
      ExecCallback(length);
207
0
      return NS_OK;
208
0
    }
209
0
210
0
    // Already closed stream or an error occurred.
211
0
    if (rv == NS_BASE_STREAM_CLOSED ||
212
0
        NS_WARN_IF(rv == NS_ERROR_NOT_AVAILABLE) ||
213
0
        NS_WARN_IF(rv != NS_BASE_STREAM_WOULD_BLOCK)) {
214
0
      ExecCallback(-1);
215
0
      return NS_OK;
216
0
    }
217
0
  }
218
0
219
0
  // Async length access.
220
0
  nsCOMPtr<nsIAsyncInputStreamLength> asyncStreamLength =
221
0
    do_QueryInterface(mStream);
222
0
  if (asyncStreamLength) {
223
0
    nsresult rv =
224
0
     asyncStreamLength->AsyncLengthWait(this,
225
0
                                        GetCurrentThreadSerialEventTarget());
226
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
227
0
      ExecCallback(-1);
228
0
    }
229
0
230
0
    return NS_OK;
231
0
  }
232
0
233
0
  // Fallback using available().
234
0
  uint64_t available = 0;
235
0
  nsresult rv = mStream->Available(&available);
236
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
237
0
    ExecCallback(-1);
238
0
    return NS_OK;
239
0
  }
240
0
241
0
  ExecCallback((int64_t)available);
242
0
  return NS_OK;
243
0
}
244
245
NS_IMETHODIMP
246
InputStreamLengthHelper::OnInputStreamLengthReady(nsIAsyncInputStreamLength* aStream,
247
                                                  int64_t aLength)
248
0
{
249
0
  ExecCallback(aLength);
250
0
  return NS_OK;
251
0
}
252
253
void
254
InputStreamLengthHelper::ExecCallback(int64_t aLength)
255
0
{
256
0
  MOZ_ASSERT(mCallback);
257
0
258
0
  std::function<void(int64_t aLength)> callback;
259
0
  callback.swap(mCallback);
260
0
261
0
  callback(aLength);
262
0
}
263
264
NS_IMPL_ISUPPORTS_INHERITED(InputStreamLengthHelper, Runnable,
265
                            nsIInputStreamLengthCallback)
266
267
} // mozilla namespace