/* 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/Mutex.h" #include "nsCOMPtr.h" #include "nsITransport.h" #include "nsProxyRelease.h" #include "nsSocketTransportService2.h" #include "nsThreadUtils.h" #include "nsTransportUtils.h" using namespace mozilla; //----------------------------------------------------------------------------- class nsTransportStatusEvent; class nsTransportEventSinkProxy : public nsITransportEventSink { public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSITRANSPORTEVENTSINK nsTransportEventSinkProxy(nsITransportEventSink* sink, nsIEventTarget* target) : mSink(sink), mTarget(target), mLock("nsTransportEventSinkProxy.mLock"), mLastEvent(nullptr) { NS_ADDREF(mSink); } private: virtual ~nsTransportEventSinkProxy() { // our reference to mSink could be the last, so be sure to release // it on the target thread. otherwise, we could get into trouble. NS_ProxyRelease("nsTransportEventSinkProxy::mSink", mTarget, dont_AddRef(mSink)); } public: nsITransportEventSink* mSink; nsCOMPtr mTarget; Mutex mLock MOZ_UNANNOTATED; nsTransportStatusEvent* mLastEvent; }; class nsTransportStatusEvent : public Runnable { public: nsTransportStatusEvent(nsTransportEventSinkProxy* proxy, nsITransport* transport, nsresult status, int64_t progress, int64_t progressMax) : Runnable("nsTransportStatusEvent"), mProxy(proxy), mTransport(transport), mStatus(status), mProgress(progress), mProgressMax(progressMax) {} ~nsTransportStatusEvent() { auto ReleaseTransport = [transport(std::move(mTransport))]() mutable {}; if (!OnSocketThread()) { gSocketTransportService->Dispatch(NS_NewRunnableFunction( "nsHttpConnection::~nsHttpConnection", std::move(ReleaseTransport))); } } NS_IMETHOD Run() override { // since this event is being handled, we need to clear the proxy's ref. // if not coalescing all, then last event may not equal self! { MutexAutoLock lock(mProxy->mLock); if (mProxy->mLastEvent == this) mProxy->mLastEvent = nullptr; } mProxy->mSink->OnTransportStatus(mTransport, mStatus, mProgress, mProgressMax); return NS_OK; } RefPtr mProxy; // parameters to OnTransportStatus nsCOMPtr mTransport; nsresult mStatus; int64_t mProgress; int64_t mProgressMax; }; NS_IMPL_ISUPPORTS(nsTransportEventSinkProxy, nsITransportEventSink) NS_IMETHODIMP nsTransportEventSinkProxy::OnTransportStatus(nsITransport* transport, nsresult status, int64_t progress, int64_t progressMax) { nsresult rv = NS_OK; RefPtr event; { MutexAutoLock lock(mLock); // try to coalesce events! ;-) if (mLastEvent && (mLastEvent->mStatus == status)) { mLastEvent->mStatus = status; mLastEvent->mProgress = progress; mLastEvent->mProgressMax = progressMax; } else { event = new nsTransportStatusEvent(this, transport, status, progress, progressMax); if (!event) rv = NS_ERROR_OUT_OF_MEMORY; mLastEvent = event; // weak ref } } if (event) { rv = mTarget->Dispatch(event, NS_DISPATCH_NORMAL); if (NS_FAILED(rv)) { NS_WARNING("unable to post transport status event"); MutexAutoLock lock(mLock); // cleanup.. don't reference anymore! mLastEvent = nullptr; } } return rv; } //----------------------------------------------------------------------------- nsresult net_NewTransportEventSinkProxy(nsITransportEventSink** result, nsITransportEventSink* sink, nsIEventTarget* target) { RefPtr res = new nsTransportEventSinkProxy(sink, target); res.forget(result); return NS_OK; }