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 "nsStreamListenerTee.h" michael@0: #include "nsProxyRelease.h" michael@0: michael@0: NS_IMPL_ISUPPORTS(nsStreamListenerTee, michael@0: nsIStreamListener, michael@0: nsIRequestObserver, michael@0: nsIStreamListenerTee, michael@0: nsIThreadRetargetableStreamListener) michael@0: michael@0: NS_IMETHODIMP michael@0: nsStreamListenerTee::OnStartRequest(nsIRequest *request, michael@0: nsISupports *context) michael@0: { michael@0: NS_ENSURE_TRUE(mListener, NS_ERROR_NOT_INITIALIZED); michael@0: nsresult rv1 = mListener->OnStartRequest(request, context); michael@0: nsresult rv2 = NS_OK; michael@0: if (mObserver) michael@0: rv2 = mObserver->OnStartRequest(request, context); michael@0: michael@0: // Preserve NS_SUCCESS_XXX in rv1 in case mObserver didn't throw michael@0: return (NS_FAILED(rv2) && NS_SUCCEEDED(rv1)) ? rv2 : rv1; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStreamListenerTee::OnStopRequest(nsIRequest *request, michael@0: nsISupports *context, michael@0: nsresult status) michael@0: { michael@0: NS_ENSURE_TRUE(mListener, NS_ERROR_NOT_INITIALIZED); michael@0: // it is critical that we close out the input stream tee michael@0: if (mInputTee) { michael@0: mInputTee->SetSink(nullptr); michael@0: mInputTee = 0; michael@0: } michael@0: michael@0: // release sink on the same thread where the data was written (bug 716293) michael@0: if (mEventTarget) { michael@0: nsIOutputStream *sink = nullptr; michael@0: mSink.swap(sink); michael@0: if (NS_FAILED(NS_ProxyRelease(mEventTarget, sink))) { michael@0: NS_WARNING("Releasing sink on the current thread!"); michael@0: NS_RELEASE(sink); michael@0: } michael@0: } michael@0: else { michael@0: mSink = 0; michael@0: } michael@0: michael@0: nsresult rv = mListener->OnStopRequest(request, context, status); michael@0: if (mObserver) michael@0: mObserver->OnStopRequest(request, context, status); michael@0: mObserver = 0; michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStreamListenerTee::OnDataAvailable(nsIRequest *request, michael@0: nsISupports *context, michael@0: nsIInputStream *input, michael@0: uint64_t offset, michael@0: uint32_t count) michael@0: { michael@0: NS_ENSURE_TRUE(mListener, NS_ERROR_NOT_INITIALIZED); michael@0: NS_ENSURE_TRUE(mSink, NS_ERROR_NOT_INITIALIZED); michael@0: michael@0: nsCOMPtr tee; michael@0: nsresult rv; michael@0: michael@0: if (!mInputTee) { michael@0: if (mEventTarget) michael@0: rv = NS_NewInputStreamTeeAsync(getter_AddRefs(tee), input, michael@0: mSink, mEventTarget); michael@0: else michael@0: rv = NS_NewInputStreamTee(getter_AddRefs(tee), input, mSink); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: mInputTee = do_QueryInterface(tee, &rv); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: else { michael@0: // re-initialize the input tee since the input stream may have changed. michael@0: rv = mInputTee->SetSource(input); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: tee = do_QueryInterface(mInputTee, &rv); michael@0: if (NS_FAILED(rv)) return rv; michael@0: } michael@0: michael@0: return mListener->OnDataAvailable(request, context, tee, offset, count); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStreamListenerTee::CheckListenerChain() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Should be on main thread!"); michael@0: nsresult rv = NS_OK; michael@0: nsCOMPtr retargetableListener = michael@0: do_QueryInterface(mListener, &rv); michael@0: if (retargetableListener) { michael@0: rv = retargetableListener->CheckListenerChain(); michael@0: } michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: if (!mObserver) { michael@0: return rv; michael@0: } michael@0: retargetableListener = do_QueryInterface(mObserver, &rv); michael@0: if (retargetableListener) { michael@0: rv = retargetableListener->CheckListenerChain(); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStreamListenerTee::Init(nsIStreamListener *listener, michael@0: nsIOutputStream *sink, michael@0: nsIRequestObserver *requestObserver) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(listener); michael@0: NS_ENSURE_ARG_POINTER(sink); michael@0: mListener = listener; michael@0: mSink = sink; michael@0: mObserver = requestObserver; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsStreamListenerTee::InitAsync(nsIStreamListener *listener, michael@0: nsIEventTarget *eventTarget, michael@0: nsIOutputStream *sink, michael@0: nsIRequestObserver *requestObserver) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(eventTarget); michael@0: mEventTarget = eventTarget; michael@0: return Init(listener, sink, requestObserver); michael@0: }