michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsIOService.h" michael@0: #include "nsSyncStreamListener.h" michael@0: #include "nsIPipe.h" michael@0: #include "nsThreadUtils.h" michael@0: #include michael@0: michael@0: nsresult michael@0: nsSyncStreamListener::Init() michael@0: { michael@0: return NS_NewPipe(getter_AddRefs(mPipeIn), michael@0: getter_AddRefs(mPipeOut), michael@0: nsIOService::gDefaultSegmentSize, michael@0: UINT32_MAX, // no size limit michael@0: false, michael@0: false); michael@0: } michael@0: michael@0: nsresult michael@0: nsSyncStreamListener::WaitForData() michael@0: { michael@0: mKeepWaiting = true; michael@0: michael@0: while (mKeepWaiting) michael@0: NS_ENSURE_STATE(NS_ProcessNextEvent(NS_GetCurrentThread())); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsSyncStreamListener::nsISupports michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMPL_ISUPPORTS(nsSyncStreamListener, michael@0: nsIStreamListener, michael@0: nsIRequestObserver, michael@0: nsIInputStream, michael@0: nsISyncStreamListener) michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsSyncStreamListener::nsISyncStreamListener michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: nsSyncStreamListener::GetInputStream(nsIInputStream **result) michael@0: { michael@0: NS_ADDREF(*result = this); michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsSyncStreamListener::nsIStreamListener michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: nsSyncStreamListener::OnStartRequest(nsIRequest *request, michael@0: nsISupports *context) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSyncStreamListener::OnDataAvailable(nsIRequest *request, michael@0: nsISupports *context, michael@0: nsIInputStream *stream, michael@0: uint64_t offset, michael@0: uint32_t count) michael@0: { michael@0: uint32_t bytesWritten; michael@0: michael@0: nsresult rv = mPipeOut->WriteFrom(stream, count, &bytesWritten); michael@0: michael@0: // if we get an error, then return failure. this will cause the michael@0: // channel to be canceled, and as a result our OnStopRequest method michael@0: // will be called immediately. because of this we do not need to michael@0: // set mStatus or mKeepWaiting here. michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: // we expect that all data will be written to the pipe because michael@0: // the pipe was created to have "infinite" room. michael@0: NS_ASSERTION(bytesWritten == count, "did not write all data"); michael@0: michael@0: mKeepWaiting = false; // unblock Read michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSyncStreamListener::OnStopRequest(nsIRequest *request, michael@0: nsISupports *context, michael@0: nsresult status) michael@0: { michael@0: mStatus = status; michael@0: mKeepWaiting = false; // unblock Read michael@0: mDone = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: //----------------------------------------------------------------------------- michael@0: // nsSyncStreamListener::nsIInputStream michael@0: //----------------------------------------------------------------------------- michael@0: michael@0: NS_IMETHODIMP michael@0: nsSyncStreamListener::Close() michael@0: { michael@0: mStatus = NS_BASE_STREAM_CLOSED; michael@0: mDone = true; michael@0: michael@0: // It'd be nice if we could explicitly cancel the request at this point, michael@0: // but we don't have a reference to it, so the best we can do is close the michael@0: // pipe so that the next OnDataAvailable event will fail. michael@0: if (mPipeIn) { michael@0: mPipeIn->Close(); michael@0: mPipeIn = nullptr; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSyncStreamListener::Available(uint64_t *result) michael@0: { michael@0: if (NS_FAILED(mStatus)) michael@0: return mStatus; michael@0: michael@0: mStatus = mPipeIn->Available(result); michael@0: if (NS_SUCCEEDED(mStatus) && (*result == 0) && !mDone) { michael@0: mStatus = WaitForData(); michael@0: if (NS_SUCCEEDED(mStatus)) michael@0: mStatus = mPipeIn->Available(result); michael@0: } michael@0: return mStatus; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSyncStreamListener::Read(char *buf, michael@0: uint32_t bufLen, michael@0: uint32_t *result) michael@0: { michael@0: if (mStatus == NS_BASE_STREAM_CLOSED) { michael@0: *result = 0; michael@0: return NS_OK; michael@0: } michael@0: michael@0: uint64_t avail64; michael@0: if (NS_FAILED(Available(&avail64))) michael@0: return mStatus; michael@0: michael@0: uint32_t avail = (uint32_t)std::min(avail64, (uint64_t)bufLen); michael@0: mStatus = mPipeIn->Read(buf, avail, result); michael@0: return mStatus; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSyncStreamListener::ReadSegments(nsWriteSegmentFun writer, michael@0: void *closure, michael@0: uint32_t count, michael@0: uint32_t *result) michael@0: { michael@0: if (mStatus == NS_BASE_STREAM_CLOSED) { michael@0: *result = 0; michael@0: return NS_OK; michael@0: } michael@0: michael@0: uint64_t avail64; michael@0: if (NS_FAILED(Available(&avail64))) michael@0: return mStatus; michael@0: michael@0: uint32_t avail = (uint32_t)std::min(avail64, (uint64_t)count); michael@0: mStatus = mPipeIn->ReadSegments(writer, closure, avail, result); michael@0: return mStatus; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsSyncStreamListener::IsNonBlocking(bool *result) michael@0: { michael@0: *result = false; michael@0: return NS_OK; michael@0: }