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