Source code

Revision control

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
/* 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 "nsTransportUtils.h"
#include "nsITransport.h"
#include "nsProxyRelease.h"
#include "nsThreadUtils.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.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<nsIEventTarget> mTarget;
  Mutex mLock;
  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() = default;

  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<nsTransportEventSinkProxy> mProxy;

  // parameters to OnTransportStatus
  nsCOMPtr<nsITransport> 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<nsTransportStatusEvent> 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) {
  *result = new nsTransportEventSinkProxy(sink, target);
  if (!*result) return NS_ERROR_OUT_OF_MEMORY;
  NS_ADDREF(*result);
  return NS_OK;
}