2018-05-23 08:12:35 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
#include "InputStreamLengthHelper.h"
|
|
|
|
#include "mozilla/dom/WorkerCommon.h"
|
2018-05-31 19:12:26 +03:00
|
|
|
#include "nsIAsyncInputStream.h"
|
2018-05-23 08:12:35 +03:00
|
|
|
#include "nsIInputStream.h"
|
2018-05-23 08:12:36 +03:00
|
|
|
#include "nsIStreamTransportService.h"
|
|
|
|
|
|
|
|
static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
|
2018-05-23 08:12:35 +03:00
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
|
2018-05-23 08:12:36 +03:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
class AvailableEvent final : public Runnable
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
AvailableEvent(nsIInputStream* stream,
|
|
|
|
const std::function<void(int64_t aLength)>& aCallback)
|
|
|
|
: Runnable("mozilla::AvailableEvent")
|
|
|
|
, mStream(stream)
|
|
|
|
, mCallback(aCallback)
|
|
|
|
, mSize(-1)
|
|
|
|
{
|
|
|
|
mCallbackTarget = GetCurrentThreadSerialEventTarget();
|
|
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHOD
|
|
|
|
Run() override
|
|
|
|
{
|
|
|
|
// ping
|
|
|
|
if (!NS_IsMainThread()) {
|
|
|
|
uint64_t size = 0;
|
|
|
|
if (NS_WARN_IF(NS_FAILED(mStream->Available(&size)))) {
|
|
|
|
mSize = -1;
|
|
|
|
} else {
|
|
|
|
mSize = (int64_t)size;
|
|
|
|
}
|
|
|
|
|
|
|
|
mStream = nullptr;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIRunnable> self(this); // overly cute
|
|
|
|
mCallbackTarget->Dispatch(self.forget(), NS_DISPATCH_NORMAL);
|
|
|
|
mCallbackTarget = nullptr;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// pong
|
2018-05-31 19:12:25 +03:00
|
|
|
std::function<void(int64_t aLength)> callback;
|
|
|
|
callback.swap(mCallback);
|
|
|
|
callback(mSize);
|
2018-05-23 08:12:36 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
nsCOMPtr<nsIInputStream> mStream;
|
|
|
|
std::function<void(int64_t aLength)> mCallback;
|
|
|
|
nsCOMPtr<nsIEventTarget> mCallbackTarget;
|
|
|
|
|
|
|
|
int64_t mSize;
|
|
|
|
};
|
|
|
|
|
|
|
|
} // anonymous
|
|
|
|
|
|
|
|
/* static */ bool
|
|
|
|
InputStreamLengthHelper::GetSyncLength(nsIInputStream* aStream,
|
|
|
|
int64_t* aLength)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aStream);
|
|
|
|
MOZ_ASSERT(aLength);
|
|
|
|
|
|
|
|
*aLength = -1;
|
|
|
|
|
|
|
|
// Sync length access.
|
|
|
|
nsCOMPtr<nsIInputStreamLength> streamLength = do_QueryInterface(aStream);
|
|
|
|
if (streamLength) {
|
|
|
|
int64_t length = -1;
|
|
|
|
nsresult rv = streamLength->Length(&length);
|
|
|
|
|
|
|
|
// All good!
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
*aLength = length;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Already closed stream or an error occurred.
|
|
|
|
if (rv == NS_BASE_STREAM_CLOSED ||
|
|
|
|
NS_WARN_IF(rv == NS_ERROR_NOT_AVAILABLE) ||
|
|
|
|
NS_WARN_IF(rv != NS_BASE_STREAM_WOULD_BLOCK)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIAsyncInputStreamLength> asyncStreamLength =
|
|
|
|
do_QueryInterface(aStream);
|
|
|
|
if (asyncStreamLength) {
|
|
|
|
// GetAsyncLength should be used.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-05-31 19:12:26 +03:00
|
|
|
// We cannot calculate the length of an async stream.
|
|
|
|
nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(aStream);
|
|
|
|
if (asyncStream) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-05-23 08:12:36 +03:00
|
|
|
// For main-thread only, we want to avoid calling ::Available() for blocking
|
|
|
|
// streams.
|
|
|
|
if (NS_IsMainThread()) {
|
|
|
|
bool nonBlocking = false;
|
|
|
|
if (NS_WARN_IF(NS_FAILED(aStream->IsNonBlocking(&nonBlocking)))) {
|
|
|
|
// Let's return -1. There is nothing else we can do here.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!nonBlocking) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fallback using available().
|
|
|
|
uint64_t available = 0;
|
|
|
|
nsresult rv = aStream->Available(&available);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
// Let's return -1. There is nothing else we can do here.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
*aLength = (int64_t)available;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-05-23 08:12:35 +03:00
|
|
|
/* static */ void
|
2018-05-23 08:12:36 +03:00
|
|
|
InputStreamLengthHelper::GetAsyncLength(nsIInputStream* aStream,
|
|
|
|
const std::function<void(int64_t aLength)>& aCallback)
|
2018-05-23 08:12:35 +03:00
|
|
|
{
|
|
|
|
MOZ_ASSERT(aStream);
|
|
|
|
MOZ_ASSERT(aCallback);
|
|
|
|
|
|
|
|
// We don't want to allow this class to be used on workers because we are not
|
|
|
|
// using the correct Runnable types.
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread() || !dom::IsCurrentThreadRunningWorker());
|
|
|
|
|
|
|
|
RefPtr<InputStreamLengthHelper> helper =
|
|
|
|
new InputStreamLengthHelper(aStream, aCallback);
|
|
|
|
|
2018-05-23 08:12:36 +03:00
|
|
|
// Let's be sure that we don't call ::Available() on main-thread.
|
|
|
|
if (NS_IsMainThread()) {
|
|
|
|
nsCOMPtr<nsIInputStreamLength> streamLength = do_QueryInterface(aStream);
|
|
|
|
nsCOMPtr<nsIAsyncInputStreamLength> asyncStreamLength =
|
|
|
|
do_QueryInterface(aStream);
|
|
|
|
if (!streamLength && !asyncStreamLength) {
|
2018-05-31 19:12:26 +03:00
|
|
|
// We cannot calculate the length of an async stream. We must fix the
|
|
|
|
// caller if this happens.
|
2018-06-06 00:19:00 +03:00
|
|
|
#ifdef DEBUG
|
2018-05-31 19:12:26 +03:00
|
|
|
nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(aStream);
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(!asyncStream);
|
2018-06-06 00:19:00 +03:00
|
|
|
#endif
|
2018-05-31 19:12:26 +03:00
|
|
|
|
2018-05-23 08:12:36 +03:00
|
|
|
bool nonBlocking = false;
|
|
|
|
if (NS_SUCCEEDED(aStream->IsNonBlocking(&nonBlocking)) && !nonBlocking) {
|
|
|
|
nsCOMPtr<nsIEventTarget> target =
|
|
|
|
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
|
|
|
|
MOZ_ASSERT(target);
|
|
|
|
|
|
|
|
RefPtr<AvailableEvent> event = new AvailableEvent(aStream, aCallback);
|
|
|
|
target->Dispatch(event.forget(), NS_DISPATCH_NORMAL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-23 08:12:35 +03:00
|
|
|
// Let's go async in order to have similar behaviors for sync and async
|
|
|
|
// nsIInputStreamLength implementations.
|
|
|
|
GetCurrentThreadSerialEventTarget()->Dispatch(helper, NS_DISPATCH_NORMAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
InputStreamLengthHelper::InputStreamLengthHelper(nsIInputStream* aStream,
|
|
|
|
const std::function<void(int64_t aLength)>& aCallback)
|
|
|
|
: Runnable("InputStreamLengthHelper")
|
|
|
|
, mStream(aStream)
|
|
|
|
, mCallback(aCallback)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aStream);
|
|
|
|
MOZ_ASSERT(aCallback);
|
|
|
|
}
|
|
|
|
|
|
|
|
InputStreamLengthHelper::~InputStreamLengthHelper() = default;
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
InputStreamLengthHelper::Run()
|
|
|
|
{
|
|
|
|
// Sync length access.
|
|
|
|
nsCOMPtr<nsIInputStreamLength> streamLength = do_QueryInterface(mStream);
|
|
|
|
if (streamLength) {
|
|
|
|
int64_t length = -1;
|
|
|
|
nsresult rv = streamLength->Length(&length);
|
|
|
|
|
|
|
|
// All good!
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
2018-05-31 19:12:25 +03:00
|
|
|
ExecCallback(length);
|
2018-05-23 08:12:35 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Already closed stream or an error occurred.
|
|
|
|
if (rv == NS_BASE_STREAM_CLOSED ||
|
|
|
|
NS_WARN_IF(rv == NS_ERROR_NOT_AVAILABLE) ||
|
|
|
|
NS_WARN_IF(rv != NS_BASE_STREAM_WOULD_BLOCK)) {
|
2018-05-31 19:12:25 +03:00
|
|
|
ExecCallback(-1);
|
2018-05-23 08:12:35 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Async length access.
|
|
|
|
nsCOMPtr<nsIAsyncInputStreamLength> asyncStreamLength =
|
|
|
|
do_QueryInterface(mStream);
|
|
|
|
if (asyncStreamLength) {
|
|
|
|
nsresult rv =
|
|
|
|
asyncStreamLength->AsyncLengthWait(this,
|
|
|
|
GetCurrentThreadSerialEventTarget());
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
2018-05-31 19:12:25 +03:00
|
|
|
ExecCallback(-1);
|
2018-05-23 08:12:35 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fallback using available().
|
|
|
|
uint64_t available = 0;
|
|
|
|
nsresult rv = mStream->Available(&available);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
2018-05-31 19:12:25 +03:00
|
|
|
ExecCallback(-1);
|
2018-05-23 08:12:35 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2018-05-31 19:12:25 +03:00
|
|
|
ExecCallback((int64_t)available);
|
2018-05-23 08:12:35 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
InputStreamLengthHelper::OnInputStreamLengthReady(nsIAsyncInputStreamLength* aStream,
|
|
|
|
int64_t aLength)
|
|
|
|
{
|
2018-05-31 19:12:25 +03:00
|
|
|
ExecCallback(aLength);
|
2018-05-23 08:12:35 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2018-05-31 19:12:25 +03:00
|
|
|
void
|
|
|
|
InputStreamLengthHelper::ExecCallback(int64_t aLength)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(mCallback);
|
|
|
|
|
|
|
|
std::function<void(int64_t aLength)> callback;
|
|
|
|
callback.swap(mCallback);
|
|
|
|
|
|
|
|
callback(aLength);
|
|
|
|
}
|
|
|
|
|
2018-05-23 08:12:35 +03:00
|
|
|
NS_IMPL_ISUPPORTS_INHERITED(InputStreamLengthHelper, Runnable,
|
|
|
|
nsIInputStreamLengthCallback)
|
|
|
|
|
|
|
|
} // mozilla namespace
|