diff options
Diffstat (limited to 'netwerk/base/nsStreamListenerTee.cpp')
-rw-r--r-- | netwerk/base/nsStreamListenerTee.cpp | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/netwerk/base/nsStreamListenerTee.cpp b/netwerk/base/nsStreamListenerTee.cpp new file mode 100644 index 0000000000..445d0fe81a --- /dev/null +++ b/netwerk/base/nsStreamListenerTee.cpp @@ -0,0 +1,159 @@ +/* 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 "nsStreamListenerTee.h" +#include "nsProxyRelease.h" +#include "nsIRequest.h" + +namespace mozilla { +namespace net { + +NS_IMPL_ISUPPORTS(nsStreamListenerTee, nsIStreamListener, nsIRequestObserver, + nsIStreamListenerTee, nsIThreadRetargetableStreamListener, + nsIMultiPartChannelListener) + +NS_IMETHODIMP +nsStreamListenerTee::OnStartRequest(nsIRequest* request) { + NS_ENSURE_TRUE(mListener, NS_ERROR_NOT_INITIALIZED); + + nsCOMPtr<nsIMultiPartChannel> multiPartChannel = do_QueryInterface(request); + if (multiPartChannel) { + mIsMultiPart = true; + } + + nsresult rv1 = mListener->OnStartRequest(request); + nsresult rv2 = NS_OK; + if (mObserver) rv2 = mObserver->OnStartRequest(request); + + // Preserve NS_SUCCESS_XXX in rv1 in case mObserver didn't throw + return (NS_FAILED(rv2) && NS_SUCCEEDED(rv1)) ? rv2 : rv1; +} + +NS_IMETHODIMP +nsStreamListenerTee::OnStopRequest(nsIRequest* request, nsresult status) { + NS_ENSURE_TRUE(mListener, NS_ERROR_NOT_INITIALIZED); + // it is critical that we close out the input stream tee + if (mInputTee) { + mInputTee->SetSink(nullptr); + mInputTee = nullptr; + } + + if (!mIsMultiPart) { + // release sink on the same thread where the data was written (bug 716293) + if (mEventTarget) { + NS_ProxyRelease("nsStreamListenerTee::mSink", mEventTarget, + mSink.forget()); + } else { + mSink = nullptr; + } + } + + nsresult rv = mListener->OnStopRequest(request, status); + if (mObserver) mObserver->OnStopRequest(request, status); + if (!mIsMultiPart) { + mObserver = nullptr; + } + return rv; +} + +NS_IMETHODIMP +nsStreamListenerTee::OnDataAvailable(nsIRequest* request, nsIInputStream* input, + uint64_t offset, uint32_t count) { + NS_ENSURE_TRUE(mListener, NS_ERROR_NOT_INITIALIZED); + NS_ENSURE_TRUE(mSink, NS_ERROR_NOT_INITIALIZED); + + nsCOMPtr<nsIInputStream> tee; + nsresult rv; + + if (!mInputTee) { + if (mEventTarget) { + rv = NS_NewInputStreamTeeAsync(getter_AddRefs(tee), input, mSink, + mEventTarget); + } else { + rv = NS_NewInputStreamTee(getter_AddRefs(tee), input, mSink); + } + if (NS_FAILED(rv)) return rv; + + mInputTee = do_QueryInterface(tee, &rv); + if (NS_FAILED(rv)) return rv; + } else { + // re-initialize the input tee since the input stream may have changed. + rv = mInputTee->SetSource(input); + if (NS_FAILED(rv)) return rv; + + tee = mInputTee; + } + + return mListener->OnDataAvailable(request, tee, offset, count); +} + +NS_IMETHODIMP +nsStreamListenerTee::OnAfterLastPart(nsresult aStatus) { + // release sink on the same thread where the data was written (bug 716293) + if (mEventTarget) { + NS_ProxyRelease("nsStreamListenerTee::mSink", mEventTarget, mSink.forget()); + } else { + mSink = nullptr; + } + + if (nsCOMPtr<nsIMultiPartChannelListener> multi = + do_QueryInterface(mListener)) { + multi->OnAfterLastPart(aStatus); + } + if (!SameCOMIdentity(mListener, mObserver)) { + if (nsCOMPtr<nsIMultiPartChannelListener> multi = + do_QueryInterface(mObserver)) { + multi->OnAfterLastPart(aStatus); + } + } + + mObserver = nullptr; + return NS_OK; +} + +NS_IMETHODIMP +nsStreamListenerTee::CheckListenerChain() { + NS_ASSERTION(NS_IsMainThread(), "Should be on main thread!"); + nsresult rv = NS_OK; + nsCOMPtr<nsIThreadRetargetableStreamListener> retargetableListener = + do_QueryInterface(mListener, &rv); + if (retargetableListener) { + rv = retargetableListener->CheckListenerChain(); + } + if (NS_FAILED(rv)) { + return rv; + } + if (!mObserver) { + return rv; + } + retargetableListener = do_QueryInterface(mObserver, &rv); + if (retargetableListener) { + rv = retargetableListener->CheckListenerChain(); + } + return rv; +} + +NS_IMETHODIMP +nsStreamListenerTee::Init(nsIStreamListener* listener, nsIOutputStream* sink, + nsIRequestObserver* requestObserver) { + NS_ENSURE_ARG_POINTER(listener); + NS_ENSURE_ARG_POINTER(sink); + mListener = listener; + mSink = sink; + mObserver = requestObserver; + return NS_OK; +} + +NS_IMETHODIMP +nsStreamListenerTee::InitAsync(nsIStreamListener* listener, + nsIEventTarget* eventTarget, + nsIOutputStream* sink, + nsIRequestObserver* requestObserver) { + NS_ENSURE_ARG_POINTER(eventTarget); + mEventTarget = eventTarget; + return Init(listener, sink, requestObserver); +} + +} // namespace net +} // namespace mozilla |