michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 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 "nsStreamLoader.h" michael@0: #include "nsIInputStream.h" michael@0: #include "nsIChannel.h" michael@0: #include "nsError.h" michael@0: #include "GeckoProfiler.h" michael@0: michael@0: nsStreamLoader::nsStreamLoader() michael@0: : mData(nullptr), michael@0: mAllocated(0), michael@0: mLength(0) michael@0: { michael@0: } michael@0: michael@0: nsStreamLoader::~nsStreamLoader() michael@0: { michael@0: ReleaseData(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStreamLoader::Init(nsIStreamLoaderObserver* observer) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(observer); michael@0: mObserver = observer; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsStreamLoader::Create(nsISupports *aOuter, REFNSIID aIID, void **aResult) michael@0: { michael@0: if (aOuter) return NS_ERROR_NO_AGGREGATION; michael@0: michael@0: nsStreamLoader* it = new nsStreamLoader(); michael@0: if (it == nullptr) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: NS_ADDREF(it); michael@0: nsresult rv = it->QueryInterface(aIID, aResult); michael@0: NS_RELEASE(it); michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(nsStreamLoader, nsIStreamLoader, michael@0: nsIRequestObserver, nsIStreamListener) michael@0: michael@0: NS_IMETHODIMP michael@0: nsStreamLoader::GetNumBytesRead(uint32_t* aNumBytes) michael@0: { michael@0: *aNumBytes = mLength; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* readonly attribute nsIRequest request; */ michael@0: NS_IMETHODIMP michael@0: nsStreamLoader::GetRequest(nsIRequest **aRequest) michael@0: { michael@0: NS_IF_ADDREF(*aRequest = mRequest); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStreamLoader::OnStartRequest(nsIRequest* request, nsISupports *ctxt) michael@0: { michael@0: nsCOMPtr chan( do_QueryInterface(request) ); michael@0: if (chan) { michael@0: int64_t contentLength = -1; michael@0: chan->GetContentLength(&contentLength); michael@0: if (contentLength >= 0) { michael@0: if (contentLength > UINT32_MAX) { michael@0: // Too big to fit into uint32, so let's bail. michael@0: // XXX we should really make mAllocated and mLength 64-bit instead. michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: uint32_t contentLength32 = uint32_t(contentLength); michael@0: // preallocate buffer michael@0: mData = static_cast(moz_malloc(contentLength32)); michael@0: if (!mData) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: mAllocated = contentLength32; michael@0: } michael@0: } michael@0: mContext = ctxt; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStreamLoader::OnStopRequest(nsIRequest* request, nsISupports *ctxt, michael@0: nsresult aStatus) michael@0: { michael@0: PROFILER_LABEL("network", "nsStreamLoader::OnStopRequest"); michael@0: if (mObserver) { michael@0: // provide nsIStreamLoader::request during call to OnStreamComplete michael@0: mRequest = request; michael@0: nsresult rv = mObserver->OnStreamComplete(this, mContext, aStatus, michael@0: mLength, mData); michael@0: if (rv == NS_SUCCESS_ADOPTED_DATA) { michael@0: // the observer now owns the data buffer, and the loader must michael@0: // not deallocate it michael@0: mData = nullptr; michael@0: } michael@0: // done.. cleanup michael@0: ReleaseData(); michael@0: mRequest = 0; michael@0: mObserver = 0; michael@0: mContext = 0; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_METHOD michael@0: nsStreamLoader::WriteSegmentFun(nsIInputStream *inStr, michael@0: void *closure, michael@0: const char *fromSegment, michael@0: uint32_t toOffset, michael@0: uint32_t count, michael@0: uint32_t *writeCount) michael@0: { michael@0: nsStreamLoader *self = (nsStreamLoader *) closure; michael@0: michael@0: if (count > UINT32_MAX - self->mLength) { michael@0: return NS_ERROR_ILLEGAL_VALUE; // is there a better error to use here? michael@0: } michael@0: michael@0: if (self->mLength + count > self->mAllocated) { michael@0: self->mData = static_cast(NS_Realloc(self->mData, michael@0: self->mLength + count)); michael@0: if (!self->mData) { michael@0: self->ReleaseData(); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: self->mAllocated = self->mLength + count; michael@0: } michael@0: michael@0: ::memcpy(self->mData + self->mLength, fromSegment, count); michael@0: self->mLength += count; michael@0: michael@0: *writeCount = count; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStreamLoader::OnDataAvailable(nsIRequest* request, nsISupports *ctxt, michael@0: nsIInputStream *inStr, michael@0: uint64_t sourceOffset, uint32_t count) michael@0: { michael@0: uint32_t countRead; michael@0: return inStr->ReadSegments(WriteSegmentFun, this, count, &countRead); michael@0: } michael@0: michael@0: void michael@0: nsStreamLoader::ReleaseData() michael@0: { michael@0: if (mData) { michael@0: NS_Free(mData); michael@0: mData = nullptr; michael@0: } michael@0: mLength = 0; michael@0: mAllocated = 0; michael@0: }