tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

EventSourceEventService.cpp (9454B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "EventSourceEventService.h"
      8 
      9 #include "mozilla/Services.h"
     10 #include "mozilla/StaticPtr.h"
     11 #include "nsIObserverService.h"
     12 #include "nsISupportsPrimitives.h"
     13 #include "nsSocketTransportService2.h"
     14 #include "nsThreadUtils.h"
     15 #include "nsXULAppAPI.h"
     16 
     17 namespace mozilla::dom {
     18 
     19 namespace {
     20 
     21 StaticRefPtr<EventSourceEventService> gEventSourceEventService;
     22 
     23 }  // anonymous namespace
     24 
     25 class EventSourceBaseRunnable : public Runnable {
     26 public:
     27  EventSourceBaseRunnable(uint64_t aHttpChannelId, uint64_t aInnerWindowID)
     28      : Runnable("dom::EventSourceBaseRunnable"),
     29        mHttpChannelId(aHttpChannelId),
     30        mInnerWindowID(aInnerWindowID) {}
     31 
     32  NS_IMETHOD Run() override {
     33    MOZ_ASSERT(NS_IsMainThread());
     34    RefPtr<EventSourceEventService> service =
     35        EventSourceEventService::GetOrCreate();
     36    MOZ_ASSERT(service);
     37 
     38    EventSourceEventService::EventSourceListeners listeners;
     39 
     40    service->GetListeners(mInnerWindowID, listeners);
     41 
     42    for (uint32_t i = 0; i < listeners.Length(); ++i) {
     43      DoWork(listeners[i]);
     44    }
     45 
     46    return NS_OK;
     47  }
     48 
     49 protected:
     50  ~EventSourceBaseRunnable() = default;
     51 
     52  virtual void DoWork(nsIEventSourceEventListener* aListener) = 0;
     53 
     54  uint64_t mHttpChannelId;
     55  uint64_t mInnerWindowID;
     56 };
     57 
     58 class EventSourceConnectionOpenedRunnable final
     59    : public EventSourceBaseRunnable {
     60 public:
     61  EventSourceConnectionOpenedRunnable(uint64_t aHttpChannelId,
     62                                      uint64_t aInnerWindowID)
     63      : EventSourceBaseRunnable(aHttpChannelId, aInnerWindowID) {}
     64 
     65 private:
     66  virtual void DoWork(nsIEventSourceEventListener* aListener) override {
     67    DebugOnly<nsresult> rv =
     68        aListener->EventSourceConnectionOpened(mHttpChannelId);
     69    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
     70                         "EventSourceConnectionOpened failed");
     71  }
     72 };
     73 
     74 class EventSourceConnectionClosedRunnable final
     75    : public EventSourceBaseRunnable {
     76 public:
     77  EventSourceConnectionClosedRunnable(uint64_t aHttpChannelId,
     78                                      uint64_t aInnerWindowID)
     79      : EventSourceBaseRunnable(aHttpChannelId, aInnerWindowID) {}
     80 
     81 private:
     82  virtual void DoWork(nsIEventSourceEventListener* aListener) override {
     83    DebugOnly<nsresult> rv =
     84        aListener->EventSourceConnectionClosed(mHttpChannelId);
     85    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
     86                         "EventSourceConnectionClosed failed");
     87  }
     88 };
     89 
     90 class EventSourceEventRunnable final : public EventSourceBaseRunnable {
     91 public:
     92  EventSourceEventRunnable(uint64_t aHttpChannelId, uint64_t aInnerWindowID,
     93                           const nsAString& aEventName,
     94                           const nsAString& aLastEventID,
     95                           const nsAString& aData, uint32_t aRetry,
     96                           DOMHighResTimeStamp aTimeStamp)
     97      : EventSourceBaseRunnable(aHttpChannelId, aInnerWindowID),
     98        mEventName(aEventName),
     99        mLastEventID(aLastEventID),
    100        mData(aData),
    101        mRetry(aRetry),
    102        mTimeStamp(aTimeStamp) {}
    103 
    104 private:
    105  virtual void DoWork(nsIEventSourceEventListener* aListener) override {
    106    DebugOnly<nsresult> rv = aListener->EventReceived(
    107        mHttpChannelId, mEventName, mLastEventID, mData, mRetry, mTimeStamp);
    108 
    109    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Event op failed");
    110  }
    111 
    112  nsString mEventName;
    113  nsString mLastEventID;
    114  nsString mData;
    115  uint32_t mRetry;
    116  DOMHighResTimeStamp mTimeStamp;
    117 };
    118 
    119 /* static */
    120 already_AddRefed<EventSourceEventService>
    121 EventSourceEventService::GetOrCreate() {
    122  MOZ_ASSERT(NS_IsMainThread());
    123 
    124  if (!gEventSourceEventService) {
    125    gEventSourceEventService = new EventSourceEventService();
    126  }
    127 
    128  RefPtr<EventSourceEventService> service = gEventSourceEventService.get();
    129  return service.forget();
    130 }
    131 
    132 NS_INTERFACE_MAP_BEGIN(EventSourceEventService)
    133  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEventSourceEventService)
    134  NS_INTERFACE_MAP_ENTRY(nsIObserver)
    135  NS_INTERFACE_MAP_ENTRY(nsIEventSourceEventService)
    136 NS_INTERFACE_MAP_END
    137 
    138 NS_IMPL_ADDREF(EventSourceEventService)
    139 NS_IMPL_RELEASE(EventSourceEventService)
    140 
    141 EventSourceEventService::EventSourceEventService() : mCountListeners(0) {
    142  MOZ_ASSERT(NS_IsMainThread());
    143 
    144  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
    145  if (obs) {
    146    obs->AddObserver(this, "xpcom-shutdown", false);
    147    obs->AddObserver(this, "inner-window-destroyed", false);
    148  }
    149 }
    150 
    151 EventSourceEventService::~EventSourceEventService() {
    152  MOZ_ASSERT(NS_IsMainThread());
    153 }
    154 
    155 void EventSourceEventService::EventSourceConnectionOpened(
    156    uint64_t aHttpChannelId, uint64_t aInnerWindowID) {
    157  // Let's continue only if we have some listeners.
    158  if (!HasListeners()) {
    159    return;
    160  }
    161 
    162  RefPtr<EventSourceConnectionOpenedRunnable> runnable =
    163      new EventSourceConnectionOpenedRunnable(aHttpChannelId, aInnerWindowID);
    164  DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable);
    165  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed");
    166 }
    167 
    168 void EventSourceEventService::EventSourceConnectionClosed(
    169    uint64_t aHttpChannelId, uint64_t aInnerWindowID) {
    170  // Let's continue only if we have some listeners.
    171  if (!HasListeners()) {
    172    return;
    173  }
    174  RefPtr<EventSourceConnectionClosedRunnable> runnable =
    175      new EventSourceConnectionClosedRunnable(aHttpChannelId, aInnerWindowID);
    176  DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable);
    177  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed");
    178 }
    179 
    180 void EventSourceEventService::EventReceived(
    181    uint64_t aHttpChannelId, uint64_t aInnerWindowID,
    182    const nsAString& aEventName, const nsAString& aLastEventID,
    183    const nsAString& aData, uint32_t aRetry, DOMHighResTimeStamp aTimeStamp) {
    184  // Let's continue only if we have some listeners.
    185  if (!HasListeners()) {
    186    return;
    187  }
    188 
    189  RefPtr<EventSourceEventRunnable> runnable =
    190      new EventSourceEventRunnable(aHttpChannelId, aInnerWindowID, aEventName,
    191                                   aLastEventID, aData, aRetry, aTimeStamp);
    192  DebugOnly<nsresult> rv = NS_DispatchToMainThread(runnable);
    193  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed");
    194 }
    195 
    196 NS_IMETHODIMP
    197 EventSourceEventService::AddListener(uint64_t aInnerWindowID,
    198                                     nsIEventSourceEventListener* aListener) {
    199  MOZ_ASSERT(NS_IsMainThread());
    200  if (!aListener) {
    201    return NS_ERROR_FAILURE;
    202  }
    203  ++mCountListeners;
    204 
    205  WindowListener* listener = mWindows.GetOrInsertNew(aInnerWindowID);
    206 
    207  listener->mListeners.AppendElement(aListener);
    208 
    209  return NS_OK;
    210 }
    211 
    212 NS_IMETHODIMP
    213 EventSourceEventService::RemoveListener(
    214    uint64_t aInnerWindowID, nsIEventSourceEventListener* aListener) {
    215  MOZ_ASSERT(NS_IsMainThread());
    216 
    217  if (!aListener) {
    218    return NS_ERROR_FAILURE;
    219  }
    220 
    221  WindowListener* listener = mWindows.Get(aInnerWindowID);
    222  if (!listener) {
    223    return NS_ERROR_FAILURE;
    224  }
    225 
    226  if (!listener->mListeners.RemoveElement(aListener)) {
    227    return NS_ERROR_FAILURE;
    228  }
    229 
    230  // The last listener for this window.
    231  if (listener->mListeners.IsEmpty()) {
    232    mWindows.Remove(aInnerWindowID);
    233  }
    234 
    235  MOZ_ASSERT(mCountListeners);
    236  --mCountListeners;
    237 
    238  return NS_OK;
    239 }
    240 
    241 NS_IMETHODIMP
    242 EventSourceEventService::HasListenerFor(uint64_t aInnerWindowID,
    243                                        bool* aResult) {
    244  MOZ_ASSERT(NS_IsMainThread());
    245 
    246  *aResult = mWindows.Get(aInnerWindowID);
    247 
    248  return NS_OK;
    249 }
    250 
    251 NS_IMETHODIMP
    252 EventSourceEventService::Observe(nsISupports* aSubject, const char* aTopic,
    253                                 const char16_t* aData) {
    254  MOZ_ASSERT(NS_IsMainThread());
    255 
    256  if (!strcmp(aTopic, "xpcom-shutdown")) {
    257    Shutdown();
    258    return NS_OK;
    259  }
    260 
    261  if (!strcmp(aTopic, "inner-window-destroyed") && HasListeners()) {
    262    nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
    263    NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
    264 
    265    uint64_t innerID;
    266    nsresult rv = wrapper->GetData(&innerID);
    267    NS_ENSURE_SUCCESS(rv, rv);
    268 
    269    WindowListener* listener = mWindows.Get(innerID);
    270    if (!listener) {
    271      return NS_OK;
    272    }
    273 
    274    MOZ_ASSERT(mCountListeners >= listener->mListeners.Length());
    275    mCountListeners -= listener->mListeners.Length();
    276    mWindows.Remove(innerID);
    277  }
    278 
    279  // This should not happen.
    280  return NS_ERROR_FAILURE;
    281 }
    282 
    283 void EventSourceEventService::Shutdown() {
    284  MOZ_ASSERT(NS_IsMainThread());
    285 
    286  if (gEventSourceEventService) {
    287    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
    288    if (obs) {
    289      obs->RemoveObserver(gEventSourceEventService, "xpcom-shutdown");
    290      obs->RemoveObserver(gEventSourceEventService, "inner-window-destroyed");
    291    }
    292 
    293    mWindows.Clear();
    294    gEventSourceEventService = nullptr;
    295  }
    296 }
    297 
    298 bool EventSourceEventService::HasListeners() const { return !!mCountListeners; }
    299 
    300 void EventSourceEventService::GetListeners(
    301    uint64_t aInnerWindowID,
    302    EventSourceEventService::EventSourceListeners& aListeners) const {
    303  aListeners.Clear();
    304 
    305  WindowListener* listener = mWindows.Get(aInnerWindowID);
    306  if (!listener) {
    307    return;
    308  }
    309 
    310  aListeners.AppendElements(listener->mListeners);
    311 }
    312 
    313 }  // namespace mozilla::dom