WebSocketEventService.cpp (18738B)
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 "WebSocketEventListenerChild.h" 8 #include "WebSocketEventService.h" 9 #include "WebSocketFrame.h" 10 11 #include "mozilla/net/NeckoChild.h" 12 #include "mozilla/StaticPtr.h" 13 #include "nsISupportsPrimitives.h" 14 #include "nsIObserverService.h" 15 #include "nsXULAppAPI.h" 16 #include "nsSocketTransportService2.h" 17 #include "nsThreadUtils.h" 18 #include "mozilla/Services.h" 19 #include "nsIWebSocketImpl.h" 20 21 namespace mozilla { 22 namespace net { 23 24 namespace { 25 26 StaticRefPtr<WebSocketEventService> gWebSocketEventService; 27 28 bool IsChildProcess() { 29 return XRE_GetProcessType() != GeckoProcessType_Default; 30 } 31 32 } // anonymous namespace 33 34 class WebSocketBaseRunnable : public Runnable { 35 public: 36 WebSocketBaseRunnable(uint32_t aWebSocketSerialID, uint64_t aInnerWindowID) 37 : Runnable("net::WebSocketBaseRunnable"), 38 mWebSocketSerialID(aWebSocketSerialID), 39 mInnerWindowID(aInnerWindowID) {} 40 41 NS_IMETHOD Run() override { 42 MOZ_ASSERT(NS_IsMainThread()); 43 44 RefPtr<WebSocketEventService> service = 45 WebSocketEventService::GetOrCreate(); 46 MOZ_ASSERT(service); 47 48 WebSocketEventService::WindowListeners listeners; 49 service->GetListeners(mInnerWindowID, listeners); 50 51 for (uint32_t i = 0; i < listeners.Length(); ++i) { 52 DoWork(listeners[i]); 53 } 54 55 return NS_OK; 56 } 57 58 protected: 59 ~WebSocketBaseRunnable() = default; 60 61 virtual void DoWork(nsIWebSocketEventListener* aListener) = 0; 62 63 uint32_t mWebSocketSerialID; 64 uint64_t mInnerWindowID; 65 }; 66 67 class WebSocketFrameRunnable final : public WebSocketBaseRunnable { 68 public: 69 WebSocketFrameRunnable(uint32_t aWebSocketSerialID, uint64_t aInnerWindowID, 70 already_AddRefed<WebSocketFrame> aFrame, 71 bool aFrameSent) 72 : WebSocketBaseRunnable(aWebSocketSerialID, aInnerWindowID), 73 mFrame(std::move(aFrame)), 74 mFrameSent(aFrameSent) {} 75 76 private: 77 virtual void DoWork(nsIWebSocketEventListener* aListener) override { 78 DebugOnly<nsresult> rv{}; 79 if (mFrameSent) { 80 rv = aListener->FrameSent(mWebSocketSerialID, mFrame); 81 } else { 82 rv = aListener->FrameReceived(mWebSocketSerialID, mFrame); 83 } 84 85 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Frame op failed"); 86 } 87 88 RefPtr<WebSocketFrame> mFrame; 89 bool mFrameSent; 90 }; 91 92 class WebSocketCreatedRunnable final : public WebSocketBaseRunnable { 93 public: 94 WebSocketCreatedRunnable(uint32_t aWebSocketSerialID, uint64_t aInnerWindowID, 95 const nsAString& aURI, const nsACString& aProtocols) 96 : WebSocketBaseRunnable(aWebSocketSerialID, aInnerWindowID), 97 mURI(aURI), 98 mProtocols(aProtocols) {} 99 100 private: 101 virtual void DoWork(nsIWebSocketEventListener* aListener) override { 102 DebugOnly<nsresult> rv = 103 aListener->WebSocketCreated(mWebSocketSerialID, mURI, mProtocols); 104 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WebSocketCreated failed"); 105 } 106 107 const nsString mURI; 108 const nsCString mProtocols; 109 }; 110 111 class WebSocketOpenedRunnable final : public WebSocketBaseRunnable { 112 public: 113 WebSocketOpenedRunnable(uint32_t aWebSocketSerialID, uint64_t aInnerWindowID, 114 const nsAString& aEffectiveURI, 115 const nsACString& aProtocols, 116 const nsACString& aExtensions, 117 uint64_t aHttpChannelId) 118 : WebSocketBaseRunnable(aWebSocketSerialID, aInnerWindowID), 119 mEffectiveURI(aEffectiveURI), 120 mProtocols(aProtocols), 121 mExtensions(aExtensions), 122 mHttpChannelId(aHttpChannelId) {} 123 124 private: 125 virtual void DoWork(nsIWebSocketEventListener* aListener) override { 126 DebugOnly<nsresult> rv = 127 aListener->WebSocketOpened(mWebSocketSerialID, mEffectiveURI, 128 mProtocols, mExtensions, mHttpChannelId); 129 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WebSocketOpened failed"); 130 } 131 132 const nsString mEffectiveURI; 133 const nsCString mProtocols; 134 const nsCString mExtensions; 135 uint64_t mHttpChannelId; 136 }; 137 138 class WebSocketMessageAvailableRunnable final : public WebSocketBaseRunnable { 139 public: 140 WebSocketMessageAvailableRunnable(uint32_t aWebSocketSerialID, 141 uint64_t aInnerWindowID, 142 const nsACString& aData, 143 uint16_t aMessageType) 144 : WebSocketBaseRunnable(aWebSocketSerialID, aInnerWindowID), 145 mData(aData), 146 mMessageType(aMessageType) {} 147 148 private: 149 virtual void DoWork(nsIWebSocketEventListener* aListener) override { 150 DebugOnly<nsresult> rv = aListener->WebSocketMessageAvailable( 151 mWebSocketSerialID, mData, mMessageType); 152 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WebSocketMessageAvailable failed"); 153 } 154 155 const nsCString mData; 156 uint16_t mMessageType; 157 }; 158 159 class WebSocketClosedRunnable final : public WebSocketBaseRunnable { 160 public: 161 WebSocketClosedRunnable(uint32_t aWebSocketSerialID, uint64_t aInnerWindowID, 162 bool aWasClean, uint16_t aCode, 163 const nsAString& aReason) 164 : WebSocketBaseRunnable(aWebSocketSerialID, aInnerWindowID), 165 mWasClean(aWasClean), 166 mCode(aCode), 167 mReason(aReason) {} 168 169 private: 170 virtual void DoWork(nsIWebSocketEventListener* aListener) override { 171 DebugOnly<nsresult> rv = aListener->WebSocketClosed( 172 mWebSocketSerialID, mWasClean, mCode, mReason); 173 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "WebSocketClosed failed"); 174 } 175 176 bool mWasClean; 177 uint16_t mCode; 178 const nsString mReason; 179 }; 180 181 /* static */ 182 already_AddRefed<WebSocketEventService> WebSocketEventService::Get() { 183 MOZ_ASSERT(NS_IsMainThread()); 184 RefPtr<WebSocketEventService> service = gWebSocketEventService.get(); 185 return service.forget(); 186 } 187 188 /* static */ 189 already_AddRefed<WebSocketEventService> WebSocketEventService::GetOrCreate() { 190 MOZ_ASSERT(NS_IsMainThread()); 191 192 if (!gWebSocketEventService) { 193 gWebSocketEventService = new WebSocketEventService(); 194 } 195 196 RefPtr<WebSocketEventService> service = gWebSocketEventService.get(); 197 return service.forget(); 198 } 199 200 NS_INTERFACE_MAP_BEGIN(WebSocketEventService) 201 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebSocketEventService) 202 NS_INTERFACE_MAP_ENTRY(nsIObserver) 203 NS_INTERFACE_MAP_ENTRY(nsIWebSocketEventService) 204 NS_INTERFACE_MAP_END 205 206 NS_IMPL_ADDREF(WebSocketEventService) 207 NS_IMPL_RELEASE(WebSocketEventService) 208 209 WebSocketEventService::WebSocketEventService() : mCountListeners(0) { 210 MOZ_ASSERT(NS_IsMainThread()); 211 212 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 213 if (obs) { 214 obs->AddObserver(this, "xpcom-shutdown", false); 215 obs->AddObserver(this, "inner-window-destroyed", false); 216 } 217 } 218 219 WebSocketEventService::~WebSocketEventService() { 220 MOZ_ASSERT(NS_IsMainThread()); 221 } 222 223 void WebSocketEventService::WebSocketCreated(uint32_t aWebSocketSerialID, 224 uint64_t aInnerWindowID, 225 const nsAString& aURI, 226 const nsACString& aProtocols, 227 nsIEventTarget* aTarget) { 228 // Let's continue only if we have some listeners. 229 if (!HasListeners()) { 230 return; 231 } 232 233 RefPtr<WebSocketCreatedRunnable> runnable = new WebSocketCreatedRunnable( 234 aWebSocketSerialID, aInnerWindowID, aURI, aProtocols); 235 DebugOnly<nsresult> rv = aTarget 236 ? aTarget->Dispatch(runnable, NS_DISPATCH_NORMAL) 237 : NS_DispatchToMainThread(runnable); 238 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed"); 239 } 240 241 void WebSocketEventService::WebSocketOpened(uint32_t aWebSocketSerialID, 242 uint64_t aInnerWindowID, 243 const nsAString& aEffectiveURI, 244 const nsACString& aProtocols, 245 const nsACString& aExtensions, 246 uint64_t aHttpChannelId, 247 nsIEventTarget* aTarget) { 248 // Let's continue only if we have some listeners. 249 if (!HasListeners()) { 250 return; 251 } 252 253 RefPtr<WebSocketOpenedRunnable> runnable = new WebSocketOpenedRunnable( 254 aWebSocketSerialID, aInnerWindowID, aEffectiveURI, aProtocols, 255 aExtensions, aHttpChannelId); 256 DebugOnly<nsresult> rv = aTarget 257 ? aTarget->Dispatch(runnable, NS_DISPATCH_NORMAL) 258 : NS_DispatchToMainThread(runnable); 259 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed"); 260 } 261 262 void WebSocketEventService::WebSocketMessageAvailable( 263 uint32_t aWebSocketSerialID, uint64_t aInnerWindowID, 264 const nsACString& aData, uint16_t aMessageType, nsIEventTarget* aTarget) { 265 // Let's continue only if we have some listeners. 266 if (!HasListeners()) { 267 return; 268 } 269 270 RefPtr<WebSocketMessageAvailableRunnable> runnable = 271 new WebSocketMessageAvailableRunnable(aWebSocketSerialID, aInnerWindowID, 272 aData, aMessageType); 273 DebugOnly<nsresult> rv = aTarget 274 ? aTarget->Dispatch(runnable, NS_DISPATCH_NORMAL) 275 : NS_DispatchToMainThread(runnable); 276 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed"); 277 } 278 279 void WebSocketEventService::WebSocketClosed(uint32_t aWebSocketSerialID, 280 uint64_t aInnerWindowID, 281 bool aWasClean, uint16_t aCode, 282 const nsAString& aReason, 283 nsIEventTarget* aTarget) { 284 // Let's continue only if we have some listeners. 285 if (!HasListeners()) { 286 return; 287 } 288 289 RefPtr<WebSocketClosedRunnable> runnable = new WebSocketClosedRunnable( 290 aWebSocketSerialID, aInnerWindowID, aWasClean, aCode, aReason); 291 DebugOnly<nsresult> rv = aTarget 292 ? aTarget->Dispatch(runnable, NS_DISPATCH_NORMAL) 293 : NS_DispatchToMainThread(runnable); 294 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed"); 295 } 296 297 void WebSocketEventService::FrameReceived( 298 uint32_t aWebSocketSerialID, uint64_t aInnerWindowID, 299 already_AddRefed<WebSocketFrame> aFrame, nsIEventTarget* aTarget) { 300 RefPtr<WebSocketFrame> frame(std::move(aFrame)); 301 MOZ_ASSERT(frame); 302 303 // Let's continue only if we have some listeners. 304 if (!HasListeners()) { 305 return; 306 } 307 308 RefPtr<WebSocketFrameRunnable> runnable = 309 new WebSocketFrameRunnable(aWebSocketSerialID, aInnerWindowID, 310 frame.forget(), false /* frameSent */); 311 DebugOnly<nsresult> rv = aTarget 312 ? aTarget->Dispatch(runnable, NS_DISPATCH_NORMAL) 313 : NS_DispatchToMainThread(runnable); 314 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed"); 315 } 316 317 void WebSocketEventService::FrameSent(uint32_t aWebSocketSerialID, 318 uint64_t aInnerWindowID, 319 already_AddRefed<WebSocketFrame> aFrame, 320 nsIEventTarget* aTarget) { 321 RefPtr<WebSocketFrame> frame(std::move(aFrame)); 322 MOZ_ASSERT(frame); 323 324 // Let's continue only if we have some listeners. 325 if (!HasListeners()) { 326 return; 327 } 328 329 RefPtr<WebSocketFrameRunnable> runnable = new WebSocketFrameRunnable( 330 aWebSocketSerialID, aInnerWindowID, frame.forget(), true /* frameSent */); 331 332 DebugOnly<nsresult> rv = aTarget 333 ? aTarget->Dispatch(runnable, NS_DISPATCH_NORMAL) 334 : NS_DispatchToMainThread(runnable); 335 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "NS_DispatchToMainThread failed"); 336 } 337 338 void WebSocketEventService::AssociateWebSocketImplWithSerialID( 339 nsIWebSocketImpl* aWebSocketImpl, uint32_t aWebSocketSerialID) { 340 MOZ_ASSERT(NS_IsMainThread()); 341 342 mWebSocketImplMap.InsertOrUpdate(aWebSocketSerialID, 343 do_GetWeakReference(aWebSocketImpl)); 344 } 345 346 NS_IMETHODIMP 347 WebSocketEventService::SendMessage(uint32_t aWebSocketSerialID, 348 const nsAString& aMessage) { 349 MOZ_ASSERT(NS_IsMainThread()); 350 351 nsWeakPtr weakPtr = mWebSocketImplMap.Get(aWebSocketSerialID); 352 nsCOMPtr<nsIWebSocketImpl> webSocketImpl = do_QueryReferent(weakPtr); 353 354 if (!webSocketImpl) { 355 return NS_ERROR_NOT_AVAILABLE; 356 } 357 358 return webSocketImpl->SendMessage(aMessage); 359 } 360 361 NS_IMETHODIMP 362 WebSocketEventService::AddListener(uint64_t aInnerWindowID, 363 nsIWebSocketEventListener* aListener) { 364 MOZ_ASSERT(NS_IsMainThread()); 365 366 if (!aListener) { 367 return NS_ERROR_FAILURE; 368 } 369 370 ++mCountListeners; 371 372 mWindows 373 .LookupOrInsertWith( 374 aInnerWindowID, 375 [&] { 376 auto listener = MakeUnique<WindowListener>(); 377 378 if (IsChildProcess()) { 379 PWebSocketEventListenerChild* actor = 380 gNeckoChild->SendPWebSocketEventListenerConstructor( 381 aInnerWindowID); 382 383 listener->mActor = 384 static_cast<WebSocketEventListenerChild*>(actor); 385 MOZ_ASSERT(listener->mActor); 386 } 387 388 return listener; 389 }) 390 ->mListeners.AppendElement(aListener); 391 392 return NS_OK; 393 } 394 395 NS_IMETHODIMP 396 WebSocketEventService::RemoveListener(uint64_t aInnerWindowID, 397 nsIWebSocketEventListener* aListener) { 398 MOZ_ASSERT(NS_IsMainThread()); 399 400 if (!aListener) { 401 return NS_ERROR_FAILURE; 402 } 403 404 WindowListener* listener = mWindows.Get(aInnerWindowID); 405 if (!listener) { 406 return NS_ERROR_FAILURE; 407 } 408 409 if (!listener->mListeners.RemoveElement(aListener)) { 410 return NS_ERROR_FAILURE; 411 } 412 413 // The last listener for this window. 414 if (listener->mListeners.IsEmpty()) { 415 if (IsChildProcess()) { 416 ShutdownActorListener(listener); 417 } 418 419 mWindows.Remove(aInnerWindowID); 420 } 421 422 MOZ_ASSERT(mCountListeners); 423 --mCountListeners; 424 425 return NS_OK; 426 } 427 428 NS_IMETHODIMP 429 WebSocketEventService::HasListenerFor(uint64_t aInnerWindowID, bool* aResult) { 430 MOZ_ASSERT(NS_IsMainThread()); 431 432 *aResult = mWindows.Get(aInnerWindowID); 433 434 return NS_OK; 435 } 436 437 NS_IMETHODIMP 438 WebSocketEventService::Observe(nsISupports* aSubject, const char* aTopic, 439 const char16_t* aData) { 440 MOZ_ASSERT(NS_IsMainThread()); 441 442 if (!strcmp(aTopic, "xpcom-shutdown")) { 443 Shutdown(); 444 return NS_OK; 445 } 446 447 if (!strcmp(aTopic, "inner-window-destroyed") && HasListeners()) { 448 nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject); 449 NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE); 450 451 uint64_t innerID; 452 nsresult rv = wrapper->GetData(&innerID); 453 NS_ENSURE_SUCCESS(rv, rv); 454 455 WindowListener* listener = mWindows.Get(innerID); 456 if (!listener) { 457 return NS_OK; 458 } 459 460 MOZ_ASSERT(mCountListeners >= listener->mListeners.Length()); 461 mCountListeners -= listener->mListeners.Length(); 462 463 if (IsChildProcess()) { 464 ShutdownActorListener(listener); 465 } 466 467 mWindows.Remove(innerID); 468 } 469 470 // This should not happen. 471 return NS_ERROR_FAILURE; 472 } 473 474 void WebSocketEventService::Shutdown() { 475 MOZ_ASSERT(NS_IsMainThread()); 476 477 if (gWebSocketEventService) { 478 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); 479 if (obs) { 480 obs->RemoveObserver(gWebSocketEventService, "xpcom-shutdown"); 481 obs->RemoveObserver(gWebSocketEventService, "inner-window-destroyed"); 482 } 483 484 mWindows.Clear(); 485 gWebSocketEventService = nullptr; 486 } 487 } 488 489 bool WebSocketEventService::HasListeners() const { return !!mCountListeners; } 490 491 void WebSocketEventService::GetListeners( 492 uint64_t aInnerWindowID, 493 WebSocketEventService::WindowListeners& aListeners) const { 494 aListeners.Clear(); 495 496 WindowListener* listener = mWindows.Get(aInnerWindowID); 497 if (!listener) { 498 return; 499 } 500 501 aListeners.AppendElements(listener->mListeners); 502 } 503 504 void WebSocketEventService::ShutdownActorListener(WindowListener* aListener) { 505 MOZ_ASSERT(aListener); 506 MOZ_ASSERT(aListener->mActor); 507 aListener->mActor->Close(); 508 aListener->mActor = nullptr; 509 } 510 511 already_AddRefed<WebSocketFrame> WebSocketEventService::CreateFrameIfNeeded( 512 bool aFinBit, bool aRsvBit1, bool aRsvBit2, bool aRsvBit3, uint8_t aOpCode, 513 bool aMaskBit, uint32_t aMask, const nsCString& aPayload) { 514 if (!HasListeners()) { 515 return nullptr; 516 } 517 518 return MakeAndAddRef<WebSocketFrame>(aFinBit, aRsvBit1, aRsvBit2, aRsvBit3, 519 aOpCode, aMaskBit, aMask, aPayload); 520 } 521 522 already_AddRefed<WebSocketFrame> WebSocketEventService::CreateFrameIfNeeded( 523 bool aFinBit, bool aRsvBit1, bool aRsvBit2, bool aRsvBit3, uint8_t aOpCode, 524 bool aMaskBit, uint32_t aMask, uint8_t* aPayload, uint32_t aPayloadLength) { 525 if (!HasListeners()) { 526 return nullptr; 527 } 528 529 nsAutoCString payloadStr; 530 if (NS_WARN_IF(!(payloadStr.Assign((const char*)aPayload, aPayloadLength, 531 mozilla::fallible)))) { 532 return nullptr; 533 } 534 535 return MakeAndAddRef<WebSocketFrame>(aFinBit, aRsvBit1, aRsvBit2, aRsvBit3, 536 aOpCode, aMaskBit, aMask, payloadStr); 537 } 538 539 already_AddRefed<WebSocketFrame> WebSocketEventService::CreateFrameIfNeeded( 540 bool aFinBit, bool aRsvBit1, bool aRsvBit2, bool aRsvBit3, uint8_t aOpCode, 541 bool aMaskBit, uint32_t aMask, uint8_t* aPayloadInHdr, 542 uint32_t aPayloadInHdrLength, uint8_t* aPayload, uint32_t aPayloadLength) { 543 if (!HasListeners()) { 544 return nullptr; 545 } 546 547 uint32_t payloadLength = aPayloadLength + aPayloadInHdrLength; 548 549 nsAutoCString payload; 550 if (NS_WARN_IF(!payload.SetLength(payloadLength, fallible))) { 551 return nullptr; 552 } 553 554 char* payloadPtr = payload.BeginWriting(); 555 if (aPayloadInHdrLength) { 556 memcpy(payloadPtr, aPayloadInHdr, aPayloadInHdrLength); 557 } 558 559 memcpy(payloadPtr + aPayloadInHdrLength, aPayload, aPayloadLength); 560 561 return MakeAndAddRef<WebSocketFrame>(aFinBit, aRsvBit1, aRsvBit2, aRsvBit3, 562 aOpCode, aMaskBit, aMask, payload); 563 } 564 565 } // namespace net 566 } // namespace mozilla