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/. */
7 #include "mozilla/ArrayUtils.h"
8 #include "mozilla/Components.h"
9 #include "mozilla/DataMutex.h"
10 #include "mozilla/DebugOnly.h"
11 #include "mozilla/LoadInfo.h"
12 #include "mozilla/DOMEventTargetHelper.h"
13 #include "mozilla/dom/EventSource.h"
14 #include "mozilla/dom/EventSourceBinding.h"
15 #include "mozilla/dom/MessageEvent.h"
16 #include "mozilla/dom/MessageEventBinding.h"
17 #include "mozilla/dom/ScriptSettings.h"
18 #include "mozilla/dom/WorkerPrivate.h"
19 #include "mozilla/dom/WorkerRef.h"
20 #include "mozilla/dom/WorkerRunnable.h"
21 #include "mozilla/dom/WorkerScope.h"
22 #include "mozilla/dom/EventSourceEventService.h"
23 #include "mozilla/ScopeExit.h"
24 #include "mozilla/UniquePtrExtensions.h"
25 #include "nsComponentManagerUtils.h"
26 #include "nsIThreadRetargetableStreamListener.h"
27 #include "nsNetUtil.h"
28 #include "nsIAuthPrompt.h"
29 #include "nsIAuthPrompt2.h"
30 #include "nsIHttpChannel.h"
31 #include "nsIInputStream.h"
32 #include "nsIInterfaceRequestorUtils.h"
33 #include "nsMimeTypes.h"
34 #include "nsIPromptFactory.h"
35 #include "nsIWindowWatcher.h"
36 #include "nsPresContext.h"
37 #include "nsProxyRelease.h"
38 #include "nsContentPolicyUtils.h"
39 #include "nsIStringBundle.h"
40 #include "nsIConsoleService.h"
41 #include "nsIObserverService.h"
42 #include "nsIScriptObjectPrincipal.h"
43 #include "nsJSUtils.h"
44 #include "nsIThreadRetargetableRequest.h"
45 #include "nsIAsyncVerifyRedirectCallback.h"
46 #include "nsIScriptError.h"
47 #include "nsContentUtils.h"
48 #include "mozilla/Preferences.h"
49 #include "xpcpublic.h"
50 #include "nsWrapperCacheInlines.h"
51 #include "mozilla/Attributes.h"
53 #include "mozilla/Encoding.h"
54 #include "ReferrerInfo.h"
56 namespace mozilla::dom
{
59 static LazyLogModule
gEventSourceLog("EventSource");
62 #define SPACE_CHAR (char16_t)0x0020
63 #define CR_CHAR (char16_t)0x000D
64 #define LF_CHAR (char16_t)0x000A
65 #define COLON_CHAR (char16_t)0x003A
67 // Reconnection time related values in milliseconds. The default one is equal
68 // to the default value of the pref dom.server-events.default-reconnection-time
69 #define MIN_RECONNECTION_TIME_VALUE 500
70 #define DEFAULT_RECONNECTION_TIME_VALUE 5000
71 #define MAX_RECONNECTION_TIME_VALUE \
72 PR_IntervalToMilliseconds(DELAY_INTERVAL_LIMIT)
74 class EventSourceImpl final
: public nsIObserver
,
75 public nsIStreamListener
,
76 public nsIChannelEventSink
,
77 public nsIInterfaceRequestor
,
78 public nsSupportsWeakReference
,
79 public nsIEventTarget
,
80 public nsITimerCallback
,
82 public nsIThreadRetargetableStreamListener
{
84 NS_DECL_THREADSAFE_ISUPPORTS
86 NS_DECL_NSIREQUESTOBSERVER
87 NS_DECL_NSISTREAMLISTENER
88 NS_DECL_NSICHANNELEVENTSINK
89 NS_DECL_NSIINTERFACEREQUESTOR
90 NS_DECL_NSIEVENTTARGET_FULL
91 NS_DECL_NSITIMERCALLBACK
93 NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
95 EventSourceImpl(EventSource
* aEventSource
,
96 nsICookieJarSettings
* aCookieJarSettings
);
98 enum { CONNECTING
= 0U, OPEN
= 1U, CLOSED
= 2U };
102 void Init(nsIPrincipal
* aPrincipal
, const nsAString
& aURL
, ErrorResult
& aRv
);
104 nsresult
GetBaseURI(nsIURI
** aBaseURI
);
106 void SetupHttpChannel();
107 nsresult
SetupReferrerInfo(const nsCOMPtr
<Document
>& aDocument
);
108 nsresult
InitChannelAndRequestEventSource(bool aEventTargetAccessAllowed
);
109 nsresult
ResetConnection();
111 nsresult
SetReconnectionTimeout();
113 void AnnounceConnection();
114 void DispatchAllMessageEvents();
115 nsresult
RestartConnection();
116 void ReestablishConnection();
117 void DispatchFailConnection();
118 void FailConnection();
123 nsresult
PrintErrorOnConsole(const char* aBundleURI
, const char* aError
,
124 const nsTArray
<nsString
>& aFormatStrings
);
125 nsresult
ConsoleError();
127 static nsresult
StreamReaderFunc(nsIInputStream
* aInputStream
, void* aClosure
,
128 const char* aFromRawSegment
,
129 uint32_t aToOffset
, uint32_t aCount
,
130 uint32_t* aWriteCount
);
131 void ParseSegment(const char* aBuffer
, uint32_t aLength
);
132 nsresult
SetFieldAndClear();
134 nsresult
ResetEvent();
135 nsresult
DispatchCurrentMessageEvent();
136 nsresult
ParseCharacter(char16_t aChr
);
137 nsresult
CheckHealthOfRequestCallback(nsIRequest
* aRequestCallback
);
138 nsresult
OnRedirectVerifyCallback(nsresult result
);
139 nsresult
ParseURL(const nsAString
& aURL
);
140 nsresult
AddWindowObservers();
141 void RemoveWindowObservers();
143 void CloseInternal();
144 void CleanupOnMainThread();
146 bool CreateWorkerRef(WorkerPrivate
* aWorkerPrivate
);
147 void ReleaseWorkerRef();
149 void AssertIsOnTargetThread() const {
150 MOZ_DIAGNOSTIC_ASSERT(IsTargetThread());
153 bool IsTargetThread() const { return NS_GetCurrentThread() == mTargetThread
; }
155 uint16_t ReadyState() {
156 auto lock
= mSharedData
.Lock();
157 if (lock
->mEventSource
) {
158 return lock
->mEventSource
->mReadyState
;
160 // EventSourceImpl keeps EventSource alive. If mEventSource is null, it
161 // means that the EventSource has been closed.
165 void SetReadyState(uint16_t aReadyState
) {
166 auto lock
= mSharedData
.Lock();
167 MOZ_ASSERT(lock
->mEventSource
);
168 MOZ_ASSERT(!mIsShutDown
);
169 lock
->mEventSource
->mReadyState
= aReadyState
;
172 bool IsClosed() { return ReadyState() == CLOSED
; }
174 RefPtr
<EventSource
> GetEventSource() {
175 AssertIsOnTargetThread();
176 auto lock
= mSharedData
.Lock();
177 return lock
->mEventSource
;
181 * A simple state machine used to manage the event-source's line buffer
183 * PARSE_STATE_OFF -> PARSE_STATE_BEGIN_OF_STREAM
185 * PARSE_STATE_BEGIN_OF_STREAM -> PARSE_STATE_CR_CHAR |
186 * PARSE_STATE_BEGIN_OF_LINE |
187 * PARSE_STATE_COMMENT |
188 * PARSE_STATE_FIELD_NAME
190 * PARSE_STATE_CR_CHAR -> PARSE_STATE_CR_CHAR |
191 * PARSE_STATE_COMMENT |
192 * PARSE_STATE_FIELD_NAME |
193 * PARSE_STATE_BEGIN_OF_LINE
195 * PARSE_STATE_COMMENT -> PARSE_STATE_CR_CHAR |
196 * PARSE_STATE_BEGIN_OF_LINE
198 * PARSE_STATE_FIELD_NAME -> PARSE_STATE_CR_CHAR |
199 * PARSE_STATE_BEGIN_OF_LINE |
200 * PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE
202 * PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE -> PARSE_STATE_FIELD_VALUE |
203 * PARSE_STATE_CR_CHAR |
204 * PARSE_STATE_BEGIN_OF_LINE
206 * PARSE_STATE_FIELD_VALUE -> PARSE_STATE_CR_CHAR |
207 * PARSE_STATE_BEGIN_OF_LINE
209 * PARSE_STATE_BEGIN_OF_LINE -> PARSE_STATE_CR_CHAR |
210 * PARSE_STATE_COMMENT |
211 * PARSE_STATE_FIELD_NAME |
212 * PARSE_STATE_BEGIN_OF_LINE
214 * Whenever the parser find an empty line or the end-of-file
215 * it dispatches the stacked event.
220 PARSE_STATE_BEGIN_OF_STREAM
,
223 PARSE_STATE_FIELD_NAME
,
224 PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE
,
225 PARSE_STATE_FIELD_VALUE
,
226 PARSE_STATE_IGNORE_FIELD_VALUE
,
227 PARSE_STATE_BEGIN_OF_LINE
230 // Connection related data members. Should only be accessed on main thread.
231 nsCOMPtr
<nsIURI
> mSrc
;
232 uint32_t mReconnectionTime
; // in ms
233 nsCOMPtr
<nsIPrincipal
> mPrincipal
;
235 nsCOMPtr
<nsITimer
> mTimer
;
236 nsCOMPtr
<nsIHttpChannel
> mHttpChannel
;
240 // We need to be able to distinguish between different states of id field:
241 // 1) is not given at all
242 // 2) is given but is empty
243 // 3) is given and has a value
244 // We can't check for the 1st state with a simple nsString.
245 Maybe
<nsString
> mLastEventID
;
249 // Message related data members. May be set / initialized when initializing
250 // EventSourceImpl on target thread but should only be used on target thread.
251 nsString mLastEventID
;
252 UniquePtr
<Message
> mCurrentMessage
;
253 nsDeque
<Message
> mMessagesToDispatch
;
254 ParserStatus mStatus
;
255 mozilla::UniquePtr
<mozilla::Decoder
> mUnicodeDecoder
;
256 nsString mLastFieldName
;
257 nsString mLastFieldValue
;
259 // EventSourceImpl internal states.
260 // WorkerRef to keep the worker alive. (accessed on worker thread only)
261 RefPtr
<ThreadSafeWorkerRef
> mWorkerRef
;
262 // Whether the window is frozen. May be set on main thread and read on target
264 Atomic
<bool> mFrozen
;
265 // There are some messages are going to be dispatched when thaw.
266 bool mGoingToDispatchAllMessages
;
267 // Whether the EventSource is run on main thread.
268 const bool mIsMainThread
;
269 // Whether the EventSourceImpl is going to be destroyed.
270 Atomic
<bool> mIsShutDown
;
272 class EventSourceServiceNotifier final
{
274 EventSourceServiceNotifier(RefPtr
<EventSourceImpl
>&& aEventSourceImpl
,
275 uint64_t aHttpChannelId
, uint64_t aInnerWindowID
)
276 : mEventSourceImpl(std::move(aEventSourceImpl
)),
277 mHttpChannelId(aHttpChannelId
),
278 mInnerWindowID(aInnerWindowID
),
279 mConnectionOpened(false) {
280 AssertIsOnMainThread();
281 mService
= EventSourceEventService::GetOrCreate();
284 void ConnectionOpened() {
285 mEventSourceImpl
->AssertIsOnTargetThread();
286 mService
->EventSourceConnectionOpened(mHttpChannelId
, mInnerWindowID
);
287 mConnectionOpened
= true;
290 void EventReceived(const nsAString
& aEventName
,
291 const nsAString
& aLastEventID
, const nsAString
& aData
,
292 uint32_t aRetry
, DOMHighResTimeStamp aTimeStamp
) {
293 mEventSourceImpl
->AssertIsOnTargetThread();
294 mService
->EventReceived(mHttpChannelId
, mInnerWindowID
, aEventName
,
295 aLastEventID
, aData
, aRetry
, aTimeStamp
);
298 ~EventSourceServiceNotifier() {
299 // It is safe to call this on any thread because
300 // EventSourceConnectionClosed method is thread safe and
301 // NS_ReleaseOnMainThread explicitly releases the service on the main
303 if (mConnectionOpened
) {
304 // We want to notify about connection being closed only if we told
305 // it was ever opened. The check is needed if OnStartRequest is called
306 // on the main thread while close() is called on a worker thread.
307 mService
->EventSourceConnectionClosed(mHttpChannelId
, mInnerWindowID
);
309 NS_ReleaseOnMainThread("EventSourceServiceNotifier::mService",
314 RefPtr
<EventSourceEventService
> mService
;
315 RefPtr
<EventSourceImpl
> mEventSourceImpl
;
316 uint64_t mHttpChannelId
;
317 uint64_t mInnerWindowID
;
318 bool mConnectionOpened
;
322 RefPtr
<EventSource
> mEventSource
;
323 UniquePtr
<EventSourceServiceNotifier
> mServiceNotifier
;
326 DataMutex
<SharedData
> mSharedData
;
328 // Event Source owner information:
329 // - the script file name
330 // - source code line number and column number where the Event Source object
332 // - the ID of the inner window where the script lives. Note that this may not
333 // be the same as the Event Source owner window.
334 // These attributes are used for error reporting. Should only be accessed on
336 nsString mScriptFile
;
337 uint32_t mScriptLine
;
338 uint32_t mScriptColumn
;
339 uint64_t mInnerWindowID
;
342 nsCOMPtr
<nsICookieJarSettings
> mCookieJarSettings
;
344 // Pointer to the target thread for checking whether we are
345 // on the target thread. This is intentionally a non-owning
346 // pointer in order not to affect the thread destruction
347 // sequence. This pointer must only be compared for equality
348 // and must not be dereferenced.
349 nsIThread
* mTargetThread
;
352 EventSourceImpl(const EventSourceImpl
& x
) = delete;
353 EventSourceImpl
& operator=(const EventSourceImpl
& x
) = delete;
358 // If we threw during Init we never called Close
359 SetReadyState(CLOSED
);
364 NS_IMPL_ISUPPORTS(EventSourceImpl
, nsIObserver
, nsIStreamListener
,
365 nsIRequestObserver
, nsIChannelEventSink
,
366 nsIInterfaceRequestor
, nsISupportsWeakReference
,
367 nsIEventTarget
, nsIThreadRetargetableStreamListener
,
368 nsITimerCallback
, nsINamed
)
370 EventSourceImpl::EventSourceImpl(EventSource
* aEventSource
,
371 nsICookieJarSettings
* aCookieJarSettings
)
372 : mReconnectionTime(0),
373 mStatus(PARSE_STATE_OFF
),
375 mGoingToDispatchAllMessages(false),
376 mIsMainThread(NS_IsMainThread()),
378 mSharedData(SharedData
{aEventSource
}, "EventSourceImpl::mSharedData"),
382 mCookieJarSettings(aCookieJarSettings
),
383 mTargetThread(NS_GetCurrentThread()) {
384 MOZ_ASSERT(aEventSource
);
385 SetReadyState(CONNECTING
);
388 class CleanupRunnable final
: public WorkerMainThreadRunnable
{
390 explicit CleanupRunnable(RefPtr
<EventSourceImpl
>&& aEventSourceImpl
)
391 : WorkerMainThreadRunnable(GetCurrentThreadWorkerPrivate(),
392 "EventSource :: Cleanup"_ns
),
393 mESImpl(std::move(aEventSourceImpl
)) {
395 mWorkerPrivate
->AssertIsOnWorkerThread();
398 bool MainThreadRun() override
{
400 mESImpl
->CleanupOnMainThread();
401 // We want to ensure the shortest possible remaining lifetime
402 // and not depend on the Runnable's destruction.
408 RefPtr
<EventSourceImpl
> mESImpl
;
411 void EventSourceImpl::Close() {
416 SetReadyState(CLOSED
);
417 // CloseInternal potentially kills ourself, ensure
418 // to not access any members afterwards.
422 void EventSourceImpl::CloseInternal() {
423 AssertIsOnTargetThread();
424 MOZ_ASSERT(IsClosed());
426 RefPtr
<EventSource
> myES
;
428 auto lock
= mSharedData
.Lock();
429 // We want to ensure to release ourself even if we have
430 // the shutdown case, thus we put aside a pointer
431 // to the EventSource and null it out right now.
432 myES
= std::move(lock
->mEventSource
);
433 lock
->mEventSource
= nullptr;
434 lock
->mServiceNotifier
= nullptr;
437 MOZ_ASSERT(!mIsShutDown
);
442 // Invoke CleanupOnMainThread before cleaning any members. It will call
443 // ShutDown, which is supposed to be called before cleaning any members.
444 if (NS_IsMainThread()) {
445 CleanupOnMainThread();
448 // run CleanupOnMainThread synchronously on main thread since it touches
449 // observers and members only can be accessed on main thread.
450 RefPtr
<CleanupRunnable
> runnable
= new CleanupRunnable(this);
451 runnable
->Dispatch(Killing
, rv
);
452 MOZ_ASSERT(!rv
.Failed());
456 while (mMessagesToDispatch
.GetSize() != 0) {
457 delete mMessagesToDispatch
.PopFront();
461 mUnicodeDecoder
= nullptr;
462 // Release the object on its owner. Don't access to any members
464 myES
->mESImpl
= nullptr;
467 void EventSourceImpl::CleanupOnMainThread() {
468 AssertIsOnMainThread();
469 MOZ_ASSERT(IsClosed());
471 // Call ShutDown before cleaning any members.
472 MOZ_ASSERT(!mIsShutDown
);
476 RemoveWindowObservers();
485 mPrincipal
= nullptr;
489 class InitRunnable final
: public WorkerMainThreadRunnable
{
491 InitRunnable(WorkerPrivate
* aWorkerPrivate
,
492 RefPtr
<EventSourceImpl
> aEventSourceImpl
, const nsAString
& aURL
)
493 : WorkerMainThreadRunnable(aWorkerPrivate
, "EventSource :: Init"_ns
),
494 mESImpl(std::move(aEventSourceImpl
)),
496 mRv(NS_ERROR_NOT_INITIALIZED
) {
497 MOZ_ASSERT(aWorkerPrivate
);
498 aWorkerPrivate
->AssertIsOnWorkerThread();
502 bool MainThreadRun() override
{
503 // Get principal from worker's owner document or from worker.
504 WorkerPrivate
* wp
= mWorkerPrivate
;
505 while (wp
->GetParent()) {
506 wp
= wp
->GetParent();
508 nsPIDOMWindowInner
* window
= wp
->GetWindow();
509 Document
* doc
= window
? window
->GetExtantDoc() : nullptr;
510 nsCOMPtr
<nsIPrincipal
> principal
=
511 doc
? doc
->NodePrincipal() : wp
->GetPrincipal();
513 mRv
= NS_ERROR_FAILURE
;
517 mESImpl
->Init(principal
, mURL
, rv
);
518 mRv
= rv
.StealNSResult();
520 // We want to ensure that EventSourceImpl's lifecycle
521 // does not depend on this Runnable's one.
527 nsresult
ErrorCode() const { return mRv
; }
530 RefPtr
<EventSourceImpl
> mESImpl
;
531 const nsAString
& mURL
;
535 class ConnectRunnable final
: public WorkerMainThreadRunnable
{
537 explicit ConnectRunnable(WorkerPrivate
* aWorkerPrivate
,
538 RefPtr
<EventSourceImpl
> aEventSourceImpl
)
539 : WorkerMainThreadRunnable(aWorkerPrivate
, "EventSource :: Connect"_ns
),
540 mESImpl(std::move(aEventSourceImpl
)) {
541 MOZ_ASSERT(aWorkerPrivate
);
542 aWorkerPrivate
->AssertIsOnWorkerThread();
546 bool MainThreadRun() override
{
548 // We are allowed to access the event target since this runnable is
549 // synchronized with the thread the event target lives on.
550 mESImpl
->InitChannelAndRequestEventSource(true);
551 // We want to ensure the shortest possible remaining lifetime
552 // and not depend on the Runnable's destruction.
558 RefPtr
<EventSourceImpl
> mESImpl
;
561 nsresult
EventSourceImpl::ParseURL(const nsAString
& aURL
) {
562 AssertIsOnMainThread();
563 MOZ_ASSERT(!mIsShutDown
);
565 nsCOMPtr
<nsIURI
> baseURI
;
566 nsresult rv
= GetBaseURI(getter_AddRefs(baseURI
));
567 NS_ENSURE_SUCCESS(rv
, rv
);
569 nsCOMPtr
<nsIURI
> srcURI
;
570 rv
= NS_NewURI(getter_AddRefs(srcURI
), aURL
, nullptr, baseURI
);
571 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SYNTAX_ERR
);
574 rv
= nsContentUtils::GetUTFOrigin(srcURI
, origin
);
575 NS_ENSURE_SUCCESS(rv
, rv
);
578 rv
= srcURI
->GetSpec(spec
);
579 NS_ENSURE_SUCCESS(rv
, rv
);
581 // This assignment doesn't require extra synchronization because this function
582 // is only ever called from EventSourceImpl::Init(), which is either called
583 // directly if mEventSource was created on the main thread, or via a
584 // synchronous runnable if it was created on a worker thread.
586 // We can't use GetEventSource() here because it would modify the refcount,
587 // and that's not allowed off the owning thread.
588 auto lock
= mSharedData
.Lock();
589 lock
->mEventSource
->mOriginalURL
= NS_ConvertUTF8toUTF16(spec
);
596 nsresult
EventSourceImpl::AddWindowObservers() {
597 AssertIsOnMainThread();
598 MOZ_ASSERT(mIsMainThread
);
599 MOZ_ASSERT(!mIsShutDown
);
600 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
603 nsresult rv
= os
->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC
, true);
604 NS_ENSURE_SUCCESS(rv
, rv
);
605 rv
= os
->AddObserver(this, DOM_WINDOW_FROZEN_TOPIC
, true);
606 NS_ENSURE_SUCCESS(rv
, rv
);
607 rv
= os
->AddObserver(this, DOM_WINDOW_THAWED_TOPIC
, true);
608 NS_ENSURE_SUCCESS(rv
, rv
);
612 void EventSourceImpl::RemoveWindowObservers() {
613 AssertIsOnMainThread();
614 MOZ_ASSERT(mIsMainThread
);
615 MOZ_ASSERT(IsClosed());
616 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
618 os
->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC
);
619 os
->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC
);
620 os
->RemoveObserver(this, DOM_WINDOW_THAWED_TOPIC
);
624 void EventSourceImpl::Init(nsIPrincipal
* aPrincipal
, const nsAString
& aURL
,
626 AssertIsOnMainThread();
627 MOZ_ASSERT(aPrincipal
);
628 MOZ_ASSERT(ReadyState() == CONNECTING
);
629 mPrincipal
= aPrincipal
;
630 aRv
= ParseURL(aURL
);
631 if (NS_WARN_IF(aRv
.Failed())) {
634 // The conditional here is historical and not necessarily sane.
635 if (JSContext
* cx
= nsContentUtils::GetCurrentJSContext()) {
636 nsJSUtils::GetCallingLocation(cx
, mScriptFile
, &mScriptLine
,
638 mInnerWindowID
= nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx
);
642 // we observe when the window freezes and thaws
643 aRv
= AddWindowObservers();
644 if (NS_WARN_IF(aRv
.Failed())) {
650 Preferences::GetInt("dom.server-events.default-reconnection-time",
651 DEFAULT_RECONNECTION_TIME_VALUE
);
653 mUnicodeDecoder
= UTF_8_ENCODING
->NewDecoderWithBOMRemoval();
656 //-----------------------------------------------------------------------------
657 // EventSourceImpl::nsIObserver
658 //-----------------------------------------------------------------------------
661 EventSourceImpl::Observe(nsISupports
* aSubject
, const char* aTopic
,
662 const char16_t
* aData
) {
663 AssertIsOnMainThread();
668 nsCOMPtr
<nsPIDOMWindowInner
> window
= do_QueryInterface(aSubject
);
669 MOZ_ASSERT(mIsMainThread
);
671 auto lock
= mSharedData
.Lock();
672 if (!lock
->mEventSource
->GetOwner() ||
673 window
!= lock
->mEventSource
->GetOwner()) {
678 DebugOnly
<nsresult
> rv
;
679 if (strcmp(aTopic
, DOM_WINDOW_FROZEN_TOPIC
) == 0) {
681 MOZ_ASSERT(NS_SUCCEEDED(rv
), "Freeze() failed");
682 } else if (strcmp(aTopic
, DOM_WINDOW_THAWED_TOPIC
) == 0) {
684 MOZ_ASSERT(NS_SUCCEEDED(rv
), "Thaw() failed");
685 } else if (strcmp(aTopic
, DOM_WINDOW_DESTROYED_TOPIC
) == 0) {
692 //-----------------------------------------------------------------------------
693 // EventSourceImpl::nsIStreamListener
694 //-----------------------------------------------------------------------------
697 EventSourceImpl::OnStartRequest(nsIRequest
* aRequest
) {
698 AssertIsOnMainThread();
700 return NS_ERROR_ABORT
;
702 nsresult rv
= CheckHealthOfRequestCallback(aRequest
);
703 NS_ENSURE_SUCCESS(rv
, rv
);
705 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aRequest
, &rv
);
706 NS_ENSURE_SUCCESS(rv
, rv
);
709 rv
= aRequest
->GetStatus(&status
);
710 NS_ENSURE_SUCCESS(rv
, rv
);
712 if (NS_FAILED(status
)) {
713 // EventSource::OnStopRequest will evaluate if it shall either reestablish
714 // or fail the connection
715 return NS_ERROR_ABORT
;
719 rv
= httpChannel
->GetResponseStatus(&httpStatus
);
720 NS_ENSURE_SUCCESS(rv
, rv
);
722 if (httpStatus
!= 200) {
723 DispatchFailConnection();
724 return NS_ERROR_ABORT
;
727 nsAutoCString contentType
;
728 rv
= httpChannel
->GetContentType(contentType
);
729 NS_ENSURE_SUCCESS(rv
, rv
);
731 if (!contentType
.EqualsLiteral(TEXT_EVENT_STREAM
)) {
732 DispatchFailConnection();
733 return NS_ERROR_ABORT
;
736 if (!mIsMainThread
) {
737 // Try to retarget to worker thread, otherwise fall back to main thread.
738 nsCOMPtr
<nsIThreadRetargetableRequest
> rr
= do_QueryInterface(httpChannel
);
740 rv
= rr
->RetargetDeliveryTo(this);
741 if (NS_WARN_IF(NS_FAILED(rv
))) {
742 NS_WARNING("Retargeting failed");
748 auto lock
= mSharedData
.Lock();
749 lock
->mServiceNotifier
= MakeUnique
<EventSourceServiceNotifier
>(
750 this, mHttpChannel
->ChannelId(), mInnerWindowID
);
752 rv
= Dispatch(NewRunnableMethod("dom::EventSourceImpl::AnnounceConnection",
753 this, &EventSourceImpl::AnnounceConnection
),
755 NS_ENSURE_SUCCESS(rv
, rv
);
756 mStatus
= PARSE_STATE_BEGIN_OF_STREAM
;
760 // this method parses the characters as they become available instead of
762 nsresult
EventSourceImpl::StreamReaderFunc(nsIInputStream
* aInputStream
,
764 const char* aFromRawSegment
,
765 uint32_t aToOffset
, uint32_t aCount
,
766 uint32_t* aWriteCount
) {
767 // The EventSourceImpl instance is hold alive on the
768 // synchronously calling stack, so raw pointer is fine here.
769 EventSourceImpl
* thisObject
= static_cast<EventSourceImpl
*>(aClosure
);
770 if (!thisObject
|| !aWriteCount
) {
772 "EventSource cannot read from stream: no aClosure or aWriteCount");
773 return NS_ERROR_FAILURE
;
775 thisObject
->AssertIsOnTargetThread();
776 MOZ_ASSERT(!thisObject
->mIsShutDown
);
777 thisObject
->ParseSegment((const char*)aFromRawSegment
, aCount
);
778 *aWriteCount
= aCount
;
782 void EventSourceImpl::ParseSegment(const char* aBuffer
, uint32_t aLength
) {
783 AssertIsOnTargetThread();
787 char16_t buffer
[1024];
788 auto dst
= Span(buffer
);
789 auto src
= AsBytes(Span(aBuffer
, aLength
));
790 // XXX EOF handling is https://bugzilla.mozilla.org/show_bug.cgi?id=1369018
795 std::tie(result
, read
, written
, std::ignore
) =
796 mUnicodeDecoder
->DecodeToUTF16(src
, dst
, false);
797 for (auto c
: dst
.To(written
)) {
798 nsresult rv
= ParseCharacter(c
);
799 NS_ENSURE_SUCCESS_VOID(rv
);
801 if (result
== kInputEmpty
) {
804 src
= src
.From(read
);
809 EventSourceImpl::OnDataAvailable(nsIRequest
* aRequest
,
810 nsIInputStream
* aInputStream
, uint64_t aOffset
,
812 AssertIsOnTargetThread();
813 NS_ENSURE_ARG_POINTER(aInputStream
);
815 return NS_ERROR_ABORT
;
818 nsresult rv
= CheckHealthOfRequestCallback(aRequest
);
819 NS_ENSURE_SUCCESS(rv
, rv
);
822 return aInputStream
->ReadSegments(EventSourceImpl::StreamReaderFunc
, this,
827 EventSourceImpl::OnStopRequest(nsIRequest
* aRequest
, nsresult aStatusCode
) {
828 AssertIsOnMainThread();
831 return NS_ERROR_ABORT
;
834 // "Network errors that prevents the connection from being established in the
835 // first place (e.g. DNS errors), must cause the user agent to asynchronously
836 // reestablish the connection.
838 // (...) the cancelation of the fetch algorithm by the user agent (e.g. in
839 // response to window.stop() or the user canceling the network connection
840 // manually) must cause the user agent to fail the connection.
841 // There could be additional network errors that are not covered in the above
844 if (NS_FAILED(aStatusCode
) && aStatusCode
!= NS_ERROR_CONNECTION_REFUSED
&&
845 aStatusCode
!= NS_ERROR_NET_TIMEOUT
&&
846 aStatusCode
!= NS_ERROR_NET_RESET
&&
847 aStatusCode
!= NS_ERROR_NET_INTERRUPT
&&
848 aStatusCode
!= NS_ERROR_NET_PARTIAL_TRANSFER
&&
849 aStatusCode
!= NS_ERROR_NET_TIMEOUT_EXTERNAL
&&
850 aStatusCode
!= NS_ERROR_PROXY_CONNECTION_REFUSED
&&
851 aStatusCode
!= NS_ERROR_DNS_LOOKUP_QUEUE_FULL
) {
852 DispatchFailConnection();
853 return NS_ERROR_ABORT
;
856 nsresult rv
= CheckHealthOfRequestCallback(aRequest
);
857 NS_ENSURE_SUCCESS(rv
, rv
);
860 Dispatch(NewRunnableMethod("dom::EventSourceImpl::ReestablishConnection",
861 this, &EventSourceImpl::ReestablishConnection
),
863 NS_ENSURE_SUCCESS(rv
, rv
);
868 //-----------------------------------------------------------------------------
869 // EventSourceImpl::nsIChannelEventSink
870 //-----------------------------------------------------------------------------
873 EventSourceImpl::AsyncOnChannelRedirect(
874 nsIChannel
* aOldChannel
, nsIChannel
* aNewChannel
, uint32_t aFlags
,
875 nsIAsyncVerifyRedirectCallback
* aCallback
) {
876 AssertIsOnMainThread();
878 return NS_ERROR_ABORT
;
880 nsCOMPtr
<nsIRequest
> aOldRequest
= aOldChannel
;
881 MOZ_ASSERT(aOldRequest
, "Redirect from a null request?");
883 nsresult rv
= CheckHealthOfRequestCallback(aOldRequest
);
884 NS_ENSURE_SUCCESS(rv
, rv
);
886 MOZ_ASSERT(aNewChannel
, "Redirect without a channel?");
888 nsCOMPtr
<nsIURI
> newURI
;
889 rv
= NS_GetFinalChannelURI(aNewChannel
, getter_AddRefs(newURI
));
890 NS_ENSURE_SUCCESS(rv
, rv
);
892 bool isValidScheme
= newURI
->SchemeIs("http") || newURI
->SchemeIs("https");
895 mIsMainThread
? GetEventSource()->CheckCurrentGlobalCorrectness() : NS_OK
;
896 if (NS_FAILED(rv
) || !isValidScheme
) {
897 DispatchFailConnection();
898 return NS_ERROR_DOM_SECURITY_ERR
;
901 // update our channel
903 mHttpChannel
= do_QueryInterface(aNewChannel
);
904 NS_ENSURE_STATE(mHttpChannel
);
907 // The HTTP impl already copies over the referrer info on
908 // redirects, so we don't need to SetupReferrerInfo().
910 if ((aFlags
& nsIChannelEventSink::REDIRECT_PERMANENT
) != 0) {
911 rv
= NS_GetFinalChannelURI(mHttpChannel
, getter_AddRefs(mSrc
));
912 NS_ENSURE_SUCCESS(rv
, rv
);
915 aCallback
->OnRedirectVerifyCallback(NS_OK
);
920 //-----------------------------------------------------------------------------
921 // EventSourceImpl::nsIInterfaceRequestor
922 //-----------------------------------------------------------------------------
925 EventSourceImpl::GetInterface(const nsIID
& aIID
, void** aResult
) {
926 AssertIsOnMainThread();
929 return NS_ERROR_FAILURE
;
932 if (aIID
.Equals(NS_GET_IID(nsIChannelEventSink
))) {
933 *aResult
= static_cast<nsIChannelEventSink
*>(this);
938 if (aIID
.Equals(NS_GET_IID(nsIAuthPrompt
)) ||
939 aIID
.Equals(NS_GET_IID(nsIAuthPrompt2
))) {
941 nsCOMPtr
<nsIPromptFactory
> wwatch
=
942 do_GetService(NS_WINDOWWATCHER_CONTRACTID
, &rv
);
943 NS_ENSURE_SUCCESS(rv
, rv
);
945 nsCOMPtr
<nsPIDOMWindowOuter
> window
;
947 // To avoid a data race we may only access the event target if it lives on
950 auto lock
= mSharedData
.Lock();
951 rv
= lock
->mEventSource
->CheckCurrentGlobalCorrectness();
952 NS_ENSURE_SUCCESS(rv
, NS_ERROR_UNEXPECTED
);
954 if (lock
->mEventSource
->GetOwner()) {
955 window
= lock
->mEventSource
->GetOwner()->GetOuterWindow();
959 // Get the an auth prompter for our window so that the parenting
960 // of the dialogs works as it should when using tabs.
962 return wwatch
->GetPrompt(window
, aIID
, aResult
);
965 return QueryInterface(aIID
, aResult
);
969 EventSourceImpl::IsOnCurrentThread(bool* aResult
) {
970 *aResult
= IsTargetThread();
975 EventSourceImpl::IsOnCurrentThreadInfallible() { return IsTargetThread(); }
977 nsresult
EventSourceImpl::GetBaseURI(nsIURI
** aBaseURI
) {
978 AssertIsOnMainThread();
979 MOZ_ASSERT(!mIsShutDown
);
980 NS_ENSURE_ARG_POINTER(aBaseURI
);
984 nsCOMPtr
<nsIURI
> baseURI
;
986 // first we try from document->GetBaseURI()
987 nsCOMPtr
<Document
> doc
=
988 mIsMainThread
? GetEventSource()->GetDocumentIfCurrent() : nullptr;
990 baseURI
= doc
->GetBaseURI();
993 // otherwise we get from the doc's principal
995 auto* basePrin
= BasePrincipal::Cast(mPrincipal
);
996 nsresult rv
= basePrin
->GetURI(getter_AddRefs(baseURI
));
997 NS_ENSURE_SUCCESS(rv
, rv
);
1000 NS_ENSURE_STATE(baseURI
);
1002 baseURI
.forget(aBaseURI
);
1006 void EventSourceImpl::SetupHttpChannel() {
1007 AssertIsOnMainThread();
1008 MOZ_ASSERT(!mIsShutDown
);
1009 nsresult rv
= mHttpChannel
->SetRequestMethod("GET"_ns
);
1010 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1012 /* set the http request headers */
1014 rv
= mHttpChannel
->SetRequestHeader(
1015 "Accept"_ns
, nsLiteralCString(TEXT_EVENT_STREAM
), false);
1016 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1018 // LOAD_BYPASS_CACHE already adds the Cache-Control: no-cache header
1020 if (mLastEventID
.IsEmpty()) {
1023 NS_ConvertUTF16toUTF8
eventId(mLastEventID
);
1024 rv
= mHttpChannel
->SetRequestHeader("Last-Event-ID"_ns
, eventId
, false);
1026 if (NS_FAILED(rv
)) {
1027 MOZ_LOG(gEventSourceLog
, LogLevel::Warning
,
1028 ("SetupHttpChannel. rv=%x (%s)", uint32_t(rv
), eventId
.get()));
1034 nsresult
EventSourceImpl::SetupReferrerInfo(
1035 const nsCOMPtr
<Document
>& aDocument
) {
1036 AssertIsOnMainThread();
1037 MOZ_ASSERT(!mIsShutDown
);
1040 auto referrerInfo
= MakeRefPtr
<ReferrerInfo
>(*aDocument
);
1041 nsresult rv
= mHttpChannel
->SetReferrerInfoWithoutClone(referrerInfo
);
1042 NS_ENSURE_SUCCESS(rv
, rv
);
1048 nsresult
EventSourceImpl::InitChannelAndRequestEventSource(
1049 const bool aEventTargetAccessAllowed
) {
1050 AssertIsOnMainThread();
1052 return NS_ERROR_ABORT
;
1055 bool isValidScheme
= mSrc
->SchemeIs("http") || mSrc
->SchemeIs("https");
1057 MOZ_ASSERT_IF(mIsMainThread
, aEventTargetAccessAllowed
);
1059 nsresult rv
= aEventTargetAccessAllowed
1061 // We can't call GetEventSource() because we're not
1062 // allowed to touch the refcount off the worker thread
1063 // due to an assertion, event if it would have otherwise
1065 auto lock
= mSharedData
.Lock();
1066 return lock
->mEventSource
->CheckCurrentGlobalCorrectness();
1069 if (NS_FAILED(rv
) || !isValidScheme
) {
1070 DispatchFailConnection();
1071 return NS_ERROR_DOM_SECURITY_ERR
;
1074 nsCOMPtr
<Document
> doc
;
1075 nsSecurityFlags securityFlags
=
1076 nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT
;
1078 auto lock
= mSharedData
.Lock();
1079 doc
= aEventTargetAccessAllowed
? lock
->mEventSource
->GetDocumentIfCurrent()
1082 if (lock
->mEventSource
->mWithCredentials
) {
1083 securityFlags
|= nsILoadInfo::SEC_COOKIES_INCLUDE
;
1087 // The html spec requires we use fetch cache mode of "no-store". This
1088 // maps to LOAD_BYPASS_CACHE and LOAD_INHIBIT_CACHING in necko.
1089 nsLoadFlags loadFlags
;
1090 loadFlags
= nsIRequest::LOAD_BACKGROUND
| nsIRequest::LOAD_BYPASS_CACHE
|
1091 nsIRequest::INHIBIT_CACHING
;
1093 nsCOMPtr
<nsIChannel
> channel
;
1094 // If we have the document, use it
1096 MOZ_ASSERT(mCookieJarSettings
== doc
->CookieJarSettings());
1098 nsCOMPtr
<nsILoadGroup
> loadGroup
= doc
->GetDocumentLoadGroup();
1099 rv
= NS_NewChannel(getter_AddRefs(channel
), mSrc
, doc
, securityFlags
,
1100 nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE
,
1101 nullptr, // aPerformanceStorage
1103 nullptr, // aCallbacks
1104 loadFlags
); // aLoadFlags
1106 // otherwise use the principal
1107 rv
= NS_NewChannel(getter_AddRefs(channel
), mSrc
, mPrincipal
, securityFlags
,
1108 nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE
,
1110 nullptr, // aPerformanceStorage
1111 nullptr, // loadGroup
1112 nullptr, // aCallbacks
1113 loadFlags
); // aLoadFlags
1116 NS_ENSURE_SUCCESS(rv
, rv
);
1118 mHttpChannel
= do_QueryInterface(channel
);
1119 NS_ENSURE_TRUE(mHttpChannel
, NS_ERROR_NO_INTERFACE
);
1122 rv
= SetupReferrerInfo(doc
);
1123 NS_ENSURE_SUCCESS(rv
, rv
);
1127 nsCOMPtr
<nsIInterfaceRequestor
> notificationCallbacks
;
1128 mHttpChannel
->GetNotificationCallbacks(
1129 getter_AddRefs(notificationCallbacks
));
1130 MOZ_ASSERT(!notificationCallbacks
);
1134 mHttpChannel
->SetNotificationCallbacks(this);
1136 // Start reading from the channel
1137 rv
= mHttpChannel
->AsyncOpen(this);
1138 if (NS_FAILED(rv
)) {
1139 DispatchFailConnection();
1146 void EventSourceImpl::AnnounceConnection() {
1147 AssertIsOnTargetThread();
1148 if (ReadyState() != CONNECTING
) {
1149 NS_WARNING("Unexpected mReadyState!!!");
1154 auto lock
= mSharedData
.Lock();
1155 if (lock
->mServiceNotifier
) {
1156 lock
->mServiceNotifier
->ConnectionOpened();
1160 // When a user agent is to announce the connection, the user agent must set
1161 // the readyState attribute to OPEN and queue a task to fire a simple event
1162 // named open at the EventSource object.
1164 SetReadyState(OPEN
);
1166 nsresult rv
= GetEventSource()->CheckCurrentGlobalCorrectness();
1167 if (NS_FAILED(rv
)) {
1170 // We can't hold the mutex while dispatching the event because the mutex is
1171 // not reentrant, and content might call back into our code.
1172 rv
= GetEventSource()->CreateAndDispatchSimpleEvent(u
"open"_ns
);
1173 if (NS_FAILED(rv
)) {
1174 NS_WARNING("Failed to dispatch the error event!!!");
1179 nsresult
EventSourceImpl::ResetConnection() {
1180 AssertIsOnMainThread();
1182 mHttpChannel
->Cancel(NS_ERROR_ABORT
);
1183 mHttpChannel
= nullptr;
1188 void EventSourceImpl::ResetDecoder() {
1189 AssertIsOnTargetThread();
1190 if (mUnicodeDecoder
) {
1191 UTF_8_ENCODING
->NewDecoderWithBOMRemovalInto(*mUnicodeDecoder
);
1193 mStatus
= PARSE_STATE_OFF
;
1197 class CallRestartConnection final
: public WorkerMainThreadRunnable
{
1199 explicit CallRestartConnection(RefPtr
<EventSourceImpl
>&& aEventSourceImpl
)
1200 : WorkerMainThreadRunnable(aEventSourceImpl
->mWorkerRef
->Private(),
1201 "EventSource :: RestartConnection"_ns
),
1202 mESImpl(std::move(aEventSourceImpl
)) {
1203 mWorkerPrivate
->AssertIsOnWorkerThread();
1204 MOZ_ASSERT(mESImpl
);
1207 bool MainThreadRun() override
{
1208 MOZ_ASSERT(mESImpl
);
1209 mESImpl
->RestartConnection();
1210 // We want to ensure the shortest possible remaining lifetime
1211 // and not depend on the Runnable's destruction.
1217 RefPtr
<EventSourceImpl
> mESImpl
;
1220 nsresult
EventSourceImpl::RestartConnection() {
1221 AssertIsOnMainThread();
1223 return NS_ERROR_ABORT
;
1226 nsresult rv
= ResetConnection();
1227 NS_ENSURE_SUCCESS(rv
, rv
);
1228 rv
= SetReconnectionTimeout();
1229 NS_ENSURE_SUCCESS(rv
, rv
);
1233 void EventSourceImpl::ReestablishConnection() {
1234 AssertIsOnTargetThread();
1240 if (mIsMainThread
) {
1241 rv
= RestartConnection();
1243 RefPtr
<CallRestartConnection
> runnable
= new CallRestartConnection(this);
1245 runnable
->Dispatch(Canceling
, result
);
1246 MOZ_ASSERT(!result
.Failed());
1247 rv
= result
.StealNSResult();
1249 if (NS_FAILED(rv
)) {
1253 rv
= GetEventSource()->CheckCurrentGlobalCorrectness();
1254 if (NS_FAILED(rv
)) {
1258 SetReadyState(CONNECTING
);
1260 // We can't hold the mutex while dispatching the event because the mutex is
1261 // not reentrant, and content might call back into our code.
1262 rv
= GetEventSource()->CreateAndDispatchSimpleEvent(u
"error"_ns
);
1263 if (NS_FAILED(rv
)) {
1264 NS_WARNING("Failed to dispatch the error event!!!");
1269 nsresult
EventSourceImpl::SetReconnectionTimeout() {
1270 AssertIsOnMainThread();
1272 return NS_ERROR_ABORT
;
1275 // the timer will be used whenever the requests are going finished.
1277 mTimer
= NS_NewTimer();
1278 NS_ENSURE_STATE(mTimer
);
1281 MOZ_TRY(mTimer
->InitWithCallback(this, mReconnectionTime
,
1282 nsITimer::TYPE_ONE_SHOT
));
1287 nsresult
EventSourceImpl::PrintErrorOnConsole(
1288 const char* aBundleURI
, const char* aError
,
1289 const nsTArray
<nsString
>& aFormatStrings
) {
1290 AssertIsOnMainThread();
1291 MOZ_ASSERT(!mIsShutDown
);
1292 nsCOMPtr
<nsIStringBundleService
> bundleService
=
1293 mozilla::components::StringBundle::Service();
1294 NS_ENSURE_STATE(bundleService
);
1296 nsCOMPtr
<nsIStringBundle
> strBundle
;
1298 bundleService
->CreateBundle(aBundleURI
, getter_AddRefs(strBundle
));
1299 NS_ENSURE_SUCCESS(rv
, rv
);
1301 nsCOMPtr
<nsIConsoleService
> console(
1302 do_GetService(NS_CONSOLESERVICE_CONTRACTID
, &rv
));
1303 NS_ENSURE_SUCCESS(rv
, rv
);
1305 nsCOMPtr
<nsIScriptError
> errObj(
1306 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID
, &rv
));
1307 NS_ENSURE_SUCCESS(rv
, rv
);
1309 // Localize the error message
1310 nsAutoString message
;
1311 if (!aFormatStrings
.IsEmpty()) {
1312 rv
= strBundle
->FormatStringFromName(aError
, aFormatStrings
, message
);
1314 rv
= strBundle
->GetStringFromName(aError
, message
);
1316 NS_ENSURE_SUCCESS(rv
, rv
);
1318 rv
= errObj
->InitWithWindowID(message
, mScriptFile
, u
""_ns
, mScriptLine
,
1319 mScriptColumn
, nsIScriptError::errorFlag
,
1320 "Event Source", mInnerWindowID
);
1321 NS_ENSURE_SUCCESS(rv
, rv
);
1323 // print the error message directly to the JS console
1324 rv
= console
->LogMessage(errObj
);
1325 NS_ENSURE_SUCCESS(rv
, rv
);
1330 nsresult
EventSourceImpl::ConsoleError() {
1331 AssertIsOnMainThread();
1332 MOZ_ASSERT(!mIsShutDown
);
1333 nsAutoCString targetSpec
;
1334 nsresult rv
= mSrc
->GetSpec(targetSpec
);
1335 NS_ENSURE_SUCCESS(rv
, rv
);
1337 AutoTArray
<nsString
, 1> formatStrings
;
1338 CopyUTF8toUTF16(targetSpec
, *formatStrings
.AppendElement());
1340 if (ReadyState() == CONNECTING
) {
1341 rv
= PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
1342 "connectionFailure", formatStrings
);
1344 rv
= PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
1345 "netInterrupt", formatStrings
);
1347 NS_ENSURE_SUCCESS(rv
, rv
);
1352 void EventSourceImpl::DispatchFailConnection() {
1353 AssertIsOnMainThread();
1357 nsresult rv
= ConsoleError();
1358 if (NS_FAILED(rv
)) {
1359 NS_WARNING("Failed to print to the console error");
1361 rv
= Dispatch(NewRunnableMethod("dom::EventSourceImpl::FailConnection", this,
1362 &EventSourceImpl::FailConnection
),
1363 NS_DISPATCH_NORMAL
);
1364 if (NS_WARN_IF(NS_FAILED(rv
))) {
1365 // if the worker is shutting down, the dispatching of normal WorkerRunnables
1371 void EventSourceImpl::FailConnection() {
1372 AssertIsOnTargetThread();
1376 // Must change state to closed before firing event to content.
1377 SetReadyState(CLOSED
);
1378 // When a user agent is to fail the connection, the user agent must set the
1379 // readyState attribute to CLOSED and queue a task to fire a simple event
1380 // named error at the EventSource object.
1381 nsresult rv
= GetEventSource()->CheckCurrentGlobalCorrectness();
1382 if (NS_SUCCEEDED(rv
)) {
1383 // We can't hold the mutex while dispatching the event because the mutex
1384 // is not reentrant, and content might call back into our code.
1385 rv
= GetEventSource()->CreateAndDispatchSimpleEvent(u
"error"_ns
);
1386 if (NS_FAILED(rv
)) {
1387 NS_WARNING("Failed to dispatch the error event!!!");
1390 // Call CloseInternal in the end of function because it may release
1395 NS_IMETHODIMP
EventSourceImpl::Notify(nsITimer
* aTimer
) {
1396 AssertIsOnMainThread();
1401 MOZ_ASSERT(!mHttpChannel
, "the channel hasn't been cancelled!!");
1404 nsresult rv
= InitChannelAndRequestEventSource(mIsMainThread
);
1405 if (NS_FAILED(rv
)) {
1406 NS_WARNING("InitChannelAndRequestEventSource() failed");
1412 NS_IMETHODIMP
EventSourceImpl::GetName(nsACString
& aName
) {
1413 aName
.AssignLiteral("EventSourceImpl");
1417 nsresult
EventSourceImpl::Thaw() {
1418 AssertIsOnMainThread();
1419 if (IsClosed() || !mFrozen
) {
1423 MOZ_ASSERT(!mHttpChannel
, "the connection hasn't been closed!!!");
1427 if (!mGoingToDispatchAllMessages
&& mMessagesToDispatch
.GetSize() > 0) {
1428 nsCOMPtr
<nsIRunnable
> event
=
1429 NewRunnableMethod("dom::EventSourceImpl::DispatchAllMessageEvents",
1430 this, &EventSourceImpl::DispatchAllMessageEvents
);
1431 NS_ENSURE_STATE(event
);
1433 mGoingToDispatchAllMessages
= true;
1435 rv
= Dispatch(event
.forget(), NS_DISPATCH_NORMAL
);
1436 NS_ENSURE_SUCCESS(rv
, rv
);
1439 rv
= InitChannelAndRequestEventSource(mIsMainThread
);
1440 NS_ENSURE_SUCCESS(rv
, rv
);
1445 nsresult
EventSourceImpl::Freeze() {
1446 AssertIsOnMainThread();
1447 if (IsClosed() || mFrozen
) {
1451 MOZ_ASSERT(!mHttpChannel
, "the connection hasn't been closed!!!");
1456 nsresult
EventSourceImpl::DispatchCurrentMessageEvent() {
1457 AssertIsOnTargetThread();
1458 MOZ_ASSERT(!mIsShutDown
);
1459 UniquePtr
<Message
> message(std::move(mCurrentMessage
));
1462 if (!message
|| message
->mData
.IsEmpty()) {
1466 // removes the trailing LF from mData
1467 MOZ_ASSERT(message
->mData
.CharAt(message
->mData
.Length() - 1) == LF_CHAR
,
1468 "Invalid trailing character! LF was expected instead.");
1469 message
->mData
.SetLength(message
->mData
.Length() - 1);
1471 if (message
->mEventName
.IsEmpty()) {
1472 message
->mEventName
.AssignLiteral("message");
1475 mMessagesToDispatch
.Push(message
.release());
1477 if (!mGoingToDispatchAllMessages
) {
1478 nsCOMPtr
<nsIRunnable
> event
=
1479 NewRunnableMethod("dom::EventSourceImpl::DispatchAllMessageEvents",
1480 this, &EventSourceImpl::DispatchAllMessageEvents
);
1481 NS_ENSURE_STATE(event
);
1483 mGoingToDispatchAllMessages
= true;
1485 return Dispatch(event
.forget(), NS_DISPATCH_NORMAL
);
1491 void EventSourceImpl::DispatchAllMessageEvents() {
1492 AssertIsOnTargetThread();
1493 mGoingToDispatchAllMessages
= false;
1495 if (IsClosed() || mFrozen
) {
1502 auto lock
= mSharedData
.Lock();
1503 rv
= lock
->mEventSource
->CheckCurrentGlobalCorrectness();
1504 if (NS_FAILED(rv
)) {
1508 if (NS_WARN_IF(!jsapi
.Init(lock
->mEventSource
->GetOwnerGlobal()))) {
1513 JSContext
* cx
= jsapi
.cx();
1515 while (mMessagesToDispatch
.GetSize() > 0) {
1516 UniquePtr
<Message
> message(mMessagesToDispatch
.PopFront());
1518 if (message
->mLastEventID
.isSome()) {
1519 mLastEventID
.Assign(message
->mLastEventID
.value());
1522 if (message
->mLastEventID
.isNothing() && !mLastEventID
.IsEmpty()) {
1523 message
->mLastEventID
= Some(mLastEventID
);
1527 auto lock
= mSharedData
.Lock();
1528 if (lock
->mServiceNotifier
) {
1529 lock
->mServiceNotifier
->EventReceived(message
->mEventName
, mLastEventID
,
1530 message
->mData
, mReconnectionTime
,
1535 // Now we can turn our string into a jsval
1536 JS::Rooted
<JS::Value
> jsData(cx
);
1539 jsString
= JS_NewUCStringCopyN(cx
, message
->mData
.get(),
1540 message
->mData
.Length());
1541 NS_ENSURE_TRUE_VOID(jsString
);
1543 jsData
.setString(jsString
);
1546 // create an event that uses the MessageEvent interface,
1547 // which does not bubble, is not cancelable, and has no default action
1549 RefPtr
<EventSource
> eventSource
= GetEventSource();
1550 RefPtr
<MessageEvent
> event
=
1551 new MessageEvent(eventSource
, nullptr, nullptr);
1553 event
->InitMessageEvent(nullptr, message
->mEventName
, CanBubble::eNo
,
1554 Cancelable::eNo
, jsData
, mOrigin
, mLastEventID
,
1555 nullptr, Sequence
<OwningNonNull
<MessagePort
>>());
1556 event
->SetTrusted(true);
1558 // We can't hold the mutex while dispatching the event because the mutex is
1559 // not reentrant, and content might call back into our code.
1560 IgnoredErrorResult err
;
1561 eventSource
->DispatchEvent(*event
, err
);
1563 NS_WARNING("Failed to dispatch the message event!!!");
1567 if (IsClosed() || mFrozen
) {
1573 void EventSourceImpl::ClearFields() {
1574 AssertIsOnTargetThread();
1575 mCurrentMessage
= nullptr;
1576 mLastFieldName
.Truncate();
1577 mLastFieldValue
.Truncate();
1580 nsresult
EventSourceImpl::SetFieldAndClear() {
1581 MOZ_ASSERT(!mIsShutDown
);
1582 AssertIsOnTargetThread();
1583 if (mLastFieldName
.IsEmpty()) {
1584 mLastFieldValue
.Truncate();
1587 if (!mCurrentMessage
) {
1588 mCurrentMessage
= MakeUnique
<Message
>();
1590 char16_t first_char
;
1591 first_char
= mLastFieldName
.CharAt(0);
1593 // with no case folding performed
1594 switch (first_char
) {
1596 if (mLastFieldName
.EqualsLiteral("data")) {
1597 // If the field name is "data" append the field value to the data
1598 // buffer, then append a single U+000A LINE FEED (LF) character
1599 // to the data buffer.
1600 mCurrentMessage
->mData
.Append(mLastFieldValue
);
1601 mCurrentMessage
->mData
.Append(LF_CHAR
);
1606 if (mLastFieldName
.EqualsLiteral("event")) {
1607 mCurrentMessage
->mEventName
.Assign(mLastFieldValue
);
1612 if (mLastFieldName
.EqualsLiteral("id")) {
1613 mCurrentMessage
->mLastEventID
= Some(mLastFieldValue
);
1618 if (mLastFieldName
.EqualsLiteral("retry")) {
1619 uint32_t newValue
= 0;
1620 uint32_t i
= 0; // we must ensure that there are only digits
1622 for (i
= 0; i
< mLastFieldValue
.Length(); ++i
) {
1623 if (mLastFieldValue
.CharAt(i
) < (char16_t
)'0' ||
1624 mLastFieldValue
.CharAt(i
) > (char16_t
)'9') {
1628 newValue
= newValue
* 10 + (((uint32_t)mLastFieldValue
.CharAt(i
)) -
1629 ((uint32_t)((char16_t
)'0')));
1633 if (newValue
< MIN_RECONNECTION_TIME_VALUE
) {
1634 mReconnectionTime
= MIN_RECONNECTION_TIME_VALUE
;
1635 } else if (newValue
> MAX_RECONNECTION_TIME_VALUE
) {
1636 mReconnectionTime
= MAX_RECONNECTION_TIME_VALUE
;
1638 mReconnectionTime
= newValue
;
1646 mLastFieldName
.Truncate();
1647 mLastFieldValue
.Truncate();
1652 nsresult
EventSourceImpl::CheckHealthOfRequestCallback(
1653 nsIRequest
* aRequestCallback
) {
1654 // This function could be run on target thread if http channel support
1655 // nsIThreadRetargetableRequest. otherwise, it's run on main thread.
1657 // check if we have been closed or if the request has been canceled
1658 // or if we have been frozen
1659 if (IsClosed() || mFrozen
|| !mHttpChannel
) {
1660 return NS_ERROR_ABORT
;
1663 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aRequestCallback
);
1664 NS_ENSURE_STATE(httpChannel
);
1666 if (httpChannel
!= mHttpChannel
) {
1667 NS_WARNING("wrong channel from request callback");
1668 return NS_ERROR_ABORT
;
1674 nsresult
EventSourceImpl::ParseCharacter(char16_t aChr
) {
1675 AssertIsOnTargetThread();
1679 return NS_ERROR_ABORT
;
1683 case PARSE_STATE_OFF
:
1684 NS_ERROR("Invalid state");
1685 return NS_ERROR_FAILURE
;
1688 case PARSE_STATE_BEGIN_OF_STREAM
:
1689 if (aChr
== CR_CHAR
) {
1690 mStatus
= PARSE_STATE_CR_CHAR
;
1691 } else if (aChr
== LF_CHAR
) {
1692 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1693 } else if (aChr
== COLON_CHAR
) {
1694 mStatus
= PARSE_STATE_COMMENT
;
1696 mLastFieldName
+= aChr
;
1697 mStatus
= PARSE_STATE_FIELD_NAME
;
1701 case PARSE_STATE_CR_CHAR
:
1702 if (aChr
== CR_CHAR
) {
1703 rv
= DispatchCurrentMessageEvent(); // there is an empty line (CRCR)
1704 NS_ENSURE_SUCCESS(rv
, rv
);
1705 } else if (aChr
== LF_CHAR
) {
1706 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1707 } else if (aChr
== COLON_CHAR
) {
1708 mStatus
= PARSE_STATE_COMMENT
;
1710 mLastFieldName
+= aChr
;
1711 mStatus
= PARSE_STATE_FIELD_NAME
;
1716 case PARSE_STATE_COMMENT
:
1717 if (aChr
== CR_CHAR
) {
1718 mStatus
= PARSE_STATE_CR_CHAR
;
1719 } else if (aChr
== LF_CHAR
) {
1720 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1725 case PARSE_STATE_FIELD_NAME
:
1726 if (aChr
== CR_CHAR
) {
1727 rv
= SetFieldAndClear();
1728 NS_ENSURE_SUCCESS(rv
, rv
);
1730 mStatus
= PARSE_STATE_CR_CHAR
;
1731 } else if (aChr
== LF_CHAR
) {
1732 rv
= SetFieldAndClear();
1733 NS_ENSURE_SUCCESS(rv
, rv
);
1735 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1736 } else if (aChr
== COLON_CHAR
) {
1737 mStatus
= PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE
;
1739 mLastFieldName
+= aChr
;
1744 case PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE
:
1745 if (aChr
== CR_CHAR
) {
1746 rv
= SetFieldAndClear();
1747 NS_ENSURE_SUCCESS(rv
, rv
);
1749 mStatus
= PARSE_STATE_CR_CHAR
;
1750 } else if (aChr
== LF_CHAR
) {
1751 rv
= SetFieldAndClear();
1752 NS_ENSURE_SUCCESS(rv
, rv
);
1754 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1755 } else if (aChr
== SPACE_CHAR
) {
1756 mStatus
= PARSE_STATE_FIELD_VALUE
;
1758 mLastFieldValue
+= aChr
;
1759 mStatus
= PARSE_STATE_FIELD_VALUE
;
1764 case PARSE_STATE_FIELD_VALUE
:
1765 if (aChr
== CR_CHAR
) {
1766 rv
= SetFieldAndClear();
1767 NS_ENSURE_SUCCESS(rv
, rv
);
1769 mStatus
= PARSE_STATE_CR_CHAR
;
1770 } else if (aChr
== LF_CHAR
) {
1771 rv
= SetFieldAndClear();
1772 NS_ENSURE_SUCCESS(rv
, rv
);
1774 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1775 } else if (aChr
!= 0) {
1776 // Avoid appending the null char to the field value.
1777 mLastFieldValue
+= aChr
;
1778 } else if (mLastFieldName
.EqualsLiteral("id")) {
1779 // Ignore the whole id field if aChr is null
1780 mStatus
= PARSE_STATE_IGNORE_FIELD_VALUE
;
1781 mLastFieldValue
.Truncate();
1786 case PARSE_STATE_IGNORE_FIELD_VALUE
:
1787 if (aChr
== CR_CHAR
) {
1788 mStatus
= PARSE_STATE_CR_CHAR
;
1789 } else if (aChr
== LF_CHAR
) {
1790 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1794 case PARSE_STATE_BEGIN_OF_LINE
:
1795 if (aChr
== CR_CHAR
) {
1796 rv
= DispatchCurrentMessageEvent(); // there is an empty line
1797 NS_ENSURE_SUCCESS(rv
, rv
);
1799 mStatus
= PARSE_STATE_CR_CHAR
;
1800 } else if (aChr
== LF_CHAR
) {
1801 rv
= DispatchCurrentMessageEvent(); // there is an empty line
1802 NS_ENSURE_SUCCESS(rv
, rv
);
1804 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1805 } else if (aChr
== COLON_CHAR
) {
1806 mStatus
= PARSE_STATE_COMMENT
;
1807 } else if (aChr
!= 0) {
1808 // Avoid appending the null char to the field name.
1809 mLastFieldName
+= aChr
;
1810 mStatus
= PARSE_STATE_FIELD_NAME
;
1821 class WorkerRunnableDispatcher final
: public WorkerRunnable
{
1822 RefPtr
<EventSourceImpl
> mEventSourceImpl
;
1825 WorkerRunnableDispatcher(RefPtr
<EventSourceImpl
>&& aImpl
,
1826 WorkerPrivate
* aWorkerPrivate
,
1827 already_AddRefed
<nsIRunnable
> aEvent
)
1828 : WorkerRunnable(aWorkerPrivate
, WorkerThreadUnchangedBusyCount
),
1829 mEventSourceImpl(std::move(aImpl
)),
1830 mEvent(std::move(aEvent
)) {}
1832 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
1833 aWorkerPrivate
->AssertIsOnWorkerThread();
1834 return !NS_FAILED(mEvent
->Run());
1837 void PostRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
,
1838 bool aRunResult
) override
{
1839 // Ensure we drop the RefPtr on the worker thread
1840 // and to not keep us alive longer than needed.
1841 mEventSourceImpl
= nullptr;
1844 bool PreDispatch(WorkerPrivate
* aWorkerPrivate
) override
{
1845 // We don't call WorkerRunnable::PreDispatch because it would assert the
1846 // wrong thing about which thread we're on. We're on whichever thread the
1847 // channel implementation is running on (probably the main thread or
1848 // transport thread).
1852 void PostDispatch(WorkerPrivate
* aWorkerPrivate
,
1853 bool aDispatchResult
) override
{
1854 // We don't call WorkerRunnable::PostDispatch because it would assert the
1855 // wrong thing about which thread we're on. We're on whichever thread the
1856 // channel implementation is running on (probably the main thread or
1857 // transport thread).
1861 nsCOMPtr
<nsIRunnable
> mEvent
;
1866 bool EventSourceImpl::CreateWorkerRef(WorkerPrivate
* aWorkerPrivate
) {
1867 MOZ_ASSERT(!mWorkerRef
);
1868 MOZ_ASSERT(aWorkerPrivate
);
1869 aWorkerPrivate
->AssertIsOnWorkerThread();
1875 RefPtr
<EventSourceImpl
> self
= this;
1876 RefPtr
<StrongWorkerRef
> workerRef
= StrongWorkerRef::Create(
1877 aWorkerPrivate
, "EventSource", [self
]() { self
->Close(); });
1879 if (NS_WARN_IF(!workerRef
)) {
1883 mWorkerRef
= new ThreadSafeWorkerRef(workerRef
);
1887 void EventSourceImpl::ReleaseWorkerRef() {
1888 MOZ_ASSERT(IsClosed());
1889 MOZ_ASSERT(IsCurrentThreadRunningWorker());
1890 mWorkerRef
= nullptr;
1893 //-----------------------------------------------------------------------------
1894 // EventSourceImpl::nsIEventTarget
1895 //-----------------------------------------------------------------------------
1897 EventSourceImpl::DispatchFromScript(nsIRunnable
* aEvent
, uint32_t aFlags
) {
1898 nsCOMPtr
<nsIRunnable
> event(aEvent
);
1899 return Dispatch(event
.forget(), aFlags
);
1903 EventSourceImpl::Dispatch(already_AddRefed
<nsIRunnable
> aEvent
,
1905 nsCOMPtr
<nsIRunnable
> event_ref(aEvent
);
1906 if (mIsMainThread
) {
1907 return NS_DispatchToMainThread(event_ref
.forget());
1911 // We want to avoid clutter about errors in our shutdown logs,
1912 // so just report NS_OK (we have no explicit return value
1917 // If the target is a worker, we have to use a custom WorkerRunnableDispatcher
1919 RefPtr
<WorkerRunnableDispatcher
> event
= new WorkerRunnableDispatcher(
1920 this, mWorkerRef
->Private(), event_ref
.forget());
1922 if (!event
->Dispatch()) {
1923 return NS_ERROR_FAILURE
;
1929 EventSourceImpl::DelayedDispatch(already_AddRefed
<nsIRunnable
> aEvent
,
1930 uint32_t aDelayMs
) {
1931 return NS_ERROR_NOT_IMPLEMENTED
;
1935 EventSourceImpl::RegisterShutdownTask(nsITargetShutdownTask
*) {
1936 return NS_ERROR_NOT_IMPLEMENTED
;
1940 EventSourceImpl::UnregisterShutdownTask(nsITargetShutdownTask
*) {
1941 return NS_ERROR_NOT_IMPLEMENTED
;
1944 //-----------------------------------------------------------------------------
1945 // EventSourceImpl::nsIThreadRetargetableStreamListener
1946 //-----------------------------------------------------------------------------
1948 EventSourceImpl::CheckListenerChain() {
1949 MOZ_ASSERT(NS_IsMainThread(), "Should be on the main thread!");
1952 ////////////////////////////////////////////////////////////////////////////////
1954 ////////////////////////////////////////////////////////////////////////////////
1956 EventSource::EventSource(nsIGlobalObject
* aGlobal
,
1957 nsICookieJarSettings
* aCookieJarSettings
,
1958 bool aWithCredentials
)
1959 : DOMEventTargetHelper(aGlobal
),
1960 mWithCredentials(aWithCredentials
),
1961 mIsMainThread(NS_IsMainThread()) {
1962 MOZ_ASSERT(aGlobal
);
1963 MOZ_ASSERT(aCookieJarSettings
);
1964 mESImpl
= new EventSourceImpl(this, aCookieJarSettings
);
1967 EventSource::~EventSource() = default;
1969 nsresult
EventSource::CreateAndDispatchSimpleEvent(const nsAString
& aName
) {
1970 RefPtr
<Event
> event
= NS_NewDOMEvent(this, nullptr, nullptr);
1971 // it doesn't bubble, and it isn't cancelable
1972 event
->InitEvent(aName
, false, false);
1973 event
->SetTrusted(true);
1975 DispatchEvent(*event
, rv
);
1976 return rv
.StealNSResult();
1980 already_AddRefed
<EventSource
> EventSource::Constructor(
1981 const GlobalObject
& aGlobal
, const nsAString
& aURL
,
1982 const EventSourceInit
& aEventSourceInitDict
, ErrorResult
& aRv
) {
1983 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(aGlobal
.GetAsSupports());
1984 if (NS_WARN_IF(!global
)) {
1985 aRv
.Throw(NS_ERROR_FAILURE
);
1989 nsCOMPtr
<nsICookieJarSettings
> cookieJarSettings
;
1990 nsCOMPtr
<nsPIDOMWindowInner
> ownerWindow
= do_QueryInterface(global
);
1992 Document
* doc
= ownerWindow
->GetExtantDoc();
1993 if (NS_WARN_IF(!doc
)) {
1994 aRv
.Throw(NS_ERROR_FAILURE
);
1998 cookieJarSettings
= doc
->CookieJarSettings();
2001 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
2002 if (!workerPrivate
) {
2003 aRv
.Throw(NS_ERROR_FAILURE
);
2007 cookieJarSettings
= workerPrivate
->CookieJarSettings();
2010 RefPtr
<EventSource
> eventSource
= new EventSource(
2011 global
, cookieJarSettings
, aEventSourceInitDict
.mWithCredentials
);
2013 if (NS_IsMainThread()) {
2014 // Get principal from document and init EventSourceImpl
2015 nsCOMPtr
<nsIScriptObjectPrincipal
> scriptPrincipal
=
2016 do_QueryInterface(aGlobal
.GetAsSupports());
2017 if (!scriptPrincipal
) {
2018 aRv
.Throw(NS_ERROR_FAILURE
);
2021 nsCOMPtr
<nsIPrincipal
> principal
= scriptPrincipal
->GetPrincipal();
2023 aRv
.Throw(NS_ERROR_FAILURE
);
2026 eventSource
->mESImpl
->Init(principal
, aURL
, aRv
);
2027 if (NS_WARN_IF(aRv
.Failed())) {
2031 eventSource
->mESImpl
->InitChannelAndRequestEventSource(true);
2032 return eventSource
.forget();
2037 // Scope for possible failures that need cleanup
2038 auto guardESImpl
= MakeScopeExit([&] { eventSource
->mESImpl
= nullptr; });
2040 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
2041 MOZ_ASSERT(workerPrivate
);
2043 eventSource
->mESImpl
->mInnerWindowID
= workerPrivate
->WindowID();
2045 RefPtr
<InitRunnable
> initRunnable
=
2046 new InitRunnable(workerPrivate
, eventSource
->mESImpl
, aURL
);
2047 initRunnable
->Dispatch(Canceling
, aRv
);
2048 if (NS_WARN_IF(aRv
.Failed())) {
2052 aRv
= initRunnable
->ErrorCode();
2053 if (NS_WARN_IF(aRv
.Failed())) {
2057 // In workers we have to keep the worker alive using a WorkerRef in order
2058 // to dispatch messages correctly.
2059 // Note, initRunnable->Dispatch may have cleared mESImpl.
2060 if (!eventSource
->mESImpl
||
2061 !eventSource
->mESImpl
->CreateWorkerRef(workerPrivate
)) {
2062 // The worker is already shutting down. Let's return an already closed
2063 // object, but marked as Connecting.
2064 if (eventSource
->mESImpl
) {
2065 // mESImpl is nulled by this call such that EventSourceImpl is
2066 // released before returning the object, otherwise
2067 // it will set EventSource to a CLOSED state in its DTOR..
2068 eventSource
->mESImpl
->Close();
2070 eventSource
->mReadyState
= EventSourceImpl::CONNECTING
;
2072 guardESImpl
.release();
2073 return eventSource
.forget();
2076 // Let's connect to the server.
2077 RefPtr
<ConnectRunnable
> connectRunnable
=
2078 new ConnectRunnable(workerPrivate
, eventSource
->mESImpl
);
2079 connectRunnable
->Dispatch(Canceling
, aRv
);
2080 if (NS_WARN_IF(aRv
.Failed())) {
2084 // End of scope for possible failures
2085 guardESImpl
.release();
2088 return eventSource
.forget();
2092 JSObject
* EventSource::WrapObject(JSContext
* aCx
,
2093 JS::Handle
<JSObject
*> aGivenProto
) {
2094 return EventSource_Binding::Wrap(aCx
, this, aGivenProto
);
2097 void EventSource::Close() {
2098 AssertIsOnTargetThread();
2100 // Close potentially kills ourself, ensure
2101 // to not access any members afterwards.
2106 //-----------------------------------------------------------------------------
2107 // EventSource::nsISupports
2108 //-----------------------------------------------------------------------------
2110 NS_IMPL_CYCLE_COLLECTION_CLASS(EventSource
)
2112 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(EventSource
,
2113 DOMEventTargetHelper
)
2114 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2116 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(EventSource
,
2117 DOMEventTargetHelper
)
2119 // IsCertainlyaliveForCC will return true and cause the cycle
2120 // collector to skip this instance when mESImpl is non-null and
2121 // points back to ourself.
2122 // mESImpl is initialized to be non-null in the constructor
2123 // and should have been wiped out in our close function.
2124 MOZ_ASSERT_UNREACHABLE("Paranoia cleanup that should never happen.");
2127 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2129 bool EventSource::IsCertainlyAliveForCC() const {
2130 // Until we are double linked forth and back, we want to stay alive.
2134 auto lock
= mESImpl
->mSharedData
.Lock();
2135 return lock
->mEventSource
== this;
2138 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EventSource
)
2139 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
2141 NS_IMPL_ADDREF_INHERITED(EventSource
, DOMEventTargetHelper
)
2142 NS_IMPL_RELEASE_INHERITED(EventSource
, DOMEventTargetHelper
)
2144 } // namespace mozilla::dom