summaryrefslogtreecommitdiffstats
path: root/netwerk/base/nsTransportUtils.cpp
blob: 4b965a4fdbf4e59940a96d19f5a2154e92a77db5 (plain)
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
/* 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 "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) {
  RefPtr<nsTransportEventSinkProxy> res =
      new nsTransportEventSinkProxy(sink, target);
  res.forget(result);
  return NS_OK;
}