fune/netwerk/protocol/http/AltDataOutputStreamChild.cpp
Nika Layzell e61507c3a3 Bug 1818305 - Part 1: Add a streamStatus method to nsIOutputStream, r=necko-reviewers,geckoview-reviewers,jesup,emilio,m_kato,devtools-reviewers
This new method can be used to check if the nsIOutputStream has been
closed without having data available to write. It should avoid blocking
to discover this information.

Differential Revision: https://phabricator.services.mozilla.com/D170696
2023-03-15 19:52:33 +00:00

204 lines
5.1 KiB
C++

/* -*- 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 "mozilla/net/AltDataOutputStreamChild.h"
#include "mozilla/Unused.h"
#include "nsIInputStream.h"
#include "nsStreamUtils.h"
namespace mozilla {
namespace net {
NS_IMPL_ADDREF(AltDataOutputStreamChild)
NS_IMETHODIMP_(MozExternalRefCountType) AltDataOutputStreamChild::Release() {
MOZ_ASSERT(0 != mRefCnt, "dup release");
MOZ_ASSERT(NS_IsMainThread(), "Main thread only");
--mRefCnt;
NS_LOG_RELEASE(this, mRefCnt, "AltDataOutputStreamChild");
if (mRefCnt == 1 && mIPCOpen) {
// The only reference left is the IPDL one. After the parent replies back
// with a DeleteSelf message, the child will call Send__delete__(this),
// decrementing the ref count and triggering the destructor.
SendDeleteSelf();
return 1;
}
if (mRefCnt == 0) {
mRefCnt = 1; /* stabilize */
delete this;
return 0;
}
return mRefCnt;
}
NS_INTERFACE_MAP_BEGIN(AltDataOutputStreamChild)
NS_INTERFACE_MAP_ENTRY(nsIAsyncOutputStream)
NS_INTERFACE_MAP_ENTRY(nsIOutputStream)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
AltDataOutputStreamChild::AltDataOutputStreamChild()
: mIPCOpen(false), mError(NS_OK), mCallbackFlags(0) {
MOZ_ASSERT(NS_IsMainThread(), "Main thread only");
}
void AltDataOutputStreamChild::AddIPDLReference() {
MOZ_ASSERT(!mIPCOpen, "Attempt to retain more than one IPDL reference");
mIPCOpen = true;
AddRef();
}
void AltDataOutputStreamChild::ReleaseIPDLReference() {
MOZ_ASSERT(mIPCOpen, "Attempt to release nonexistent IPDL reference");
mIPCOpen = false;
if (mCallback) {
NotifyListener();
}
Release();
}
bool AltDataOutputStreamChild::WriteDataInChunks(
const nsDependentCSubstring& data) {
const size_t kChunkSize = 128 * 1024;
size_t next = std::min(data.Length(), kChunkSize);
for (size_t i = 0; i < data.Length();
i = next, next = std::min(data.Length(), next + kChunkSize)) {
nsCString chunk(Substring(data, i, kChunkSize));
if (mIPCOpen && !SendWriteData(chunk)) {
mIPCOpen = false;
return false;
}
}
return true;
}
NS_IMETHODIMP
AltDataOutputStreamChild::Close() { return CloseWithStatus(NS_OK); }
NS_IMETHODIMP
AltDataOutputStreamChild::Flush() {
if (!mIPCOpen) {
return NS_ERROR_NOT_AVAILABLE;
}
if (NS_FAILED(mError)) {
return mError;
}
// This is a no-op
return NS_OK;
}
NS_IMETHODIMP
AltDataOutputStreamChild::StreamStatus() {
if (!mIPCOpen) {
return NS_ERROR_NOT_AVAILABLE;
}
return mError;
}
NS_IMETHODIMP
AltDataOutputStreamChild::Write(const char* aBuf, uint32_t aCount,
uint32_t* _retval) {
if (!mIPCOpen) {
return NS_ERROR_NOT_AVAILABLE;
}
if (NS_FAILED(mError)) {
return mError;
}
if (WriteDataInChunks(nsDependentCSubstring(aBuf, aCount))) {
*_retval = aCount;
return NS_OK;
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
AltDataOutputStreamChild::WriteFrom(nsIInputStream* aFromStream,
uint32_t aCount, uint32_t* _retval) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AltDataOutputStreamChild::WriteSegments(nsReadSegmentFun aReader,
void* aClosure, uint32_t aCount,
uint32_t* _retval) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
AltDataOutputStreamChild::IsNonBlocking(bool* _retval) {
*_retval = false;
return NS_OK;
}
mozilla::ipc::IPCResult AltDataOutputStreamChild::RecvError(
const nsresult& err) {
mError = err;
return IPC_OK();
}
mozilla::ipc::IPCResult AltDataOutputStreamChild::RecvDeleteSelf() {
PAltDataOutputStreamChild::Send__delete__(this);
return IPC_OK();
}
// nsIAsyncOutputStream
NS_IMETHODIMP
AltDataOutputStreamChild::CloseWithStatus(nsresult aStatus) {
if (!mIPCOpen) {
return NS_ERROR_NOT_AVAILABLE;
}
if (NS_FAILED(mError)) {
return mError;
}
Unused << SendClose(aStatus);
return NS_OK;
}
NS_IMETHODIMP
AltDataOutputStreamChild::AsyncWait(nsIOutputStreamCallback* aCallback,
uint32_t aFlags, uint32_t aRequestedCount,
nsIEventTarget* aEventTarget) {
mCallback = aCallback;
mCallbackFlags = aFlags;
mCallbackTarget = aEventTarget;
if (!mCallback) {
return NS_OK;
}
// The stream is blocking so it is writable at any time
if (!mIPCOpen || !(aFlags & WAIT_CLOSURE_ONLY)) {
NotifyListener();
}
return NS_OK;
}
void AltDataOutputStreamChild::NotifyListener() {
MOZ_ASSERT(mCallback);
if (!mCallbackTarget) {
mCallbackTarget = GetMainThreadSerialEventTarget();
}
nsCOMPtr<nsIOutputStreamCallback> asyncCallback =
NS_NewOutputStreamReadyEvent(mCallback, mCallbackTarget);
mCallback = nullptr;
mCallbackTarget = nullptr;
asyncCallback->OnOutputStreamReady(this);
}
} // namespace net
} // namespace mozilla