tor-browser

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

WebTransportEventService.cpp (8333B)


      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 "WebTransportEventService.h"
      8 #include "nsThreadUtils.h"
      9 #include "nsIObserverService.h"
     10 #include "mozilla/StaticPtr.h"
     11 #include "mozilla/Services.h"
     12 #include "nsISupportsPrimitives.h"
     13 #include "mozilla/StaticMutex.h"
     14 
     15 namespace mozilla {
     16 namespace net {
     17 
     18 namespace {
     19 
     20 StaticRefPtr<WebTransportEventService> gWebTransportEventService;
     21 static StaticMutex sLock;
     22 
     23 }  // anonymous namespace
     24 
     25 class WebTransportBaseRunnable : public Runnable {
     26 public:
     27  WebTransportBaseRunnable(uint64_t aInnerWindowID, uint64_t aHttpChannelId)
     28      : Runnable("net::WebTransportBaseRunnable"),
     29        mInnerWindowID(aInnerWindowID),
     30        mHttpChannelId(aHttpChannelId),
     31        mService(WebTransportEventService::GetOrCreate()) {}
     32 
     33  NS_IMETHOD Run() override {
     34    MOZ_ASSERT(NS_IsMainThread());
     35    MOZ_ASSERT(mService);
     36 
     37    WebTransportEventService::WebTransportEventListeners listeners;
     38    mService->GetListeners(mInnerWindowID, listeners);
     39 
     40    for (uint32_t i = 0; i < listeners.Length(); ++i) {
     41      DoWork(listeners[i]);
     42    }
     43 
     44    return NS_OK;
     45  }
     46 
     47 protected:
     48  ~WebTransportBaseRunnable() = default;
     49 
     50  virtual void DoWork(nsIWebTransportEventListener* aListener) = 0;
     51 
     52  uint64_t mInnerWindowID;
     53  uint64_t mHttpChannelId;
     54  RefPtr<WebTransportEventService> mService;
     55 };
     56 
     57 class WebTransportSessionCreatedRunnable final
     58    : public WebTransportBaseRunnable {
     59 public:
     60  WebTransportSessionCreatedRunnable(uint64_t aInnerWindowID,
     61                                     uint64_t aHttpChannelId)
     62      : WebTransportBaseRunnable(aInnerWindowID, aHttpChannelId) {}
     63 
     64 private:
     65  virtual void DoWork(nsIWebTransportEventListener* aListener) override {
     66    DebugOnly<nsresult> rv =
     67        aListener->WebTransportSessionCreated(mHttpChannelId);
     68    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
     69                         "WebTransport Session Created failed");
     70  }
     71 };
     72 
     73 class WebTransportSessionClosedRunnable final
     74    : public WebTransportBaseRunnable {
     75 public:
     76  WebTransportSessionClosedRunnable(uint64_t aInnerWindowID,
     77                                    uint64_t aHttpChannelId, uint32_t aCode,
     78                                    const nsAString& aReason)
     79      : WebTransportBaseRunnable(aInnerWindowID, aHttpChannelId),
     80        mCode(aCode),
     81        mReason(aReason) {}
     82 
     83 private:
     84  virtual void DoWork(nsIWebTransportEventListener* aListener) override {
     85    DebugOnly<nsresult> rv =
     86        aListener->WebTransportSessionClosed(mHttpChannelId, mCode, mReason);
     87    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
     88                         "WebTransport Session Closed failed");
     89  }
     90 
     91  uint32_t mCode;
     92  const nsString mReason;
     93 };
     94 
     95 /* static */
     96 already_AddRefed<WebTransportEventService>
     97 WebTransportEventService::GetOrCreate() {
     98  StaticMutexAutoLock lock(sLock);
     99 
    100  if (!gWebTransportEventService) {
    101    gWebTransportEventService = new WebTransportEventService();
    102  }
    103 
    104  RefPtr<WebTransportEventService> service = gWebTransportEventService.get();
    105  return service.forget();
    106 }
    107 
    108 NS_INTERFACE_MAP_BEGIN(WebTransportEventService)
    109  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebTransportEventService)
    110  NS_INTERFACE_MAP_ENTRY(nsIObserver)
    111  NS_INTERFACE_MAP_ENTRY(nsIWebTransportEventService)
    112 NS_INTERFACE_MAP_END
    113 
    114 NS_IMPL_ADDREF(WebTransportEventService)
    115 NS_IMPL_RELEASE(WebTransportEventService)
    116 
    117 WebTransportEventService::WebTransportEventService() : mCountListeners(0) {
    118  if (NS_IsMainThread()) {
    119    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
    120    if (obs) {
    121      obs->AddObserver(this, "xpcom-shutdown", false);
    122      obs->AddObserver(this, "inner-window-destroyed", false);
    123    }
    124  } else {
    125    gWebTransportEventService = nullptr;
    126  }
    127 }
    128 
    129 void WebTransportEventService::WebTransportSessionCreated(
    130    uint64_t aInnerWindowID, uint64_t aHttpChannelId) {
    131  // Let's continue only if we have some listeners.
    132  if (!HasListeners()) {
    133    return;
    134  }
    135 
    136  RefPtr<WebTransportSessionCreatedRunnable> runnable =
    137      new WebTransportSessionCreatedRunnable(aInnerWindowID, aHttpChannelId);
    138  DebugOnly<nsresult> rv =
    139      NS_IsMainThread() ? runnable->Run() : NS_DispatchToMainThread(runnable);
    140  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Dispatch failed");
    141 }
    142 
    143 void WebTransportEventService::WebTransportSessionClosed(
    144    uint64_t aInnerWindowID, uint64_t aHttpChannelId, uint32_t aCode,
    145    const nsAString& aReason) {
    146  // Let's continue only if we have some listeners.
    147  if (!HasListeners()) {
    148    return;
    149  }
    150 
    151  RefPtr<WebTransportSessionClosedRunnable> runnable =
    152      new WebTransportSessionClosedRunnable(aInnerWindowID, aHttpChannelId,
    153                                            aCode, aReason);
    154  DebugOnly<nsresult> rv =
    155      NS_IsMainThread() ? runnable->Run() : NS_DispatchToMainThread(runnable);
    156  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Dispatch failed");
    157 }
    158 
    159 WebTransportEventService::~WebTransportEventService() {
    160  MOZ_ASSERT(NS_IsMainThread());
    161 }
    162 
    163 NS_IMETHODIMP
    164 WebTransportEventService::AddListener(uint64_t aInnerWindowID,
    165                                      nsIWebTransportEventListener* aListener) {
    166  MOZ_ASSERT(NS_IsMainThread());
    167 
    168  if (!aListener) {
    169    return NS_ERROR_INVALID_ARG;
    170  }
    171 
    172  ++mCountListeners;
    173 
    174  WindowListener* listener = mWindows.GetOrInsertNew(aInnerWindowID);
    175 
    176  listener->mListeners.AppendElement(aListener);
    177 
    178  return NS_OK;
    179 }
    180 
    181 NS_IMETHODIMP
    182 WebTransportEventService::RemoveListener(
    183    uint64_t aInnerWindowID, nsIWebTransportEventListener* aListener) {
    184  MOZ_ASSERT(NS_IsMainThread());
    185 
    186  if (!aListener) {
    187    return NS_ERROR_INVALID_ARG;
    188  }
    189 
    190  WindowListener* listener = mWindows.Get(aInnerWindowID);
    191  if (!listener) {
    192    return NS_ERROR_FAILURE;
    193  }
    194 
    195  if (!listener->mListeners.RemoveElement(aListener)) {
    196    return NS_ERROR_FAILURE;
    197  }
    198 
    199  // The last listener for this window.
    200  if (listener->mListeners.IsEmpty()) {
    201    mWindows.Remove(aInnerWindowID);
    202  }
    203 
    204  MOZ_ASSERT(mCountListeners);
    205  --mCountListeners;
    206 
    207  return NS_OK;
    208 }
    209 
    210 NS_IMETHODIMP
    211 WebTransportEventService::HasListenerFor(uint64_t aInnerWindowID,
    212                                         bool* aResult) {
    213  MOZ_ASSERT(NS_IsMainThread());
    214 
    215  *aResult = mWindows.Get(aInnerWindowID);
    216 
    217  return NS_OK;
    218 }
    219 
    220 NS_IMETHODIMP
    221 WebTransportEventService::Observe(nsISupports* aSubject, const char* aTopic,
    222                                  const char16_t* aData) {
    223  MOZ_ASSERT(NS_IsMainThread());
    224 
    225  if (!strcmp(aTopic, "xpcom-shutdown")) {
    226    Shutdown();
    227    return NS_OK;
    228  }
    229 
    230  if (!strcmp(aTopic, "inner-window-destroyed") && HasListeners()) {
    231    nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
    232    NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
    233 
    234    uint64_t innerWindowID;
    235    nsresult rv = wrapper->GetData(&innerWindowID);
    236    NS_ENSURE_SUCCESS(rv, rv);
    237 
    238    WindowListener* listener = mWindows.Get(innerWindowID);
    239    if (!listener) {
    240      return NS_OK;
    241    }
    242 
    243    MOZ_ASSERT(mCountListeners >= listener->mListeners.Length());
    244    mCountListeners -= listener->mListeners.Length();
    245    mWindows.Remove(innerWindowID);
    246    return NS_OK;
    247  }
    248  return NS_ERROR_FAILURE;
    249 }
    250 
    251 bool WebTransportEventService::HasListeners() const {
    252  return !!mCountListeners;
    253 }
    254 
    255 void WebTransportEventService::GetListeners(
    256    uint64_t aInnerWindowID,
    257    WebTransportEventService::WebTransportEventListeners& aListeners) const {
    258  MOZ_ASSERT(NS_IsMainThread());
    259  aListeners.Clear();
    260 
    261  WindowListener* listener = mWindows.Get(aInnerWindowID);
    262  if (!listener) {
    263    return;
    264  }
    265 
    266  aListeners.AppendElements(listener->mListeners);
    267 }
    268 
    269 void WebTransportEventService::Shutdown() {
    270  MOZ_ASSERT(NS_IsMainThread());
    271  if (gWebTransportEventService) {
    272    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
    273    if (obs) {
    274      obs->RemoveObserver(gWebTransportEventService, "xpcom-shutdown");
    275      obs->RemoveObserver(gWebTransportEventService, "inner-window-destroyed");
    276    }
    277 
    278    mWindows.Clear();
    279    StaticMutexAutoLock lock(sLock);
    280    gWebTransportEventService = nullptr;
    281  }
    282 }
    283 
    284 }  // namespace net
    285 }  // namespace mozilla