EventSource.cpp (65169B)
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 "mozilla/dom/EventSource.h" 8 9 #include "ReferrerInfo.h" 10 #include "mozilla/Components.h" 11 #include "mozilla/DOMEventTargetHelper.h" 12 #include "mozilla/DataMutex.h" 13 #include "mozilla/DebugOnly.h" 14 #include "mozilla/Encoding.h" 15 #include "mozilla/GlobalFreezeObserver.h" 16 #include "mozilla/LoadInfo.h" 17 #include "mozilla/ScopeExit.h" 18 #include "mozilla/StaticPrefs_dom.h" 19 #include "mozilla/Try.h" 20 #include "mozilla/dom/EventSourceBinding.h" 21 #include "mozilla/dom/EventSourceEventService.h" 22 #include "mozilla/dom/MessageEvent.h" 23 #include "mozilla/dom/MessageEventBinding.h" 24 #include "mozilla/dom/ScriptSettings.h" 25 #include "mozilla/dom/WorkerPrivate.h" 26 #include "mozilla/dom/WorkerRef.h" 27 #include "mozilla/dom/WorkerRunnable.h" 28 #include "mozilla/dom/WorkerScope.h" 29 #include "nsComponentManagerUtils.h" 30 #include "nsContentUtils.h" 31 #include "nsError.h" 32 #include "nsGlobalWindowInner.h" 33 #include "nsIAsyncVerifyRedirectCallback.h" 34 #include "nsIAuthPrompt.h" 35 #include "nsIAuthPrompt2.h" 36 #include "nsIConsoleService.h" 37 #include "nsIHttpChannel.h" 38 #include "nsIInputStream.h" 39 #include "nsIInterfaceRequestorUtils.h" 40 #include "nsIPromptFactory.h" 41 #include "nsIScriptError.h" 42 #include "nsIScriptObjectPrincipal.h" 43 #include "nsIStringBundle.h" 44 #include "nsIThreadRetargetableRequest.h" 45 #include "nsIThreadRetargetableStreamListener.h" 46 #include "nsIWindowWatcher.h" 47 #include "nsJSUtils.h" 48 #include "nsMimeTypes.h" 49 #include "nsNetUtil.h" 50 #include "nsPresContext.h" 51 #include "nsProxyRelease.h" 52 #include "nsWrapperCacheInlines.h" 53 #include "xpcpublic.h" 54 55 namespace mozilla::dom { 56 57 #ifdef DEBUG 58 static LazyLogModule gEventSourceLog("EventSource"); 59 #endif 60 61 #define SPACE_CHAR (char16_t)0x0020 62 #define CR_CHAR (char16_t)0x000D 63 #define LF_CHAR (char16_t)0x000A 64 #define COLON_CHAR (char16_t)0x003A 65 66 // Reconnection time related values in milliseconds. The default one is equal 67 // to the default value of the pref dom.serverEvents.defaultReconnectionTime 68 #define MIN_RECONNECTION_TIME_VALUE 500 69 #define DEFAULT_RECONNECTION_TIME_VALUE 5000 70 #define MAX_RECONNECTION_TIME_VALUE \ 71 PR_IntervalToMilliseconds(DELAY_INTERVAL_LIMIT) 72 73 class EventSourceImpl final : public nsIChannelEventSink, 74 public nsIInterfaceRequestor, 75 public nsISerialEventTarget, 76 public nsITimerCallback, 77 public nsINamed, 78 public nsIThreadRetargetableStreamListener, 79 public GlobalTeardownObserver, 80 public GlobalFreezeObserver { 81 public: 82 NS_DECL_THREADSAFE_ISUPPORTS 83 NS_DECL_NSIREQUESTOBSERVER 84 NS_DECL_NSISTREAMLISTENER 85 NS_DECL_NSICHANNELEVENTSINK 86 NS_DECL_NSIINTERFACEREQUESTOR 87 NS_DECL_NSIEVENTTARGET_FULL 88 NS_DECL_NSITIMERCALLBACK 89 NS_DECL_NSINAMED 90 NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER 91 92 EventSourceImpl(EventSource* aEventSource, 93 nsICookieJarSettings* aCookieJarSettings); 94 95 enum { CONNECTING = 0U, OPEN = 1U, CLOSED = 2U }; 96 97 void Close(); 98 99 void Init(nsIGlobalObject* aWindowGlobal, nsIPrincipal* aPrincipal, 100 const nsAString& aURL, ErrorResult& aRv); 101 102 nsresult GetBaseURI(nsIURI** aBaseURI); 103 104 void SetupHttpChannel(); 105 nsresult SetupReferrerInfo(const nsCOMPtr<Document>& aDocument); 106 nsresult InitChannelAndRequestEventSource(bool aEventTargetAccessAllowed); 107 nsresult ResetConnection(); 108 void ResetDecoder(); 109 nsresult SetReconnectionTimeout(); 110 111 void AnnounceConnection(); 112 void DispatchAllMessageEvents(); 113 nsresult RestartConnection(); 114 void ReestablishConnection(); 115 void DispatchFailConnection(); 116 void FailConnection(); 117 118 void DisconnectFromOwner() override { 119 Close(); 120 GlobalTeardownObserver::DisconnectFromOwner(); 121 } 122 void FrozenCallback(nsIGlobalObject* aOwner) override { 123 DebugOnly<nsresult> rv = Freeze(); 124 MOZ_ASSERT(NS_SUCCEEDED(rv), "Freeze() failed"); 125 } 126 void ThawedCallback(nsIGlobalObject* aOwner) override { 127 DebugOnly<nsresult> rv = Thaw(); 128 MOZ_ASSERT(NS_SUCCEEDED(rv), "Thaw() failed"); 129 } 130 131 nsresult Thaw(); 132 nsresult Freeze(); 133 134 nsresult PrintErrorOnConsole(const char* aBundleURI, const char* aError, 135 const nsTArray<nsString>& aFormatStrings); 136 nsresult ConsoleError(); 137 138 static nsresult StreamReaderFunc(nsIInputStream* aInputStream, void* aClosure, 139 const char* aFromRawSegment, 140 uint32_t aToOffset, uint32_t aCount, 141 uint32_t* aWriteCount); 142 void ParseSegment(const char* aBuffer, uint32_t aLength); 143 nsresult SetFieldAndClear(); 144 void ClearFields(); 145 nsresult ResetEvent(); 146 nsresult DispatchCurrentMessageEvent(); 147 nsresult ParseCharacter(char16_t aChr); 148 nsresult CheckHealthOfRequestCallback(nsIRequest* aRequestCallback); 149 nsresult OnRedirectVerifyCallback(nsresult result); 150 nsresult ParseURL(const nsAString& aURL); 151 nsresult AddGlobalObservers(nsIGlobalObject* aGlobal); 152 void RemoveWindowObservers(); 153 154 void CloseInternal(); 155 void CleanupOnMainThread(); 156 157 bool CreateWorkerRef(WorkerPrivate* aWorkerPrivate); 158 void ReleaseWorkerRef(); 159 160 void AssertIsOnTargetThread() const { 161 MOZ_DIAGNOSTIC_ASSERT(IsTargetThread()); 162 } 163 164 bool IsTargetThread() const { return NS_GetCurrentThread() == mTargetThread; } 165 166 uint16_t ReadyState() { 167 auto lock = mSharedData.Lock(); 168 if (lock->mEventSource) { 169 return lock->mEventSource->mReadyState; 170 } 171 // EventSourceImpl keeps EventSource alive. If mEventSource is null, it 172 // means that the EventSource has been closed. 173 return CLOSED; 174 } 175 176 void SetReadyState(uint16_t aReadyState) { 177 auto lock = mSharedData.Lock(); 178 MOZ_ASSERT(lock->mEventSource); 179 MOZ_ASSERT(!mIsShutDown); 180 lock->mEventSource->mReadyState = aReadyState; 181 } 182 183 bool IsClosed() { return ReadyState() == CLOSED; } 184 185 RefPtr<EventSource> GetEventSource() { 186 AssertIsOnTargetThread(); 187 auto lock = mSharedData.Lock(); 188 return lock->mEventSource; 189 } 190 191 /** 192 * A simple state machine used to manage the event-source's line buffer 193 * 194 * PARSE_STATE_OFF -> PARSE_STATE_BEGIN_OF_STREAM 195 * 196 * PARSE_STATE_BEGIN_OF_STREAM -> PARSE_STATE_CR_CHAR | 197 * PARSE_STATE_BEGIN_OF_LINE | 198 * PARSE_STATE_COMMENT | 199 * PARSE_STATE_FIELD_NAME 200 * 201 * PARSE_STATE_CR_CHAR -> PARSE_STATE_CR_CHAR | 202 * PARSE_STATE_COMMENT | 203 * PARSE_STATE_FIELD_NAME | 204 * PARSE_STATE_BEGIN_OF_LINE 205 * 206 * PARSE_STATE_COMMENT -> PARSE_STATE_CR_CHAR | 207 * PARSE_STATE_BEGIN_OF_LINE 208 * 209 * PARSE_STATE_FIELD_NAME -> PARSE_STATE_CR_CHAR | 210 * PARSE_STATE_BEGIN_OF_LINE | 211 * PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE 212 * 213 * PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE -> PARSE_STATE_FIELD_VALUE | 214 * PARSE_STATE_CR_CHAR | 215 * PARSE_STATE_BEGIN_OF_LINE 216 * 217 * PARSE_STATE_FIELD_VALUE -> PARSE_STATE_CR_CHAR | 218 * PARSE_STATE_BEGIN_OF_LINE 219 * 220 * PARSE_STATE_BEGIN_OF_LINE -> PARSE_STATE_CR_CHAR | 221 * PARSE_STATE_COMMENT | 222 * PARSE_STATE_FIELD_NAME | 223 * PARSE_STATE_BEGIN_OF_LINE 224 * 225 * Whenever the parser find an empty line or the end-of-file 226 * it dispatches the stacked event. 227 * 228 */ 229 enum ParserStatus { 230 PARSE_STATE_OFF = 0, 231 PARSE_STATE_BEGIN_OF_STREAM, 232 PARSE_STATE_CR_CHAR, 233 PARSE_STATE_COMMENT, 234 PARSE_STATE_FIELD_NAME, 235 PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE, 236 PARSE_STATE_FIELD_VALUE, 237 PARSE_STATE_IGNORE_FIELD_VALUE, 238 PARSE_STATE_BEGIN_OF_LINE 239 }; 240 241 // Connection related data members. Should only be accessed on main thread. 242 nsCOMPtr<nsIURI> mSrc; 243 uint32_t mReconnectionTime; // in ms 244 nsCOMPtr<nsIPrincipal> mPrincipal; 245 nsString mOrigin; 246 nsCOMPtr<nsITimer> mTimer; 247 nsCOMPtr<nsIHttpChannel> mHttpChannel; 248 249 struct Message { 250 nsString mEventName; 251 // We need to be able to distinguish between different states of id field: 252 // 1) is not given at all 253 // 2) is given but is empty 254 // 3) is given and has a value 255 // We can't check for the 1st state with a simple nsString. 256 Maybe<nsString> mLastEventID; 257 nsString mData; 258 }; 259 260 // Message related data members. May be set / initialized when initializing 261 // EventSourceImpl on target thread but should only be used on target thread. 262 nsString mLastEventID; 263 UniquePtr<Message> mCurrentMessage; 264 nsDeque<Message> mMessagesToDispatch; 265 ParserStatus mStatus; 266 mozilla::UniquePtr<mozilla::Decoder> mUnicodeDecoder; 267 nsString mLastFieldName; 268 nsString mLastFieldValue; 269 270 // EventSourceImpl internal states. 271 // WorkerRef to keep the worker alive. 272 DataMutex<RefPtr<ThreadSafeWorkerRef>> mWorkerRef; 273 274 // Whether the window is frozen. May be set on main thread and read on target 275 // thread. 276 Atomic<bool> mFrozen; 277 // There are some messages are going to be dispatched when thaw. 278 bool mGoingToDispatchAllMessages; 279 // Whether the EventSource is run on main thread. 280 const bool mIsMainThread; 281 // Whether the EventSourceImpl is going to be destroyed. 282 Atomic<bool> mIsShutDown; 283 284 class EventSourceServiceNotifier final { 285 public: 286 EventSourceServiceNotifier(RefPtr<EventSourceImpl>&& aEventSourceImpl, 287 uint64_t aHttpChannelId, uint64_t aInnerWindowID) 288 : mEventSourceImpl(std::move(aEventSourceImpl)), 289 mHttpChannelId(aHttpChannelId), 290 mInnerWindowID(aInnerWindowID), 291 mConnectionOpened(false) { 292 AssertIsOnMainThread(); 293 mService = EventSourceEventService::GetOrCreate(); 294 } 295 296 void ConnectionOpened() { 297 mEventSourceImpl->AssertIsOnTargetThread(); 298 mService->EventSourceConnectionOpened(mHttpChannelId, mInnerWindowID); 299 mConnectionOpened = true; 300 } 301 302 void EventReceived(const nsAString& aEventName, 303 const nsAString& aLastEventID, const nsAString& aData, 304 uint32_t aRetry, DOMHighResTimeStamp aTimeStamp) { 305 mEventSourceImpl->AssertIsOnTargetThread(); 306 mService->EventReceived(mHttpChannelId, mInnerWindowID, aEventName, 307 aLastEventID, aData, aRetry, aTimeStamp); 308 } 309 310 ~EventSourceServiceNotifier() { 311 // It is safe to call this on any thread because 312 // EventSourceConnectionClosed method is thread safe and 313 // NS_ReleaseOnMainThread explicitly releases the service on the main 314 // thread. 315 if (mConnectionOpened) { 316 // We want to notify about connection being closed only if we told 317 // it was ever opened. The check is needed if OnStartRequest is called 318 // on the main thread while close() is called on a worker thread. 319 mService->EventSourceConnectionClosed(mHttpChannelId, mInnerWindowID); 320 } 321 NS_ReleaseOnMainThread("EventSourceServiceNotifier::mService", 322 mService.forget()); 323 } 324 325 private: 326 RefPtr<EventSourceEventService> mService; 327 RefPtr<EventSourceImpl> mEventSourceImpl; 328 uint64_t mHttpChannelId; 329 uint64_t mInnerWindowID; 330 bool mConnectionOpened; 331 }; 332 333 struct SharedData { 334 RefPtr<EventSource> mEventSource; 335 UniquePtr<EventSourceServiceNotifier> mServiceNotifier; 336 }; 337 338 DataMutex<SharedData> mSharedData; 339 340 // Event Source owner information: 341 // - the script file name 342 // - source code line number and column number where the Event Source object 343 // was constructed. 344 // - the ID of the inner window where the script lives. Note that this may not 345 // be the same as the Event Source owner window. 346 // These attributes are used for error reporting. Should only be accessed on 347 // target thread 348 JSCallingLocation mCallingLocation; 349 uint64_t mInnerWindowID; 350 351 private: 352 nsCOMPtr<nsICookieJarSettings> mCookieJarSettings; 353 354 // Pointer to the target thread for checking whether we are 355 // on the target thread. This is intentionally a non-owning 356 // pointer in order not to affect the thread destruction 357 // sequence. This pointer must only be compared for equality 358 // and must not be dereferenced. 359 nsIThread* mTargetThread; 360 361 // prevent bad usage 362 EventSourceImpl(const EventSourceImpl& x) = delete; 363 EventSourceImpl& operator=(const EventSourceImpl& x) = delete; 364 ~EventSourceImpl() { 365 if (IsClosed()) { 366 return; 367 } 368 // If we threw during Init we never called Close 369 SetReadyState(CLOSED); 370 CloseInternal(); 371 } 372 }; 373 374 NS_IMPL_ISUPPORTS(EventSourceImpl, nsIStreamListener, nsIRequestObserver, 375 nsIChannelEventSink, nsIInterfaceRequestor, 376 nsISerialEventTarget, nsIEventTarget, 377 nsIThreadRetargetableStreamListener, nsITimerCallback, 378 nsINamed) 379 380 EventSourceImpl::EventSourceImpl(EventSource* aEventSource, 381 nsICookieJarSettings* aCookieJarSettings) 382 : mReconnectionTime(0), 383 mStatus(PARSE_STATE_OFF), 384 mWorkerRef(nullptr, "EventSourceImpl::mWorkerRef"), 385 mFrozen(false), 386 mGoingToDispatchAllMessages(false), 387 mIsMainThread(NS_IsMainThread()), 388 mIsShutDown(false), 389 mSharedData(SharedData{aEventSource}, "EventSourceImpl::mSharedData"), 390 mInnerWindowID(0), 391 mCookieJarSettings(aCookieJarSettings), 392 mTargetThread(NS_GetCurrentThread()) { 393 MOZ_ASSERT(aEventSource); 394 SetReadyState(CONNECTING); 395 } 396 397 class CleanupRunnable final : public WorkerMainThreadRunnable { 398 public: 399 explicit CleanupRunnable(RefPtr<EventSourceImpl>&& aEventSourceImpl) 400 : WorkerMainThreadRunnable(GetCurrentThreadWorkerPrivate(), 401 "EventSource :: Cleanup"_ns), 402 mESImpl(std::move(aEventSourceImpl)) { 403 MOZ_ASSERT(mESImpl); 404 } 405 406 bool MainThreadRun() override { 407 MOZ_ASSERT(mESImpl); 408 mESImpl->CleanupOnMainThread(); 409 // We want to ensure the shortest possible remaining lifetime 410 // and not depend on the Runnable's destruction. 411 mESImpl = nullptr; 412 return true; 413 } 414 415 protected: 416 RefPtr<EventSourceImpl> mESImpl; 417 }; 418 419 void EventSourceImpl::Close() { 420 if (IsClosed()) { 421 return; 422 } 423 424 SetReadyState(CLOSED); 425 // CloseInternal potentially kills ourself, ensure 426 // to not access any members afterwards. 427 CloseInternal(); 428 } 429 430 void EventSourceImpl::CloseInternal() { 431 AssertIsOnTargetThread(); 432 MOZ_ASSERT(IsClosed()); 433 434 RefPtr<EventSource> myES; 435 { 436 auto lock = mSharedData.Lock(); 437 // We want to ensure to release ourself even if we have 438 // the shutdown case, thus we put aside a pointer 439 // to the EventSource and null it out right now. 440 myES = std::move(lock->mEventSource); 441 lock->mEventSource = nullptr; 442 lock->mServiceNotifier = nullptr; 443 } 444 445 MOZ_ASSERT(!mIsShutDown); 446 if (mIsShutDown) { 447 return; 448 } 449 450 // Invoke CleanupOnMainThread before cleaning any members. It will call 451 // ShutDown, which is supposed to be called before cleaning any members. 452 if (NS_IsMainThread()) { 453 CleanupOnMainThread(); 454 } else { 455 ErrorResult rv; 456 // run CleanupOnMainThread synchronously on main thread since it touches 457 // observers and members only can be accessed on main thread. 458 RefPtr<CleanupRunnable> runnable = new CleanupRunnable(this); 459 runnable->Dispatch(GetCurrentThreadWorkerPrivate(), Killing, rv); 460 MOZ_ASSERT(!rv.Failed()); 461 ReleaseWorkerRef(); 462 } 463 464 while (mMessagesToDispatch.GetSize() != 0) { 465 delete mMessagesToDispatch.PopFront(); 466 } 467 mFrozen = false; 468 ResetDecoder(); 469 mUnicodeDecoder = nullptr; 470 // Release the object on its owner. Don't access to any members 471 // after it. 472 myES->mESImpl = nullptr; 473 } 474 475 void EventSourceImpl::CleanupOnMainThread() { 476 AssertIsOnMainThread(); 477 MOZ_ASSERT(IsClosed()); 478 479 // Call ShutDown before cleaning any members. 480 MOZ_ASSERT(!mIsShutDown); 481 mIsShutDown = true; 482 483 if (mIsMainThread) { 484 RemoveWindowObservers(); 485 } 486 487 if (mTimer) { 488 mTimer->Cancel(); 489 mTimer = nullptr; 490 } 491 492 ResetConnection(); 493 mPrincipal = nullptr; 494 mSrc = nullptr; 495 } 496 497 class ConnectRunnable final : public WorkerMainThreadRunnable { 498 public: 499 explicit ConnectRunnable(WorkerPrivate* aWorkerPrivate, 500 RefPtr<EventSourceImpl> aEventSourceImpl) 501 : WorkerMainThreadRunnable(aWorkerPrivate, "EventSource :: Connect"_ns), 502 mESImpl(std::move(aEventSourceImpl)) { 503 MOZ_ASSERT(aWorkerPrivate); 504 aWorkerPrivate->AssertIsOnWorkerThread(); 505 MOZ_ASSERT(mESImpl); 506 } 507 508 bool MainThreadRun() override { 509 MOZ_ASSERT(mESImpl); 510 // We are allowed to access the event target since this runnable is 511 // synchronized with the thread the event target lives on. 512 mESImpl->InitChannelAndRequestEventSource(true); 513 // We want to ensure the shortest possible remaining lifetime 514 // and not depend on the Runnable's destruction. 515 mESImpl = nullptr; 516 return true; 517 } 518 519 private: 520 RefPtr<EventSourceImpl> mESImpl; 521 }; 522 523 nsresult EventSourceImpl::ParseURL(const nsAString& aURL) { 524 MOZ_ASSERT(!mIsShutDown); 525 // get the src 526 nsCOMPtr<nsIURI> baseURI; 527 nsresult rv = GetBaseURI(getter_AddRefs(baseURI)); 528 NS_ENSURE_SUCCESS(rv, rv); 529 530 nsCOMPtr<nsIURI> srcURI; 531 nsCOMPtr<Document> doc = 532 mIsMainThread ? GetEventSource()->GetDocumentIfCurrent() : nullptr; 533 if (doc) { 534 rv = NS_NewURI(getter_AddRefs(srcURI), aURL, doc->GetDocumentCharacterSet(), 535 baseURI); 536 } else { 537 rv = NS_NewURI(getter_AddRefs(srcURI), aURL, nullptr, baseURI); 538 } 539 540 NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR); 541 542 nsAutoString origin; 543 rv = nsContentUtils::GetWebExposedOriginSerialization(srcURI, origin); 544 NS_ENSURE_SUCCESS(rv, rv); 545 546 nsAutoCString spec; 547 rv = srcURI->GetSpec(spec); 548 NS_ENSURE_SUCCESS(rv, rv); 549 550 // This assignment doesn't require extra synchronization because this function 551 // is only ever called from EventSourceImpl::Init(), which is either called 552 // directly if mEventSource was created on the main thread, or via a 553 // synchronous runnable if it was created on a worker thread. 554 { 555 // We can't use GetEventSource() here because it would modify the refcount, 556 // and that's not allowed off the owning thread. 557 auto lock = mSharedData.Lock(); 558 lock->mEventSource->mOriginalURL = NS_ConvertUTF8toUTF16(spec); 559 } 560 mSrc = srcURI; 561 mOrigin = origin; 562 return NS_OK; 563 } 564 565 nsresult EventSourceImpl::AddGlobalObservers(nsIGlobalObject* aGlobal) { 566 AssertIsOnMainThread(); 567 MOZ_ASSERT(mIsMainThread); 568 MOZ_ASSERT(!mIsShutDown); 569 570 GlobalTeardownObserver::BindToOwner(aGlobal); 571 GlobalFreezeObserver::BindToOwner(aGlobal); 572 573 return NS_OK; 574 } 575 576 void EventSourceImpl::RemoveWindowObservers() { 577 AssertIsOnMainThread(); 578 MOZ_ASSERT(mIsMainThread); 579 MOZ_ASSERT(IsClosed()); 580 GlobalTeardownObserver::DisconnectFromOwner(); 581 DisconnectFreezeObserver(); 582 } 583 584 void EventSourceImpl::Init(nsIGlobalObject* aWindowGlobal, 585 nsIPrincipal* aPrincipal, const nsAString& aURL, 586 ErrorResult& aRv) { 587 // aWindowGlobal should only exist for main-thread EventSource 588 MOZ_ASSERT_IF(aWindowGlobal, mIsMainThread); 589 MOZ_ASSERT(aPrincipal); 590 MOZ_ASSERT(ReadyState() == CONNECTING); 591 mPrincipal = aPrincipal; 592 aRv = ParseURL(aURL); 593 if (NS_WARN_IF(aRv.Failed())) { 594 return; 595 } 596 // The conditional here is historical and not necessarily sane. 597 if (JSContext* cx = nsContentUtils::GetCurrentJSContext()) { 598 mCallingLocation = JSCallingLocation::Get(); 599 if (mIsMainThread) { 600 mInnerWindowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx); 601 } 602 } 603 604 if (mIsMainThread) { 605 // We observe when the window freezes and thaws. 606 // 607 // This will store raw pointer of us in GTO, which sounds scary as the 608 // object can be accessed cross thread. But it should be safe as GTO and 609 // EventSourceImpl lifetime should be managed in the same main thread. 610 // 611 // XXX(krosylight): But what about workers? See bug 1910585. 612 aRv = AddGlobalObservers(aWindowGlobal); 613 if (NS_WARN_IF(aRv.Failed())) { 614 return; 615 } 616 } 617 618 mReconnectionTime = StaticPrefs::dom_serverEvents_defaultReconnectionTime(); 619 if (!mReconnectionTime) { 620 mReconnectionTime = DEFAULT_RECONNECTION_TIME_VALUE; 621 } 622 623 mUnicodeDecoder = UTF_8_ENCODING->NewDecoderWithBOMRemoval(); 624 } 625 626 //----------------------------------------------------------------------------- 627 // EventSourceImpl::nsIStreamListener 628 //----------------------------------------------------------------------------- 629 630 NS_IMETHODIMP 631 EventSourceImpl::OnStartRequest(nsIRequest* aRequest) { 632 AssertIsOnMainThread(); 633 if (IsClosed()) { 634 return NS_ERROR_ABORT; 635 } 636 nsresult rv = CheckHealthOfRequestCallback(aRequest); 637 NS_ENSURE_SUCCESS(rv, rv); 638 639 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest, &rv); 640 NS_ENSURE_SUCCESS(rv, rv); 641 642 nsresult status; 643 rv = aRequest->GetStatus(&status); 644 NS_ENSURE_SUCCESS(rv, rv); 645 646 if (NS_FAILED(status)) { 647 // EventSource::OnStopRequest will evaluate if it shall either reestablish 648 // or fail the connection, based on the status. 649 return status; 650 } 651 652 uint32_t httpStatus; 653 rv = httpChannel->GetResponseStatus(&httpStatus); 654 NS_ENSURE_SUCCESS(rv, rv); 655 656 if (httpStatus != 200) { 657 DispatchFailConnection(); 658 return NS_ERROR_ABORT; 659 } 660 661 nsAutoCString contentType; 662 rv = httpChannel->GetContentType(contentType); 663 NS_ENSURE_SUCCESS(rv, rv); 664 665 if (!contentType.EqualsLiteral(TEXT_EVENT_STREAM)) { 666 DispatchFailConnection(); 667 return NS_ERROR_ABORT; 668 } 669 670 if (!mIsMainThread) { 671 // Try to retarget to worker thread, otherwise fall back to main thread. 672 nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(httpChannel); 673 if (rr) { 674 rv = rr->RetargetDeliveryTo(this); 675 if (NS_WARN_IF(NS_FAILED(rv))) { 676 NS_WARNING("Retargeting failed"); 677 } 678 } 679 } 680 681 { 682 auto lock = mSharedData.Lock(); 683 lock->mServiceNotifier = MakeUnique<EventSourceServiceNotifier>( 684 this, mHttpChannel->ChannelId(), mInnerWindowID); 685 } 686 rv = Dispatch(NewRunnableMethod("dom::EventSourceImpl::AnnounceConnection", 687 this, &EventSourceImpl::AnnounceConnection), 688 NS_DISPATCH_NORMAL); 689 NS_ENSURE_SUCCESS(rv, rv); 690 mStatus = PARSE_STATE_BEGIN_OF_STREAM; 691 return NS_OK; 692 } 693 694 // this method parses the characters as they become available instead of 695 // buffering them. 696 nsresult EventSourceImpl::StreamReaderFunc(nsIInputStream* aInputStream, 697 void* aClosure, 698 const char* aFromRawSegment, 699 uint32_t aToOffset, uint32_t aCount, 700 uint32_t* aWriteCount) { 701 // The EventSourceImpl instance is hold alive on the 702 // synchronously calling stack, so raw pointer is fine here. 703 EventSourceImpl* thisObject = static_cast<EventSourceImpl*>(aClosure); 704 if (!thisObject || !aWriteCount) { 705 NS_WARNING( 706 "EventSource cannot read from stream: no aClosure or aWriteCount"); 707 return NS_ERROR_FAILURE; 708 } 709 thisObject->AssertIsOnTargetThread(); 710 MOZ_ASSERT(!thisObject->mIsShutDown); 711 thisObject->ParseSegment((const char*)aFromRawSegment, aCount); 712 *aWriteCount = aCount; 713 return NS_OK; 714 } 715 716 void EventSourceImpl::ParseSegment(const char* aBuffer, uint32_t aLength) { 717 AssertIsOnTargetThread(); 718 if (IsClosed()) { 719 return; 720 } 721 char16_t buffer[1024]; 722 auto dst = Span(buffer); 723 auto src = AsBytes(Span(aBuffer, aLength)); 724 // XXX EOF handling is https://bugzilla.mozilla.org/show_bug.cgi?id=1369018 725 for (;;) { 726 uint32_t result; 727 size_t read; 728 size_t written; 729 std::tie(result, read, written, std::ignore) = 730 mUnicodeDecoder->DecodeToUTF16(src, dst, false); 731 for (auto c : dst.To(written)) { 732 nsresult rv = ParseCharacter(c); 733 NS_ENSURE_SUCCESS_VOID(rv); 734 } 735 if (result == kInputEmpty) { 736 return; 737 } 738 src = src.From(read); 739 } 740 } 741 742 NS_IMETHODIMP 743 EventSourceImpl::OnDataAvailable(nsIRequest* aRequest, 744 nsIInputStream* aInputStream, uint64_t aOffset, 745 uint32_t aCount) { 746 AssertIsOnTargetThread(); 747 NS_ENSURE_ARG_POINTER(aInputStream); 748 if (IsClosed()) { 749 return NS_ERROR_ABORT; 750 } 751 752 nsresult rv = CheckHealthOfRequestCallback(aRequest); 753 NS_ENSURE_SUCCESS(rv, rv); 754 755 uint32_t totalRead; 756 return aInputStream->ReadSegments(EventSourceImpl::StreamReaderFunc, this, 757 aCount, &totalRead); 758 } 759 760 NS_IMETHODIMP 761 EventSourceImpl::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) { 762 AssertIsOnMainThread(); 763 764 if (IsClosed()) { 765 return NS_ERROR_ABORT; 766 } 767 MOZ_ASSERT(mSrc); 768 // "Network errors that prevents the connection from being established in the 769 // first place (e.g. DNS errors), must cause the user agent to asynchronously 770 // reestablish the connection. 771 // 772 // (...) the cancelation of the fetch algorithm by the user agent (e.g. in 773 // response to window.stop() or the user canceling the network connection 774 // manually) must cause the user agent to fail the connection. 775 // There could be additional network errors that are not covered in the above 776 // checks 777 // See Bug 1808511 778 if (NS_FAILED(aStatusCode) && aStatusCode != NS_ERROR_CONNECTION_REFUSED && 779 aStatusCode != NS_ERROR_NET_TIMEOUT && 780 aStatusCode != NS_ERROR_NET_RESET && 781 aStatusCode != NS_ERROR_NET_INTERRUPT && 782 aStatusCode != NS_ERROR_NET_PARTIAL_TRANSFER && 783 aStatusCode != NS_ERROR_NET_TIMEOUT_EXTERNAL && 784 aStatusCode != NS_ERROR_PROXY_CONNECTION_REFUSED && 785 aStatusCode != NS_ERROR_DNS_LOOKUP_QUEUE_FULL && 786 aStatusCode != NS_ERROR_INVALID_CONTENT_ENCODING) { 787 DispatchFailConnection(); 788 return NS_ERROR_ABORT; 789 } 790 791 nsresult rv = CheckHealthOfRequestCallback(aRequest); 792 NS_ENSURE_SUCCESS(rv, rv); 793 794 rv = 795 Dispatch(NewRunnableMethod("dom::EventSourceImpl::ReestablishConnection", 796 this, &EventSourceImpl::ReestablishConnection), 797 NS_DISPATCH_NORMAL); 798 NS_ENSURE_SUCCESS(rv, rv); 799 800 return NS_OK; 801 } 802 803 //----------------------------------------------------------------------------- 804 // EventSourceImpl::nsIChannelEventSink 805 //----------------------------------------------------------------------------- 806 807 NS_IMETHODIMP 808 EventSourceImpl::AsyncOnChannelRedirect( 809 nsIChannel* aOldChannel, nsIChannel* aNewChannel, uint32_t aFlags, 810 nsIAsyncVerifyRedirectCallback* aCallback) { 811 AssertIsOnMainThread(); 812 if (IsClosed()) { 813 return NS_ERROR_ABORT; 814 } 815 nsCOMPtr<nsIRequest> aOldRequest = aOldChannel; 816 MOZ_ASSERT(aOldRequest, "Redirect from a null request?"); 817 818 nsresult rv = CheckHealthOfRequestCallback(aOldRequest); 819 NS_ENSURE_SUCCESS(rv, rv); 820 821 MOZ_ASSERT(aNewChannel, "Redirect without a channel?"); 822 823 nsCOMPtr<nsIURI> newURI; 824 rv = NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI)); 825 NS_ENSURE_SUCCESS(rv, rv); 826 827 bool isValidScheme = net::SchemeIsHttpOrHttps(newURI); 828 829 rv = 830 mIsMainThread ? GetEventSource()->CheckCurrentGlobalCorrectness() : NS_OK; 831 if (NS_FAILED(rv) || !isValidScheme) { 832 DispatchFailConnection(); 833 return NS_ERROR_DOM_SECURITY_ERR; 834 } 835 836 // update our channel 837 838 mHttpChannel = do_QueryInterface(aNewChannel); 839 NS_ENSURE_STATE(mHttpChannel); 840 841 SetupHttpChannel(); 842 // The HTTP impl already copies over the referrer info on 843 // redirects, so we don't need to SetupReferrerInfo(). 844 845 if ((aFlags & nsIChannelEventSink::REDIRECT_PERMANENT) != 0) { 846 rv = NS_GetFinalChannelURI(mHttpChannel, getter_AddRefs(mSrc)); 847 NS_ENSURE_SUCCESS(rv, rv); 848 } 849 850 aCallback->OnRedirectVerifyCallback(NS_OK); 851 852 return NS_OK; 853 } 854 855 //----------------------------------------------------------------------------- 856 // EventSourceImpl::nsIInterfaceRequestor 857 //----------------------------------------------------------------------------- 858 859 NS_IMETHODIMP 860 EventSourceImpl::GetInterface(const nsIID& aIID, void** aResult) { 861 AssertIsOnMainThread(); 862 863 if (IsClosed()) { 864 return NS_ERROR_FAILURE; 865 } 866 867 if (aIID.Equals(NS_GET_IID(nsIChannelEventSink))) { 868 *aResult = static_cast<nsIChannelEventSink*>(this); 869 NS_ADDREF_THIS(); 870 return NS_OK; 871 } 872 873 if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) || 874 aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) { 875 nsresult rv; 876 nsCOMPtr<nsIPromptFactory> wwatch = 877 do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv); 878 NS_ENSURE_SUCCESS(rv, rv); 879 880 nsCOMPtr<nsPIDOMWindowOuter> window; 881 882 // To avoid a data race we may only access the event target if it lives on 883 // the main thread. 884 if (mIsMainThread) { 885 auto lock = mSharedData.Lock(); 886 rv = lock->mEventSource->CheckCurrentGlobalCorrectness(); 887 NS_ENSURE_SUCCESS(rv, NS_ERROR_UNEXPECTED); 888 889 if (nsGlobalWindowInner* win = lock->mEventSource->GetOwnerWindow()) { 890 window = win->GetOuterWindow(); 891 } 892 } 893 894 // Get the an auth prompter for our window so that the parenting 895 // of the dialogs works as it should when using tabs. 896 897 return wwatch->GetPrompt(window, aIID, aResult); 898 } 899 900 return QueryInterface(aIID, aResult); 901 } 902 903 NS_IMETHODIMP 904 EventSourceImpl::IsOnCurrentThread(bool* aResult) { 905 *aResult = IsTargetThread(); 906 return NS_OK; 907 } 908 909 NS_IMETHODIMP_(bool) 910 EventSourceImpl::IsOnCurrentThreadInfallible() { return IsTargetThread(); } 911 912 nsresult EventSourceImpl::GetBaseURI(nsIURI** aBaseURI) { 913 MOZ_ASSERT(!mIsShutDown); 914 NS_ENSURE_ARG_POINTER(aBaseURI); 915 916 *aBaseURI = nullptr; 917 918 nsCOMPtr<nsIURI> baseURI; 919 920 // first we try from document->GetBaseURI() 921 nsCOMPtr<Document> doc = 922 mIsMainThread ? GetEventSource()->GetDocumentIfCurrent() : nullptr; 923 if (doc) { 924 baseURI = doc->GetBaseURI(); 925 } 926 927 // otherwise we get from the doc's principal 928 if (!baseURI) { 929 auto* basePrin = BasePrincipal::Cast(mPrincipal); 930 nsresult rv = basePrin->GetURI(getter_AddRefs(baseURI)); 931 NS_ENSURE_SUCCESS(rv, rv); 932 } 933 934 NS_ENSURE_STATE(baseURI); 935 936 baseURI.forget(aBaseURI); 937 return NS_OK; 938 } 939 940 void EventSourceImpl::SetupHttpChannel() { 941 AssertIsOnMainThread(); 942 MOZ_ASSERT(!mIsShutDown); 943 nsresult rv = mHttpChannel->SetRequestMethod("GET"_ns); 944 MOZ_ASSERT(NS_SUCCEEDED(rv)); 945 946 /* set the http request headers */ 947 948 rv = mHttpChannel->SetRequestHeader( 949 "Accept"_ns, nsLiteralCString(TEXT_EVENT_STREAM), false); 950 MOZ_ASSERT(NS_SUCCEEDED(rv)); 951 952 // LOAD_BYPASS_CACHE already adds the Cache-Control: no-cache header 953 954 if (mLastEventID.IsEmpty()) { 955 return; 956 } 957 NS_ConvertUTF16toUTF8 eventId(mLastEventID); 958 rv = mHttpChannel->SetRequestHeader("Last-Event-ID"_ns, eventId, false); 959 #ifdef DEBUG 960 if (NS_FAILED(rv)) { 961 MOZ_LOG(gEventSourceLog, LogLevel::Warning, 962 ("SetupHttpChannel. rv=%x (%s)", uint32_t(rv), eventId.get())); 963 } 964 #endif 965 (void)rv; 966 } 967 968 nsresult EventSourceImpl::SetupReferrerInfo( 969 const nsCOMPtr<Document>& aDocument) { 970 AssertIsOnMainThread(); 971 MOZ_ASSERT(!mIsShutDown); 972 973 if (aDocument) { 974 auto referrerInfo = MakeRefPtr<ReferrerInfo>(*aDocument); 975 nsresult rv = mHttpChannel->SetReferrerInfoWithoutClone(referrerInfo); 976 NS_ENSURE_SUCCESS(rv, rv); 977 } 978 979 return NS_OK; 980 } 981 982 nsresult EventSourceImpl::InitChannelAndRequestEventSource( 983 const bool aEventTargetAccessAllowed) { 984 AssertIsOnMainThread(); 985 if (IsClosed()) { 986 return NS_ERROR_ABORT; 987 } 988 989 bool isValidScheme = net::SchemeIsHttpOrHttps(mSrc); 990 991 MOZ_ASSERT_IF(mIsMainThread, aEventTargetAccessAllowed); 992 993 nsresult rv = aEventTargetAccessAllowed ? [this]() { 994 // We can't call GetEventSource() because we're not 995 // allowed to touch the refcount off the worker thread 996 // due to an assertion, event if it would have otherwise 997 // been safe. 998 auto lock = mSharedData.Lock(); 999 return lock->mEventSource->CheckCurrentGlobalCorrectness(); 1000 }() 1001 : NS_OK; 1002 if (NS_FAILED(rv) || !isValidScheme) { 1003 DispatchFailConnection(); 1004 return NS_ERROR_DOM_SECURITY_ERR; 1005 } 1006 1007 nsCOMPtr<Document> doc; 1008 nsSecurityFlags securityFlags = 1009 nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT; 1010 { 1011 auto lock = mSharedData.Lock(); 1012 doc = aEventTargetAccessAllowed ? lock->mEventSource->GetDocumentIfCurrent() 1013 : nullptr; 1014 1015 if (lock->mEventSource->mWithCredentials) { 1016 securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE; 1017 } 1018 } 1019 1020 // The html spec requires we use fetch cache mode of "no-store". This 1021 // maps to LOAD_BYPASS_CACHE and LOAD_INHIBIT_CACHING in necko. 1022 nsLoadFlags loadFlags; 1023 loadFlags = nsIRequest::LOAD_BACKGROUND | nsIRequest::LOAD_BYPASS_CACHE | 1024 nsIRequest::INHIBIT_CACHING; 1025 1026 nsCOMPtr<nsIChannel> channel; 1027 // If we have the document, use it 1028 if (doc) { 1029 MOZ_ASSERT(mCookieJarSettings == doc->CookieJarSettings()); 1030 1031 nsCOMPtr<nsILoadGroup> loadGroup = doc->GetDocumentLoadGroup(); 1032 rv = NS_NewChannel(getter_AddRefs(channel), mSrc, doc, securityFlags, 1033 nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE, 1034 nullptr, // aPerformanceStorage 1035 loadGroup, 1036 nullptr, // aCallbacks 1037 loadFlags); // aLoadFlags 1038 } else { 1039 // otherwise use the principal 1040 rv = NS_NewChannel(getter_AddRefs(channel), mSrc, mPrincipal, securityFlags, 1041 nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE, 1042 mCookieJarSettings, 1043 nullptr, // aPerformanceStorage 1044 nullptr, // loadGroup 1045 nullptr, // aCallbacks 1046 loadFlags); // aLoadFlags 1047 NS_ENSURE_SUCCESS(rv, rv); 1048 1049 auto workerRef = mWorkerRef.Lock(); 1050 1051 if (*workerRef) { 1052 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo(); 1053 loadInfo->SetIsInThirdPartyContext( 1054 (*workerRef)->Private()->IsThirdPartyContext()); 1055 } 1056 } 1057 1058 NS_ENSURE_SUCCESS(rv, rv); 1059 1060 mHttpChannel = do_QueryInterface(channel); 1061 NS_ENSURE_TRUE(mHttpChannel, NS_ERROR_NO_INTERFACE); 1062 1063 SetupHttpChannel(); 1064 rv = SetupReferrerInfo(doc); 1065 NS_ENSURE_SUCCESS(rv, rv); 1066 1067 #ifdef DEBUG 1068 { 1069 nsCOMPtr<nsIInterfaceRequestor> notificationCallbacks; 1070 mHttpChannel->GetNotificationCallbacks( 1071 getter_AddRefs(notificationCallbacks)); 1072 MOZ_ASSERT(!notificationCallbacks); 1073 } 1074 #endif 1075 1076 mHttpChannel->SetNotificationCallbacks(this); 1077 1078 // Start reading from the channel 1079 rv = mHttpChannel->AsyncOpen(this); 1080 if (NS_FAILED(rv)) { 1081 DispatchFailConnection(); 1082 return rv; 1083 } 1084 1085 return rv; 1086 } 1087 1088 void EventSourceImpl::AnnounceConnection() { 1089 AssertIsOnTargetThread(); 1090 if (ReadyState() != CONNECTING) { 1091 NS_WARNING("Unexpected mReadyState!!!"); 1092 return; 1093 } 1094 1095 { 1096 auto lock = mSharedData.Lock(); 1097 if (lock->mServiceNotifier) { 1098 lock->mServiceNotifier->ConnectionOpened(); 1099 } 1100 } 1101 1102 // When a user agent is to announce the connection, the user agent must set 1103 // the readyState attribute to OPEN and queue a task to fire a simple event 1104 // named open at the EventSource object. 1105 1106 SetReadyState(OPEN); 1107 1108 nsresult rv = GetEventSource()->CheckCurrentGlobalCorrectness(); 1109 if (NS_FAILED(rv)) { 1110 return; 1111 } 1112 // We can't hold the mutex while dispatching the event because the mutex is 1113 // not reentrant, and content might call back into our code. 1114 rv = GetEventSource()->CreateAndDispatchSimpleEvent(u"open"_ns); 1115 if (NS_FAILED(rv)) { 1116 NS_WARNING("Failed to dispatch the error event!!!"); 1117 return; 1118 } 1119 } 1120 1121 nsresult EventSourceImpl::ResetConnection() { 1122 AssertIsOnMainThread(); 1123 if (mHttpChannel) { 1124 mHttpChannel->Cancel(NS_ERROR_ABORT); 1125 mHttpChannel = nullptr; 1126 } 1127 return NS_OK; 1128 } 1129 1130 void EventSourceImpl::ResetDecoder() { 1131 AssertIsOnTargetThread(); 1132 if (mUnicodeDecoder) { 1133 UTF_8_ENCODING->NewDecoderWithBOMRemovalInto(*mUnicodeDecoder); 1134 } 1135 mStatus = PARSE_STATE_OFF; 1136 ClearFields(); 1137 } 1138 1139 class CallRestartConnection final : public WorkerMainThreadRunnable { 1140 public: 1141 explicit CallRestartConnection(RefPtr<EventSourceImpl>&& aEventSourceImpl) 1142 : WorkerMainThreadRunnable(GetCurrentThreadWorkerPrivate(), 1143 "EventSource :: RestartConnection"_ns), 1144 mESImpl(std::move(aEventSourceImpl)) { 1145 MOZ_ASSERT(mESImpl); 1146 } 1147 1148 bool MainThreadRun() override { 1149 MOZ_ASSERT(mESImpl); 1150 mESImpl->RestartConnection(); 1151 // We want to ensure the shortest possible remaining lifetime 1152 // and not depend on the Runnable's destruction. 1153 mESImpl = nullptr; 1154 return true; 1155 } 1156 1157 protected: 1158 RefPtr<EventSourceImpl> mESImpl; 1159 }; 1160 1161 nsresult EventSourceImpl::RestartConnection() { 1162 AssertIsOnMainThread(); 1163 if (IsClosed()) { 1164 return NS_ERROR_ABORT; 1165 } 1166 1167 nsresult rv = ResetConnection(); 1168 NS_ENSURE_SUCCESS(rv, rv); 1169 rv = SetReconnectionTimeout(); 1170 NS_ENSURE_SUCCESS(rv, rv); 1171 return NS_OK; 1172 } 1173 1174 void EventSourceImpl::ReestablishConnection() { 1175 AssertIsOnTargetThread(); 1176 if (IsClosed()) { 1177 return; 1178 } 1179 1180 nsresult rv; 1181 if (mIsMainThread) { 1182 rv = RestartConnection(); 1183 } else { 1184 RefPtr<CallRestartConnection> runnable = new CallRestartConnection(this); 1185 ErrorResult result; 1186 runnable->Dispatch(GetCurrentThreadWorkerPrivate(), Canceling, result); 1187 MOZ_ASSERT(!result.Failed()); 1188 rv = result.StealNSResult(); 1189 } 1190 if (NS_FAILED(rv)) { 1191 return; 1192 } 1193 1194 RefPtr<EventSource> source = GetEventSource(); 1195 if (!source) { 1196 NS_WARNING("Event source is null"); 1197 return; 1198 } 1199 1200 rv = source->CheckCurrentGlobalCorrectness(); 1201 if (NS_FAILED(rv)) { 1202 return; 1203 } 1204 1205 SetReadyState(CONNECTING); 1206 ResetDecoder(); 1207 // We can't hold the mutex while dispatching the event because the mutex is 1208 // not reentrant, and content might call back into our code. 1209 rv = source->CreateAndDispatchSimpleEvent(u"error"_ns); 1210 if (NS_FAILED(rv)) { 1211 NS_WARNING("Failed to dispatch the error event!!!"); 1212 return; 1213 } 1214 } 1215 1216 nsresult EventSourceImpl::SetReconnectionTimeout() { 1217 AssertIsOnMainThread(); 1218 if (IsClosed()) { 1219 return NS_ERROR_ABORT; 1220 } 1221 1222 // the timer will be used whenever the requests are going finished. 1223 if (!mTimer) { 1224 mTimer = NS_NewTimer(); 1225 NS_ENSURE_STATE(mTimer); 1226 } 1227 1228 MOZ_TRY(mTimer->InitWithCallback(this, mReconnectionTime, 1229 nsITimer::TYPE_ONE_SHOT)); 1230 1231 return NS_OK; 1232 } 1233 1234 nsresult EventSourceImpl::PrintErrorOnConsole( 1235 const char* aBundleURI, const char* aError, 1236 const nsTArray<nsString>& aFormatStrings) { 1237 AssertIsOnMainThread(); 1238 MOZ_ASSERT(!mIsShutDown); 1239 nsCOMPtr<nsIStringBundleService> bundleService = 1240 mozilla::components::StringBundle::Service(); 1241 NS_ENSURE_STATE(bundleService); 1242 1243 nsCOMPtr<nsIStringBundle> strBundle; 1244 nsresult rv = 1245 bundleService->CreateBundle(aBundleURI, getter_AddRefs(strBundle)); 1246 NS_ENSURE_SUCCESS(rv, rv); 1247 1248 nsCOMPtr<nsIConsoleService> console( 1249 do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv)); 1250 NS_ENSURE_SUCCESS(rv, rv); 1251 1252 nsCOMPtr<nsIScriptError> errObj( 1253 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv)); 1254 NS_ENSURE_SUCCESS(rv, rv); 1255 1256 // Localize the error message 1257 nsAutoString message; 1258 if (!aFormatStrings.IsEmpty()) { 1259 rv = strBundle->FormatStringFromName(aError, aFormatStrings, message); 1260 } else { 1261 rv = strBundle->GetStringFromName(aError, message); 1262 } 1263 NS_ENSURE_SUCCESS(rv, rv); 1264 1265 rv = errObj->InitWithWindowID( 1266 message, mCallingLocation.FileName(), mCallingLocation.mLine, 1267 mCallingLocation.mColumn, nsIScriptError::errorFlag, "Event Source", 1268 mInnerWindowID); 1269 NS_ENSURE_SUCCESS(rv, rv); 1270 1271 // print the error message directly to the JS console 1272 rv = console->LogMessage(errObj); 1273 NS_ENSURE_SUCCESS(rv, rv); 1274 1275 return NS_OK; 1276 } 1277 1278 nsresult EventSourceImpl::ConsoleError() { 1279 AssertIsOnMainThread(); 1280 MOZ_ASSERT(!mIsShutDown); 1281 nsAutoCString targetSpec; 1282 nsresult rv = mSrc->GetSpec(targetSpec); 1283 NS_ENSURE_SUCCESS(rv, rv); 1284 1285 AutoTArray<nsString, 1> formatStrings; 1286 CopyUTF8toUTF16(targetSpec, *formatStrings.AppendElement()); 1287 1288 if (ReadyState() == CONNECTING) { 1289 rv = PrintErrorOnConsole("chrome://global/locale/appstrings.properties", 1290 "connectionFailure", formatStrings); 1291 } else { 1292 rv = PrintErrorOnConsole("chrome://global/locale/appstrings.properties", 1293 "netInterrupt", formatStrings); 1294 } 1295 NS_ENSURE_SUCCESS(rv, rv); 1296 1297 return NS_OK; 1298 } 1299 1300 void EventSourceImpl::DispatchFailConnection() { 1301 AssertIsOnMainThread(); 1302 if (IsClosed()) { 1303 return; 1304 } 1305 nsresult rv = ConsoleError(); 1306 if (NS_FAILED(rv)) { 1307 NS_WARNING("Failed to print to the console error"); 1308 } 1309 rv = Dispatch(NewRunnableMethod("dom::EventSourceImpl::FailConnection", this, 1310 &EventSourceImpl::FailConnection), 1311 NS_DISPATCH_NORMAL); 1312 if (NS_WARN_IF(NS_FAILED(rv))) { 1313 // if the worker is shutting down, the dispatching of normal WorkerRunnables 1314 // fails. 1315 return; 1316 } 1317 } 1318 1319 void EventSourceImpl::FailConnection() { 1320 AssertIsOnTargetThread(); 1321 if (IsClosed()) { 1322 return; 1323 } 1324 // Must change state to closed before firing event to content. 1325 SetReadyState(CLOSED); 1326 // When a user agent is to fail the connection, the user agent must set the 1327 // readyState attribute to CLOSED and queue a task to fire a simple event 1328 // named error at the EventSource object. 1329 nsresult rv = GetEventSource()->CheckCurrentGlobalCorrectness(); 1330 if (NS_SUCCEEDED(rv)) { 1331 // We can't hold the mutex while dispatching the event because the mutex 1332 // is not reentrant, and content might call back into our code. 1333 rv = GetEventSource()->CreateAndDispatchSimpleEvent(u"error"_ns); 1334 if (NS_FAILED(rv)) { 1335 NS_WARNING("Failed to dispatch the error event!!!"); 1336 } 1337 } 1338 // Call CloseInternal in the end of function because it may release 1339 // EventSourceImpl. 1340 CloseInternal(); 1341 } 1342 1343 NS_IMETHODIMP EventSourceImpl::Notify(nsITimer* aTimer) { 1344 AssertIsOnMainThread(); 1345 if (IsClosed()) { 1346 return NS_OK; 1347 } 1348 1349 MOZ_ASSERT(!mHttpChannel, "the channel hasn't been cancelled!!"); 1350 1351 if (!mFrozen) { 1352 nsresult rv = InitChannelAndRequestEventSource(mIsMainThread); 1353 if (NS_FAILED(rv)) { 1354 NS_WARNING("InitChannelAndRequestEventSource() failed"); 1355 } 1356 } 1357 return NS_OK; 1358 } 1359 1360 NS_IMETHODIMP EventSourceImpl::GetName(nsACString& aName) { 1361 aName.AssignLiteral("EventSourceImpl"); 1362 return NS_OK; 1363 } 1364 1365 nsresult EventSourceImpl::Thaw() { 1366 AssertIsOnMainThread(); 1367 if (IsClosed() || !mFrozen) { 1368 return NS_OK; 1369 } 1370 1371 MOZ_ASSERT(!mHttpChannel, "the connection hasn't been closed!!!"); 1372 1373 mFrozen = false; 1374 nsresult rv; 1375 if (!mGoingToDispatchAllMessages && mMessagesToDispatch.GetSize() > 0) { 1376 nsCOMPtr<nsIRunnable> event = 1377 NewRunnableMethod("dom::EventSourceImpl::DispatchAllMessageEvents", 1378 this, &EventSourceImpl::DispatchAllMessageEvents); 1379 NS_ENSURE_STATE(event); 1380 1381 mGoingToDispatchAllMessages = true; 1382 1383 rv = Dispatch(event.forget(), NS_DISPATCH_NORMAL); 1384 NS_ENSURE_SUCCESS(rv, rv); 1385 } 1386 1387 rv = InitChannelAndRequestEventSource(mIsMainThread); 1388 NS_ENSURE_SUCCESS(rv, rv); 1389 1390 return NS_OK; 1391 } 1392 1393 nsresult EventSourceImpl::Freeze() { 1394 AssertIsOnMainThread(); 1395 if (IsClosed() || mFrozen) { 1396 return NS_OK; 1397 } 1398 1399 MOZ_ASSERT(!mHttpChannel, "the connection hasn't been closed!!!"); 1400 mFrozen = true; 1401 return NS_OK; 1402 } 1403 1404 nsresult EventSourceImpl::DispatchCurrentMessageEvent() { 1405 AssertIsOnTargetThread(); 1406 MOZ_ASSERT(!mIsShutDown); 1407 UniquePtr<Message> message(std::move(mCurrentMessage)); 1408 ClearFields(); 1409 1410 if (!message || message->mData.IsEmpty()) { 1411 return NS_OK; 1412 } 1413 1414 // removes the trailing LF from mData 1415 MOZ_ASSERT(message->mData.CharAt(message->mData.Length() - 1) == LF_CHAR, 1416 "Invalid trailing character! LF was expected instead."); 1417 message->mData.SetLength(message->mData.Length() - 1); 1418 1419 if (message->mEventName.IsEmpty()) { 1420 message->mEventName.AssignLiteral("message"); 1421 } 1422 1423 mMessagesToDispatch.Push(message.release()); 1424 1425 if (!mGoingToDispatchAllMessages) { 1426 nsCOMPtr<nsIRunnable> event = 1427 NewRunnableMethod("dom::EventSourceImpl::DispatchAllMessageEvents", 1428 this, &EventSourceImpl::DispatchAllMessageEvents); 1429 NS_ENSURE_STATE(event); 1430 1431 mGoingToDispatchAllMessages = true; 1432 1433 return Dispatch(event.forget(), NS_DISPATCH_NORMAL); 1434 } 1435 1436 return NS_OK; 1437 } 1438 1439 void EventSourceImpl::DispatchAllMessageEvents() { 1440 AssertIsOnTargetThread(); 1441 mGoingToDispatchAllMessages = false; 1442 1443 if (IsClosed() || mFrozen) { 1444 return; 1445 } 1446 1447 nsresult rv; 1448 AutoJSAPI jsapi; 1449 { 1450 auto lock = mSharedData.Lock(); 1451 rv = lock->mEventSource->CheckCurrentGlobalCorrectness(); 1452 if (NS_FAILED(rv)) { 1453 return; 1454 } 1455 1456 if (NS_WARN_IF(!jsapi.Init(lock->mEventSource->GetOwnerGlobal()))) { 1457 return; 1458 } 1459 } 1460 1461 JSContext* cx = jsapi.cx(); 1462 1463 while (mMessagesToDispatch.GetSize() > 0) { 1464 UniquePtr<Message> message(mMessagesToDispatch.PopFront()); 1465 1466 if (message->mLastEventID.isSome()) { 1467 mLastEventID.Assign(message->mLastEventID.value()); 1468 } 1469 1470 if (message->mLastEventID.isNothing() && !mLastEventID.IsEmpty()) { 1471 message->mLastEventID = Some(mLastEventID); 1472 } 1473 1474 { 1475 auto lock = mSharedData.Lock(); 1476 if (lock->mServiceNotifier) { 1477 lock->mServiceNotifier->EventReceived(message->mEventName, mLastEventID, 1478 message->mData, mReconnectionTime, 1479 PR_Now()); 1480 } 1481 } 1482 1483 // Now we can turn our string into a jsval 1484 JS::Rooted<JS::Value> jsData(cx); 1485 { 1486 JSString* jsString; 1487 jsString = JS_NewUCStringCopyN(cx, message->mData.get(), 1488 message->mData.Length()); 1489 NS_ENSURE_TRUE_VOID(jsString); 1490 1491 jsData.setString(jsString); 1492 } 1493 1494 // create an event that uses the MessageEvent interface, 1495 // which does not bubble, is not cancelable, and has no default action 1496 1497 RefPtr<EventSource> eventSource = GetEventSource(); 1498 RefPtr<MessageEvent> event = 1499 new MessageEvent(eventSource, nullptr, nullptr); 1500 1501 event->InitMessageEvent(nullptr, message->mEventName, CanBubble::eNo, 1502 Cancelable::eNo, jsData, mOrigin, mLastEventID, 1503 nullptr, Sequence<OwningNonNull<MessagePort>>()); 1504 event->SetTrusted(true); 1505 1506 // We can't hold the mutex while dispatching the event because the mutex is 1507 // not reentrant, and content might call back into our code. 1508 IgnoredErrorResult err; 1509 eventSource->DispatchEvent(*event, err); 1510 if (err.Failed()) { 1511 NS_WARNING("Failed to dispatch the message event!!!"); 1512 return; 1513 } 1514 1515 if (IsClosed() || mFrozen) { 1516 return; 1517 } 1518 } 1519 } 1520 1521 void EventSourceImpl::ClearFields() { 1522 AssertIsOnTargetThread(); 1523 mCurrentMessage = nullptr; 1524 mLastFieldName.Truncate(); 1525 mLastFieldValue.Truncate(); 1526 } 1527 1528 nsresult EventSourceImpl::SetFieldAndClear() { 1529 MOZ_ASSERT(!mIsShutDown); 1530 AssertIsOnTargetThread(); 1531 if (mLastFieldName.IsEmpty()) { 1532 mLastFieldValue.Truncate(); 1533 return NS_OK; 1534 } 1535 if (!mCurrentMessage) { 1536 mCurrentMessage = MakeUnique<Message>(); 1537 } 1538 char16_t first_char; 1539 first_char = mLastFieldName.CharAt(0); 1540 1541 // with no case folding performed 1542 switch (first_char) { 1543 case char16_t('d'): 1544 if (mLastFieldName.EqualsLiteral("data")) { 1545 // If the field name is "data" append the field value to the data 1546 // buffer, then append a single U+000A LINE FEED (LF) character 1547 // to the data buffer. 1548 mCurrentMessage->mData.Append(mLastFieldValue); 1549 mCurrentMessage->mData.Append(LF_CHAR); 1550 } 1551 break; 1552 1553 case char16_t('e'): 1554 if (mLastFieldName.EqualsLiteral("event")) { 1555 mCurrentMessage->mEventName.Assign(mLastFieldValue); 1556 } 1557 break; 1558 1559 case char16_t('i'): 1560 if (mLastFieldName.EqualsLiteral("id")) { 1561 mCurrentMessage->mLastEventID = Some(mLastFieldValue); 1562 } 1563 break; 1564 1565 case char16_t('r'): 1566 if (mLastFieldName.EqualsLiteral("retry")) { 1567 uint32_t newValue = 0; 1568 uint32_t i = 0; // we must ensure that there are only digits 1569 bool assign = true; 1570 for (i = 0; i < mLastFieldValue.Length(); ++i) { 1571 if (mLastFieldValue.CharAt(i) < (char16_t)'0' || 1572 mLastFieldValue.CharAt(i) > (char16_t)'9') { 1573 assign = false; 1574 break; 1575 } 1576 newValue = newValue * 10 + (((uint32_t)mLastFieldValue.CharAt(i)) - 1577 ((uint32_t)((char16_t)'0'))); 1578 } 1579 1580 if (assign) { 1581 if (newValue < MIN_RECONNECTION_TIME_VALUE) { 1582 mReconnectionTime = MIN_RECONNECTION_TIME_VALUE; 1583 } else if (newValue > MAX_RECONNECTION_TIME_VALUE) { 1584 mReconnectionTime = MAX_RECONNECTION_TIME_VALUE; 1585 } else { 1586 mReconnectionTime = newValue; 1587 } 1588 } 1589 break; 1590 } 1591 break; 1592 } 1593 1594 mLastFieldName.Truncate(); 1595 mLastFieldValue.Truncate(); 1596 1597 return NS_OK; 1598 } 1599 1600 nsresult EventSourceImpl::CheckHealthOfRequestCallback( 1601 nsIRequest* aRequestCallback) { 1602 // This function could be run on target thread if http channel support 1603 // nsIThreadRetargetableRequest. otherwise, it's run on main thread. 1604 1605 // check if we have been closed or if the request has been canceled 1606 // or if we have been frozen 1607 if (IsClosed() || mFrozen || !mHttpChannel) { 1608 return NS_ERROR_ABORT; 1609 } 1610 1611 nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequestCallback); 1612 NS_ENSURE_STATE(httpChannel); 1613 1614 if (httpChannel != mHttpChannel) { 1615 NS_WARNING("wrong channel from request callback"); 1616 return NS_ERROR_ABORT; 1617 } 1618 1619 return NS_OK; 1620 } 1621 1622 nsresult EventSourceImpl::ParseCharacter(char16_t aChr) { 1623 AssertIsOnTargetThread(); 1624 nsresult rv; 1625 1626 if (IsClosed()) { 1627 return NS_ERROR_ABORT; 1628 } 1629 1630 switch (mStatus) { 1631 case PARSE_STATE_OFF: 1632 NS_ERROR("Invalid state"); 1633 return NS_ERROR_FAILURE; 1634 break; 1635 1636 case PARSE_STATE_BEGIN_OF_STREAM: 1637 if (aChr == CR_CHAR) { 1638 mStatus = PARSE_STATE_CR_CHAR; 1639 } else if (aChr == LF_CHAR) { 1640 mStatus = PARSE_STATE_BEGIN_OF_LINE; 1641 } else if (aChr == COLON_CHAR) { 1642 mStatus = PARSE_STATE_COMMENT; 1643 } else { 1644 mLastFieldName += aChr; 1645 mStatus = PARSE_STATE_FIELD_NAME; 1646 } 1647 break; 1648 1649 case PARSE_STATE_CR_CHAR: 1650 if (aChr == CR_CHAR) { 1651 rv = DispatchCurrentMessageEvent(); // there is an empty line (CRCR) 1652 NS_ENSURE_SUCCESS(rv, rv); 1653 } else if (aChr == LF_CHAR) { 1654 mStatus = PARSE_STATE_BEGIN_OF_LINE; 1655 } else if (aChr == COLON_CHAR) { 1656 mStatus = PARSE_STATE_COMMENT; 1657 } else { 1658 mLastFieldName += aChr; 1659 mStatus = PARSE_STATE_FIELD_NAME; 1660 } 1661 1662 break; 1663 1664 case PARSE_STATE_COMMENT: 1665 if (aChr == CR_CHAR) { 1666 mStatus = PARSE_STATE_CR_CHAR; 1667 } else if (aChr == LF_CHAR) { 1668 mStatus = PARSE_STATE_BEGIN_OF_LINE; 1669 } 1670 1671 break; 1672 1673 case PARSE_STATE_FIELD_NAME: 1674 if (aChr == CR_CHAR) { 1675 rv = SetFieldAndClear(); 1676 NS_ENSURE_SUCCESS(rv, rv); 1677 1678 mStatus = PARSE_STATE_CR_CHAR; 1679 } else if (aChr == LF_CHAR) { 1680 rv = SetFieldAndClear(); 1681 NS_ENSURE_SUCCESS(rv, rv); 1682 1683 mStatus = PARSE_STATE_BEGIN_OF_LINE; 1684 } else if (aChr == COLON_CHAR) { 1685 mStatus = PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE; 1686 } else { 1687 mLastFieldName += aChr; 1688 } 1689 1690 break; 1691 1692 case PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE: 1693 if (aChr == CR_CHAR) { 1694 rv = SetFieldAndClear(); 1695 NS_ENSURE_SUCCESS(rv, rv); 1696 1697 mStatus = PARSE_STATE_CR_CHAR; 1698 } else if (aChr == LF_CHAR) { 1699 rv = SetFieldAndClear(); 1700 NS_ENSURE_SUCCESS(rv, rv); 1701 1702 mStatus = PARSE_STATE_BEGIN_OF_LINE; 1703 } else if (aChr == SPACE_CHAR) { 1704 mStatus = PARSE_STATE_FIELD_VALUE; 1705 } else { 1706 mLastFieldValue += aChr; 1707 mStatus = PARSE_STATE_FIELD_VALUE; 1708 } 1709 1710 break; 1711 1712 case PARSE_STATE_FIELD_VALUE: 1713 if (aChr == CR_CHAR) { 1714 rv = SetFieldAndClear(); 1715 NS_ENSURE_SUCCESS(rv, rv); 1716 1717 mStatus = PARSE_STATE_CR_CHAR; 1718 } else if (aChr == LF_CHAR) { 1719 rv = SetFieldAndClear(); 1720 NS_ENSURE_SUCCESS(rv, rv); 1721 1722 mStatus = PARSE_STATE_BEGIN_OF_LINE; 1723 } else if (aChr != 0) { 1724 // Avoid appending the null char to the field value. 1725 mLastFieldValue += aChr; 1726 } else if (mLastFieldName.EqualsLiteral("id")) { 1727 // Ignore the whole id field if aChr is null 1728 mStatus = PARSE_STATE_IGNORE_FIELD_VALUE; 1729 mLastFieldValue.Truncate(); 1730 } 1731 1732 break; 1733 1734 case PARSE_STATE_IGNORE_FIELD_VALUE: 1735 if (aChr == CR_CHAR) { 1736 mStatus = PARSE_STATE_CR_CHAR; 1737 } else if (aChr == LF_CHAR) { 1738 mStatus = PARSE_STATE_BEGIN_OF_LINE; 1739 } 1740 break; 1741 1742 case PARSE_STATE_BEGIN_OF_LINE: 1743 if (aChr == CR_CHAR) { 1744 rv = DispatchCurrentMessageEvent(); // there is an empty line 1745 NS_ENSURE_SUCCESS(rv, rv); 1746 1747 mStatus = PARSE_STATE_CR_CHAR; 1748 } else if (aChr == LF_CHAR) { 1749 rv = DispatchCurrentMessageEvent(); // there is an empty line 1750 NS_ENSURE_SUCCESS(rv, rv); 1751 1752 mStatus = PARSE_STATE_BEGIN_OF_LINE; 1753 } else if (aChr == COLON_CHAR) { 1754 mStatus = PARSE_STATE_COMMENT; 1755 } else if (aChr != 0) { 1756 // Avoid appending the null char to the field name. 1757 mLastFieldName += aChr; 1758 mStatus = PARSE_STATE_FIELD_NAME; 1759 } 1760 1761 break; 1762 } 1763 1764 return NS_OK; 1765 } 1766 1767 namespace { 1768 1769 class WorkerRunnableDispatcher final : public WorkerThreadRunnable { 1770 RefPtr<EventSourceImpl> mEventSourceImpl; 1771 1772 public: 1773 WorkerRunnableDispatcher(RefPtr<EventSourceImpl>&& aImpl, 1774 WorkerPrivate* aWorkerPrivate, 1775 already_AddRefed<nsIRunnable> aEvent) 1776 : WorkerThreadRunnable("WorkerRunnableDispatcher"), 1777 mEventSourceImpl(std::move(aImpl)), 1778 mEvent(std::move(aEvent)) {} 1779 1780 bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override { 1781 aWorkerPrivate->AssertIsOnWorkerThread(); 1782 return !NS_FAILED(mEvent->Run()); 1783 } 1784 1785 void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, 1786 bool aRunResult) override { 1787 // Ensure we drop the RefPtr on the worker thread 1788 // and to not keep us alive longer than needed. 1789 mEventSourceImpl = nullptr; 1790 } 1791 1792 bool PreDispatch(WorkerPrivate* aWorkerPrivate) override { 1793 // We don't call WorkerRunnable::PreDispatch because it would assert the 1794 // wrong thing about which thread we're on. We're on whichever thread the 1795 // channel implementation is running on (probably the main thread or 1796 // transport thread). 1797 return true; 1798 } 1799 1800 void PostDispatch(WorkerPrivate* aWorkerPrivate, 1801 bool aDispatchResult) override { 1802 // We don't call WorkerRunnable::PostDispatch because it would assert the 1803 // wrong thing about which thread we're on. We're on whichever thread the 1804 // channel implementation is running on (probably the main thread or 1805 // transport thread). 1806 } 1807 1808 private: 1809 nsCOMPtr<nsIRunnable> mEvent; 1810 }; 1811 1812 } // namespace 1813 1814 bool EventSourceImpl::CreateWorkerRef(WorkerPrivate* aWorkerPrivate) { 1815 auto tsWorkerRef = mWorkerRef.Lock(); 1816 MOZ_ASSERT(!*tsWorkerRef); 1817 MOZ_ASSERT(aWorkerPrivate); 1818 aWorkerPrivate->AssertIsOnWorkerThread(); 1819 1820 if (mIsShutDown) { 1821 return false; 1822 } 1823 1824 RefPtr<EventSourceImpl> self = this; 1825 RefPtr<StrongWorkerRef> workerRef = StrongWorkerRef::Create( 1826 aWorkerPrivate, "EventSource", [self]() { self->Close(); }); 1827 1828 if (NS_WARN_IF(!workerRef)) { 1829 return false; 1830 } 1831 1832 *tsWorkerRef = new ThreadSafeWorkerRef(workerRef); 1833 return true; 1834 } 1835 1836 void EventSourceImpl::ReleaseWorkerRef() { 1837 MOZ_ASSERT(IsClosed()); 1838 MOZ_ASSERT(IsCurrentThreadRunningWorker()); 1839 auto workerRef = mWorkerRef.Lock(); 1840 *workerRef = nullptr; 1841 } 1842 1843 //----------------------------------------------------------------------------- 1844 // EventSourceImpl::nsIEventTarget 1845 //----------------------------------------------------------------------------- 1846 NS_IMETHODIMP 1847 EventSourceImpl::DispatchFromScript(nsIRunnable* aEvent, DispatchFlags aFlags) { 1848 nsCOMPtr<nsIRunnable> event(aEvent); 1849 return Dispatch(event.forget(), aFlags); 1850 } 1851 1852 NS_IMETHODIMP 1853 EventSourceImpl::Dispatch(already_AddRefed<nsIRunnable> aEvent, 1854 DispatchFlags aFlags) { 1855 // FIXME: This dispatch implementation has inconsistent leaking behaviour when 1856 // `NS_DISPATCH_FALLIBLE` is not specified. 1857 nsCOMPtr<nsIRunnable> event_ref(aEvent); 1858 if (mIsMainThread) { 1859 return NS_DispatchToMainThread(event_ref.forget(), aFlags); 1860 } 1861 1862 if (mIsShutDown) { 1863 // We want to avoid clutter about errors in our shutdown logs, 1864 // so just report NS_OK (we have no explicit return value 1865 // for shutdown). 1866 return NS_OK; 1867 } 1868 1869 // If the target is a worker, we have to use a custom WorkerRunnableDispatcher 1870 // runnable. 1871 auto workerRef = mWorkerRef.Lock(); 1872 // Return NS_OK if the worker has already shutdown 1873 if (!*workerRef) { 1874 return NS_OK; 1875 } 1876 RefPtr<WorkerRunnableDispatcher> event = new WorkerRunnableDispatcher( 1877 this, (*workerRef)->Private(), event_ref.forget()); 1878 1879 if (!event->Dispatch((*workerRef)->Private())) { 1880 return NS_ERROR_FAILURE; 1881 } 1882 return NS_OK; 1883 } 1884 1885 NS_IMETHODIMP 1886 EventSourceImpl::DelayedDispatch(already_AddRefed<nsIRunnable> aEvent, 1887 uint32_t aDelayMs) { 1888 return NS_ERROR_NOT_IMPLEMENTED; 1889 } 1890 1891 NS_IMETHODIMP 1892 EventSourceImpl::RegisterShutdownTask(nsITargetShutdownTask*) { 1893 return NS_ERROR_NOT_IMPLEMENTED; 1894 } 1895 1896 NS_IMETHODIMP 1897 EventSourceImpl::UnregisterShutdownTask(nsITargetShutdownTask*) { 1898 return NS_ERROR_NOT_IMPLEMENTED; 1899 } 1900 1901 //----------------------------------------------------------------------------- 1902 // EventSourceImpl::nsIThreadRetargetableStreamListener 1903 //----------------------------------------------------------------------------- 1904 NS_IMETHODIMP 1905 EventSourceImpl::CheckListenerChain() { 1906 MOZ_ASSERT(NS_IsMainThread(), "Should be on the main thread!"); 1907 return NS_OK; 1908 } 1909 1910 NS_IMETHODIMP 1911 EventSourceImpl::OnDataFinished(nsresult) { return NS_OK; } 1912 1913 //////////////////////////////////////////////////////////////////////////////// 1914 // EventSource 1915 //////////////////////////////////////////////////////////////////////////////// 1916 1917 EventSource::EventSource(nsIGlobalObject* aGlobal, 1918 nsICookieJarSettings* aCookieJarSettings, 1919 bool aWithCredentials) 1920 : DOMEventTargetHelper(aGlobal), 1921 mWithCredentials(aWithCredentials), 1922 mIsMainThread(NS_IsMainThread()) { 1923 MOZ_ASSERT(aGlobal); 1924 MOZ_ASSERT(aCookieJarSettings); 1925 mESImpl = new EventSourceImpl(this, aCookieJarSettings); 1926 } 1927 1928 EventSource::~EventSource() = default; 1929 1930 nsresult EventSource::CreateAndDispatchSimpleEvent(const nsAString& aName) { 1931 RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr); 1932 // it doesn't bubble, and it isn't cancelable 1933 event->InitEvent(aName, false, false); 1934 event->SetTrusted(true); 1935 ErrorResult rv; 1936 DispatchEvent(*event, rv); 1937 return rv.StealNSResult(); 1938 } 1939 1940 /* static */ 1941 already_AddRefed<EventSource> EventSource::Constructor( 1942 const GlobalObject& aGlobal, const nsAString& aURL, 1943 const EventSourceInit& aEventSourceInitDict, ErrorResult& aRv) { 1944 nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); 1945 if (NS_WARN_IF(!global)) { 1946 aRv.Throw(NS_ERROR_FAILURE); 1947 return nullptr; 1948 } 1949 1950 nsCOMPtr<nsICookieJarSettings> cookieJarSettings; 1951 nsCOMPtr<nsPIDOMWindowInner> ownerWindow = do_QueryInterface(global); 1952 if (ownerWindow) { 1953 Document* doc = ownerWindow->GetExtantDoc(); 1954 if (NS_WARN_IF(!doc)) { 1955 aRv.Throw(NS_ERROR_FAILURE); 1956 return nullptr; 1957 } 1958 1959 cookieJarSettings = doc->CookieJarSettings(); 1960 } else { 1961 // Worker side. 1962 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 1963 if (!workerPrivate) { 1964 aRv.Throw(NS_ERROR_FAILURE); 1965 return nullptr; 1966 } 1967 1968 cookieJarSettings = workerPrivate->CookieJarSettings(); 1969 } 1970 1971 RefPtr<EventSource> eventSource = new EventSource( 1972 global, cookieJarSettings, aEventSourceInitDict.mWithCredentials); 1973 1974 if (NS_IsMainThread()) { 1975 // Get principal from document and init EventSourceImpl 1976 nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal = 1977 do_QueryInterface(aGlobal.GetAsSupports()); 1978 if (!scriptPrincipal) { 1979 aRv.Throw(NS_ERROR_FAILURE); 1980 return nullptr; 1981 } 1982 nsCOMPtr<nsIPrincipal> principal = scriptPrincipal->GetPrincipal(); 1983 if (!principal) { 1984 aRv.Throw(NS_ERROR_FAILURE); 1985 return nullptr; 1986 } 1987 eventSource->mESImpl->Init(global, principal, aURL, aRv); 1988 if (NS_WARN_IF(aRv.Failed())) { 1989 return nullptr; 1990 } 1991 1992 eventSource->mESImpl->InitChannelAndRequestEventSource(true); 1993 return eventSource.forget(); 1994 } 1995 1996 // Worker side. 1997 { 1998 // Scope for possible failures that need cleanup 1999 auto guardESImpl = MakeScopeExit([&] { eventSource->mESImpl = nullptr; }); 2000 2001 WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); 2002 MOZ_ASSERT(workerPrivate); 2003 2004 eventSource->mESImpl->mInnerWindowID = workerPrivate->WindowID(); 2005 2006 eventSource->mESImpl->Init(nullptr, workerPrivate->GetPrincipal(), aURL, 2007 aRv); 2008 if (NS_WARN_IF(aRv.Failed())) { 2009 return nullptr; 2010 } 2011 2012 // In workers we have to keep the worker alive using a WorkerRef in order 2013 // to dispatch messages correctly. 2014 if (!eventSource->mESImpl->CreateWorkerRef(workerPrivate)) { 2015 // The worker is already shutting down. Let's return an already closed 2016 // object, but marked as Connecting. 2017 if (eventSource->mESImpl) { 2018 // mESImpl is nulled by this call such that EventSourceImpl is 2019 // released before returning the object, otherwise 2020 // it will set EventSource to a CLOSED state in its DTOR.. 2021 eventSource->mESImpl->Close(); 2022 } 2023 eventSource->mReadyState = EventSourceImpl::CONNECTING; 2024 2025 guardESImpl.release(); 2026 return eventSource.forget(); 2027 } 2028 2029 // Let's connect to the server. 2030 RefPtr<ConnectRunnable> connectRunnable = 2031 new ConnectRunnable(workerPrivate, eventSource->mESImpl); 2032 connectRunnable->Dispatch(workerPrivate, Canceling, aRv); 2033 if (NS_WARN_IF(aRv.Failed())) { 2034 return nullptr; 2035 } 2036 2037 // End of scope for possible failures 2038 guardESImpl.release(); 2039 } 2040 2041 return eventSource.forget(); 2042 } 2043 2044 // nsWrapperCache 2045 JSObject* EventSource::WrapObject(JSContext* aCx, 2046 JS::Handle<JSObject*> aGivenProto) { 2047 return EventSource_Binding::Wrap(aCx, this, aGivenProto); 2048 } 2049 2050 void EventSource::Close() { 2051 AssertIsOnTargetThread(); 2052 if (mESImpl) { 2053 // Close potentially kills ourself, ensure 2054 // to not access any members afterwards. 2055 mESImpl->Close(); 2056 } 2057 } 2058 2059 //----------------------------------------------------------------------------- 2060 // EventSource::nsISupports 2061 //----------------------------------------------------------------------------- 2062 2063 NS_IMPL_CYCLE_COLLECTION_CLASS(EventSource) 2064 2065 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(EventSource, 2066 DOMEventTargetHelper) 2067 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 2068 2069 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(EventSource, 2070 DOMEventTargetHelper) 2071 if (tmp->mESImpl) { 2072 // IsCertainlyaliveForCC will return true and cause the cycle 2073 // collector to skip this instance when mESImpl is non-null and 2074 // points back to ourself. 2075 // mESImpl is initialized to be non-null in the constructor 2076 // and should have been wiped out in our close function. 2077 MOZ_ASSERT_UNREACHABLE("Paranoia cleanup that should never happen."); 2078 tmp->Close(); 2079 } 2080 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 2081 2082 bool EventSource::IsCertainlyAliveForCC() const { 2083 // Until we are double linked forth and back, we want to stay alive. 2084 if (!mESImpl) { 2085 return false; 2086 } 2087 auto lock = mESImpl->mSharedData.Lock(); 2088 return lock->mEventSource == this; 2089 } 2090 2091 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EventSource) 2092 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper) 2093 2094 NS_IMPL_ADDREF_INHERITED(EventSource, DOMEventTargetHelper) 2095 NS_IMPL_RELEASE_INHERITED(EventSource, DOMEventTargetHelper) 2096 2097 } // namespace mozilla::dom