/* -*- 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 asyncCallback = NS_NewOutputStreamReadyEvent(mCallback, mCallbackTarget); mCallback = nullptr; mCallbackTarget = nullptr; asyncCallback->OnOutputStreamReady(this); } } // namespace net } // namespace mozilla