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/dom/EventSource.h"
9 #include "mozilla/ArrayUtils.h"
10 #include "mozilla/Components.h"
11 #include "mozilla/DataMutex.h"
12 #include "mozilla/DebugOnly.h"
13 #include "mozilla/LoadInfo.h"
14 #include "mozilla/DOMEventTargetHelper.h"
15 #include "mozilla/dom/EventSourceBinding.h"
16 #include "mozilla/dom/MessageEvent.h"
17 #include "mozilla/dom/MessageEventBinding.h"
18 #include "mozilla/dom/ScriptSettings.h"
19 #include "mozilla/dom/WorkerPrivate.h"
20 #include "mozilla/dom/WorkerRef.h"
21 #include "mozilla/dom/WorkerRunnable.h"
22 #include "mozilla/dom/WorkerScope.h"
23 #include "mozilla/dom/EventSourceEventService.h"
24 #include "mozilla/ScopeExit.h"
25 #include "mozilla/UniquePtrExtensions.h"
26 #include "nsComponentManagerUtils.h"
27 #include "nsIThreadRetargetableStreamListener.h"
28 #include "nsNetUtil.h"
29 #include "nsIAuthPrompt.h"
30 #include "nsIAuthPrompt2.h"
31 #include "nsIHttpChannel.h"
32 #include "nsIInputStream.h"
33 #include "nsIInterfaceRequestorUtils.h"
34 #include "nsMimeTypes.h"
35 #include "nsIPromptFactory.h"
36 #include "nsIWindowWatcher.h"
37 #include "nsPresContext.h"
38 #include "nsProxyRelease.h"
39 #include "nsContentPolicyUtils.h"
40 #include "nsIStringBundle.h"
41 #include "nsIConsoleService.h"
42 #include "nsIObserverService.h"
43 #include "nsIScriptObjectPrincipal.h"
44 #include "nsJSUtils.h"
45 #include "nsIThreadRetargetableRequest.h"
46 #include "nsIAsyncVerifyRedirectCallback.h"
47 #include "nsIScriptError.h"
48 #include "nsContentUtils.h"
49 #include "mozilla/Preferences.h"
50 #include "xpcpublic.h"
51 #include "nsWrapperCacheInlines.h"
52 #include "mozilla/Attributes.h"
54 #include "mozilla/Encoding.h"
55 #include "ReferrerInfo.h"
57 namespace mozilla::dom
{
59 static LazyLogModule
gEventSourceLog("EventSource");
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
66 // Reconnection time related values in milliseconds. The default one is equal
67 // to the default value of the pref dom.server-events.default-reconnection-time
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)
73 class EventSourceImpl final
: public nsIObserver
,
74 public nsIStreamListener
,
75 public nsIChannelEventSink
,
76 public nsIInterfaceRequestor
,
77 public nsSupportsWeakReference
,
78 public nsIEventTarget
,
79 public nsITimerCallback
,
80 public nsIThreadRetargetableStreamListener
{
82 NS_DECL_THREADSAFE_ISUPPORTS
84 NS_DECL_NSIREQUESTOBSERVER
85 NS_DECL_NSISTREAMLISTENER
86 NS_DECL_NSICHANNELEVENTSINK
87 NS_DECL_NSIINTERFACEREQUESTOR
88 NS_DECL_NSIEVENTTARGET_FULL
89 NS_DECL_NSITIMERCALLBACK
90 NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
92 EventSourceImpl(EventSource
* aEventSource
,
93 nsICookieJarSettings
* aCookieJarSettings
);
95 enum { CONNECTING
= 0U, OPEN
= 1U, CLOSED
= 2U };
99 void Init(nsIPrincipal
* aPrincipal
, const nsAString
& aURL
, ErrorResult
& aRv
);
101 nsresult
GetBaseURI(nsIURI
** aBaseURI
);
103 void SetupHttpChannel();
104 nsresult
SetupReferrerInfo(const nsCOMPtr
<Document
>& aDocument
);
105 nsresult
InitChannelAndRequestEventSource(bool aEventTargetAccessAllowed
);
106 nsresult
ResetConnection();
108 nsresult
SetReconnectionTimeout();
110 void AnnounceConnection();
111 void DispatchAllMessageEvents();
112 nsresult
RestartConnection();
113 void ReestablishConnection();
114 void DispatchFailConnection();
115 void FailConnection();
120 nsresult
PrintErrorOnConsole(const char* aBundleURI
, const char* aError
,
121 const nsTArray
<nsString
>& aFormatStrings
);
122 nsresult
ConsoleError();
124 static nsresult
StreamReaderFunc(nsIInputStream
* aInputStream
, void* aClosure
,
125 const char* aFromRawSegment
,
126 uint32_t aToOffset
, uint32_t aCount
,
127 uint32_t* aWriteCount
);
128 void ParseSegment(const char* aBuffer
, uint32_t aLength
);
129 nsresult
SetFieldAndClear();
131 nsresult
ResetEvent();
132 nsresult
DispatchCurrentMessageEvent();
133 nsresult
ParseCharacter(char16_t aChr
);
134 nsresult
CheckHealthOfRequestCallback(nsIRequest
* aRequestCallback
);
135 nsresult
OnRedirectVerifyCallback(nsresult result
);
136 nsresult
ParseURL(const nsAString
& aURL
);
137 nsresult
AddWindowObservers();
138 void RemoveWindowObservers();
140 void CloseInternal();
141 void CleanupOnMainThread();
143 bool CreateWorkerRef(WorkerPrivate
* aWorkerPrivate
);
144 void ReleaseWorkerRef();
146 void AssertIsOnTargetThread() const {
147 MOZ_DIAGNOSTIC_ASSERT(IsTargetThread());
150 bool IsTargetThread() const { return NS_GetCurrentThread() == mTargetThread
; }
152 uint16_t ReadyState() {
153 auto lock
= mSharedData
.Lock();
154 if (lock
->mEventSource
) {
155 return lock
->mEventSource
->mReadyState
;
157 // EventSourceImpl keeps EventSource alive. If mEventSource is null, it
158 // means that the EventSource has been closed.
162 void SetReadyState(uint16_t aReadyState
) {
163 auto lock
= mSharedData
.Lock();
164 MOZ_ASSERT(lock
->mEventSource
);
165 MOZ_ASSERT(!mIsShutDown
);
166 lock
->mEventSource
->mReadyState
= aReadyState
;
169 bool IsClosed() { return ReadyState() == CLOSED
; }
171 RefPtr
<EventSource
> GetEventSource() {
172 AssertIsOnTargetThread();
173 auto lock
= mSharedData
.Lock();
174 return lock
->mEventSource
;
178 * A simple state machine used to manage the event-source's line buffer
180 * PARSE_STATE_OFF -> PARSE_STATE_BEGIN_OF_STREAM
182 * PARSE_STATE_BEGIN_OF_STREAM -> PARSE_STATE_CR_CHAR |
183 * PARSE_STATE_BEGIN_OF_LINE |
184 * PARSE_STATE_COMMENT |
185 * PARSE_STATE_FIELD_NAME
187 * PARSE_STATE_CR_CHAR -> PARSE_STATE_CR_CHAR |
188 * PARSE_STATE_COMMENT |
189 * PARSE_STATE_FIELD_NAME |
190 * PARSE_STATE_BEGIN_OF_LINE
192 * PARSE_STATE_COMMENT -> PARSE_STATE_CR_CHAR |
193 * PARSE_STATE_BEGIN_OF_LINE
195 * PARSE_STATE_FIELD_NAME -> PARSE_STATE_CR_CHAR |
196 * PARSE_STATE_BEGIN_OF_LINE |
197 * PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE
199 * PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE -> PARSE_STATE_FIELD_VALUE |
200 * PARSE_STATE_CR_CHAR |
201 * PARSE_STATE_BEGIN_OF_LINE
203 * PARSE_STATE_FIELD_VALUE -> PARSE_STATE_CR_CHAR |
204 * PARSE_STATE_BEGIN_OF_LINE
206 * PARSE_STATE_BEGIN_OF_LINE -> PARSE_STATE_CR_CHAR |
207 * PARSE_STATE_COMMENT |
208 * PARSE_STATE_FIELD_NAME |
209 * PARSE_STATE_BEGIN_OF_LINE
211 * Whenever the parser find an empty line or the end-of-file
212 * it dispatches the stacked event.
217 PARSE_STATE_BEGIN_OF_STREAM
,
220 PARSE_STATE_FIELD_NAME
,
221 PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE
,
222 PARSE_STATE_FIELD_VALUE
,
223 PARSE_STATE_IGNORE_FIELD_VALUE
,
224 PARSE_STATE_BEGIN_OF_LINE
227 // Connection related data members. Should only be accessed on main thread.
228 nsCOMPtr
<nsIURI
> mSrc
;
229 uint32_t mReconnectionTime
; // in ms
230 nsCOMPtr
<nsIPrincipal
> mPrincipal
;
232 nsCOMPtr
<nsITimer
> mTimer
;
233 nsCOMPtr
<nsIHttpChannel
> mHttpChannel
;
237 // We need to be able to distinguish between different states of id field:
238 // 1) is not given at all
239 // 2) is given but is empty
240 // 3) is given and has a value
241 // We can't check for the 1st state with a simple nsString.
242 Maybe
<nsString
> mLastEventID
;
246 // Message related data members. May be set / initialized when initializing
247 // EventSourceImpl on target thread but should only be used on target thread.
248 nsString mLastEventID
;
249 UniquePtr
<Message
> mCurrentMessage
;
250 nsDeque
<Message
> mMessagesToDispatch
;
251 ParserStatus mStatus
;
252 mozilla::UniquePtr
<mozilla::Decoder
> mUnicodeDecoder
;
253 nsString mLastFieldName
;
254 nsString mLastFieldValue
;
256 // EventSourceImpl internal states.
257 // WorkerRef to keep the worker alive. (accessed on worker thread only)
258 RefPtr
<ThreadSafeWorkerRef
> mWorkerRef
;
259 // Whether the window is frozen. May be set on main thread and read on target
261 Atomic
<bool> mFrozen
;
262 // There are some messages are going to be dispatched when thaw.
263 bool mGoingToDispatchAllMessages
;
264 // Whether the EventSource is run on main thread.
265 const bool mIsMainThread
;
266 // Whether the EventSourceImpl is going to be destroyed.
267 Atomic
<bool> mIsShutDown
;
269 class EventSourceServiceNotifier final
{
271 EventSourceServiceNotifier(RefPtr
<EventSourceImpl
>&& aEventSourceImpl
,
272 uint64_t aHttpChannelId
, uint64_t aInnerWindowID
)
273 : mEventSourceImpl(std::move(aEventSourceImpl
)),
274 mHttpChannelId(aHttpChannelId
),
275 mInnerWindowID(aInnerWindowID
),
276 mConnectionOpened(false) {
277 AssertIsOnMainThread();
278 mService
= EventSourceEventService::GetOrCreate();
281 void ConnectionOpened() {
282 mEventSourceImpl
->AssertIsOnTargetThread();
283 mService
->EventSourceConnectionOpened(mHttpChannelId
, mInnerWindowID
);
284 mConnectionOpened
= true;
287 void EventReceived(const nsAString
& aEventName
,
288 const nsAString
& aLastEventID
, const nsAString
& aData
,
289 uint32_t aRetry
, DOMHighResTimeStamp aTimeStamp
) {
290 mEventSourceImpl
->AssertIsOnTargetThread();
291 mService
->EventReceived(mHttpChannelId
, mInnerWindowID
, aEventName
,
292 aLastEventID
, aData
, aRetry
, aTimeStamp
);
295 ~EventSourceServiceNotifier() {
296 // It is safe to call this on any thread because
297 // EventSourceConnectionClosed method is thread safe and
298 // NS_ReleaseOnMainThread explicitly releases the service on the main
300 if (mConnectionOpened
) {
301 // We want to notify about connection being closed only if we told
302 // it was ever opened. The check is needed if OnStartRequest is called
303 // on the main thread while close() is called on a worker thread.
304 mService
->EventSourceConnectionClosed(mHttpChannelId
, mInnerWindowID
);
306 NS_ReleaseOnMainThread("EventSourceServiceNotifier::mService",
311 RefPtr
<EventSourceEventService
> mService
;
312 RefPtr
<EventSourceImpl
> mEventSourceImpl
;
313 uint64_t mHttpChannelId
;
314 uint64_t mInnerWindowID
;
315 bool mConnectionOpened
;
319 RefPtr
<EventSource
> mEventSource
;
320 UniquePtr
<EventSourceServiceNotifier
> mServiceNotifier
;
323 DataMutex
<SharedData
> mSharedData
;
325 // Event Source owner information:
326 // - the script file name
327 // - source code line number and column number where the Event Source object
329 // - the ID of the inner window where the script lives. Note that this may not
330 // be the same as the Event Source owner window.
331 // These attributes are used for error reporting. Should only be accessed on
333 nsString mScriptFile
;
334 uint32_t mScriptLine
;
335 uint32_t mScriptColumn
;
336 uint64_t mInnerWindowID
;
339 nsCOMPtr
<nsICookieJarSettings
> mCookieJarSettings
;
341 // Pointer to the target thread for checking whether we are
342 // on the target thread. This is intentionally a non-owning
343 // pointer in order not to affect the thread destruction
344 // sequence. This pointer must only be compared for equality
345 // and must not be dereferenced.
346 nsIThread
* mTargetThread
;
349 EventSourceImpl(const EventSourceImpl
& x
) = delete;
350 EventSourceImpl
& operator=(const EventSourceImpl
& x
) = delete;
355 // If we threw during Init we never called Close
356 SetReadyState(CLOSED
);
361 NS_IMPL_ISUPPORTS(EventSourceImpl
, nsIObserver
, nsIStreamListener
,
362 nsIRequestObserver
, nsIChannelEventSink
,
363 nsIInterfaceRequestor
, nsISupportsWeakReference
,
364 nsIEventTarget
, nsIThreadRetargetableStreamListener
)
366 EventSourceImpl::EventSourceImpl(EventSource
* aEventSource
,
367 nsICookieJarSettings
* aCookieJarSettings
)
368 : mReconnectionTime(0),
369 mStatus(PARSE_STATE_OFF
),
371 mGoingToDispatchAllMessages(false),
372 mIsMainThread(NS_IsMainThread()),
374 mSharedData(SharedData
{aEventSource
}, "EventSourceImpl::mSharedData"),
378 mCookieJarSettings(aCookieJarSettings
),
379 mTargetThread(NS_GetCurrentThread()) {
380 MOZ_ASSERT(aEventSource
);
381 SetReadyState(CONNECTING
);
384 class CleanupRunnable final
: public WorkerMainThreadRunnable
{
386 explicit CleanupRunnable(RefPtr
<EventSourceImpl
>&& aEventSourceImpl
)
387 : WorkerMainThreadRunnable(GetCurrentThreadWorkerPrivate(),
388 "EventSource :: Cleanup"_ns
),
389 mESImpl(std::move(aEventSourceImpl
)) {
391 mWorkerPrivate
->AssertIsOnWorkerThread();
394 bool MainThreadRun() override
{
396 mESImpl
->CleanupOnMainThread();
397 // We want to ensure the shortest possible remaining lifetime
398 // and not depend on the Runnable's destruction.
404 RefPtr
<EventSourceImpl
> mESImpl
;
407 void EventSourceImpl::Close() {
412 SetReadyState(CLOSED
);
413 // CloseInternal potentially kills ourself, ensure
414 // to not access any members afterwards.
418 void EventSourceImpl::CloseInternal() {
419 AssertIsOnTargetThread();
420 MOZ_ASSERT(IsClosed());
422 RefPtr
<EventSource
> myES
;
424 auto lock
= mSharedData
.Lock();
425 // We want to ensure to release ourself even if we have
426 // the shutdown case, thus we put aside a pointer
427 // to the EventSource and null it out right now.
428 myES
= std::move(lock
->mEventSource
);
429 lock
->mEventSource
= nullptr;
430 lock
->mServiceNotifier
= nullptr;
433 MOZ_ASSERT(!mIsShutDown
);
438 // Invoke CleanupOnMainThread before cleaning any members. It will call
439 // ShutDown, which is supposed to be called before cleaning any members.
440 if (NS_IsMainThread()) {
441 CleanupOnMainThread();
444 // run CleanupOnMainThread synchronously on main thread since it touches
445 // observers and members only can be accessed on main thread.
446 RefPtr
<CleanupRunnable
> runnable
= new CleanupRunnable(this);
447 runnable
->Dispatch(Killing
, rv
);
448 MOZ_ASSERT(!rv
.Failed());
452 while (mMessagesToDispatch
.GetSize() != 0) {
453 delete mMessagesToDispatch
.PopFront();
457 mUnicodeDecoder
= nullptr;
458 // Release the object on its owner. Don't access to any members
460 myES
->mESImpl
= nullptr;
463 void EventSourceImpl::CleanupOnMainThread() {
464 AssertIsOnMainThread();
465 MOZ_ASSERT(IsClosed());
467 // Call ShutDown before cleaning any members.
468 MOZ_ASSERT(!mIsShutDown
);
472 RemoveWindowObservers();
481 mPrincipal
= nullptr;
485 class InitRunnable final
: public WorkerMainThreadRunnable
{
487 InitRunnable(WorkerPrivate
* aWorkerPrivate
,
488 RefPtr
<EventSourceImpl
> aEventSourceImpl
, const nsAString
& aURL
)
489 : WorkerMainThreadRunnable(aWorkerPrivate
, "EventSource :: Init"_ns
),
490 mESImpl(std::move(aEventSourceImpl
)),
492 mRv(NS_ERROR_NOT_INITIALIZED
) {
493 MOZ_ASSERT(aWorkerPrivate
);
494 aWorkerPrivate
->AssertIsOnWorkerThread();
498 bool MainThreadRun() override
{
499 // Get principal from worker's owner document or from worker.
500 WorkerPrivate
* wp
= mWorkerPrivate
;
501 while (wp
->GetParent()) {
502 wp
= wp
->GetParent();
504 nsPIDOMWindowInner
* window
= wp
->GetWindow();
505 Document
* doc
= window
? window
->GetExtantDoc() : nullptr;
506 nsCOMPtr
<nsIPrincipal
> principal
=
507 doc
? doc
->NodePrincipal() : wp
->GetPrincipal();
509 mRv
= NS_ERROR_FAILURE
;
513 mESImpl
->Init(principal
, mURL
, rv
);
514 mRv
= rv
.StealNSResult();
516 // We want to ensure that EventSourceImpl's lifecycle
517 // does not depend on this Runnable's one.
523 nsresult
ErrorCode() const { return mRv
; }
526 RefPtr
<EventSourceImpl
> mESImpl
;
527 const nsAString
& mURL
;
531 class ConnectRunnable final
: public WorkerMainThreadRunnable
{
533 explicit ConnectRunnable(WorkerPrivate
* aWorkerPrivate
,
534 RefPtr
<EventSourceImpl
> aEventSourceImpl
)
535 : WorkerMainThreadRunnable(aWorkerPrivate
, "EventSource :: Connect"_ns
),
536 mESImpl(std::move(aEventSourceImpl
)) {
537 MOZ_ASSERT(aWorkerPrivate
);
538 aWorkerPrivate
->AssertIsOnWorkerThread();
542 bool MainThreadRun() override
{
544 // We are allowed to access the event target since this runnable is
545 // synchronized with the thread the event target lives on.
546 mESImpl
->InitChannelAndRequestEventSource(true);
547 // We want to ensure the shortest possible remaining lifetime
548 // and not depend on the Runnable's destruction.
554 RefPtr
<EventSourceImpl
> mESImpl
;
557 nsresult
EventSourceImpl::ParseURL(const nsAString
& aURL
) {
558 AssertIsOnMainThread();
559 MOZ_ASSERT(!mIsShutDown
);
561 nsCOMPtr
<nsIURI
> baseURI
;
562 nsresult rv
= GetBaseURI(getter_AddRefs(baseURI
));
563 NS_ENSURE_SUCCESS(rv
, rv
);
565 nsCOMPtr
<nsIURI
> srcURI
;
566 rv
= NS_NewURI(getter_AddRefs(srcURI
), aURL
, nullptr, baseURI
);
567 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SYNTAX_ERR
);
570 rv
= nsContentUtils::GetUTFOrigin(srcURI
, origin
);
571 NS_ENSURE_SUCCESS(rv
, rv
);
574 rv
= srcURI
->GetSpec(spec
);
575 NS_ENSURE_SUCCESS(rv
, rv
);
577 // This assignment doesn't require extra synchronization because this function
578 // is only ever called from EventSourceImpl::Init(), which is either called
579 // directly if mEventSource was created on the main thread, or via a
580 // synchronous runnable if it was created on a worker thread.
582 // We can't use GetEventSource() here because it would modify the refcount,
583 // and that's not allowed off the owning thread.
584 auto lock
= mSharedData
.Lock();
585 lock
->mEventSource
->mOriginalURL
= NS_ConvertUTF8toUTF16(spec
);
592 nsresult
EventSourceImpl::AddWindowObservers() {
593 AssertIsOnMainThread();
594 MOZ_ASSERT(mIsMainThread
);
595 MOZ_ASSERT(!mIsShutDown
);
596 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
599 nsresult rv
= os
->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC
, true);
600 NS_ENSURE_SUCCESS(rv
, rv
);
601 rv
= os
->AddObserver(this, DOM_WINDOW_FROZEN_TOPIC
, true);
602 NS_ENSURE_SUCCESS(rv
, rv
);
603 rv
= os
->AddObserver(this, DOM_WINDOW_THAWED_TOPIC
, true);
604 NS_ENSURE_SUCCESS(rv
, rv
);
608 void EventSourceImpl::RemoveWindowObservers() {
609 AssertIsOnMainThread();
610 MOZ_ASSERT(mIsMainThread
);
611 MOZ_ASSERT(IsClosed());
612 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
614 os
->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC
);
615 os
->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC
);
616 os
->RemoveObserver(this, DOM_WINDOW_THAWED_TOPIC
);
620 void EventSourceImpl::Init(nsIPrincipal
* aPrincipal
, const nsAString
& aURL
,
622 AssertIsOnMainThread();
623 MOZ_ASSERT(aPrincipal
);
624 MOZ_ASSERT(ReadyState() == CONNECTING
);
625 mPrincipal
= aPrincipal
;
626 aRv
= ParseURL(aURL
);
627 if (NS_WARN_IF(aRv
.Failed())) {
630 // The conditional here is historical and not necessarily sane.
631 if (JSContext
* cx
= nsContentUtils::GetCurrentJSContext()) {
632 nsJSUtils::GetCallingLocation(cx
, mScriptFile
, &mScriptLine
,
634 mInnerWindowID
= nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx
);
638 // we observe when the window freezes and thaws
639 aRv
= AddWindowObservers();
640 if (NS_WARN_IF(aRv
.Failed())) {
646 Preferences::GetInt("dom.server-events.default-reconnection-time",
647 DEFAULT_RECONNECTION_TIME_VALUE
);
649 mUnicodeDecoder
= UTF_8_ENCODING
->NewDecoderWithBOMRemoval();
652 //-----------------------------------------------------------------------------
653 // EventSourceImpl::nsIObserver
654 //-----------------------------------------------------------------------------
657 EventSourceImpl::Observe(nsISupports
* aSubject
, const char* aTopic
,
658 const char16_t
* aData
) {
659 AssertIsOnMainThread();
664 nsCOMPtr
<nsPIDOMWindowInner
> window
= do_QueryInterface(aSubject
);
665 MOZ_ASSERT(mIsMainThread
);
667 auto lock
= mSharedData
.Lock();
668 if (!lock
->mEventSource
->GetOwner() ||
669 window
!= lock
->mEventSource
->GetOwner()) {
674 DebugOnly
<nsresult
> rv
;
675 if (strcmp(aTopic
, DOM_WINDOW_FROZEN_TOPIC
) == 0) {
677 MOZ_ASSERT(NS_SUCCEEDED(rv
), "Freeze() failed");
678 } else if (strcmp(aTopic
, DOM_WINDOW_THAWED_TOPIC
) == 0) {
680 MOZ_ASSERT(NS_SUCCEEDED(rv
), "Thaw() failed");
681 } else if (strcmp(aTopic
, DOM_WINDOW_DESTROYED_TOPIC
) == 0) {
688 //-----------------------------------------------------------------------------
689 // EventSourceImpl::nsIStreamListener
690 //-----------------------------------------------------------------------------
693 EventSourceImpl::OnStartRequest(nsIRequest
* aRequest
) {
694 AssertIsOnMainThread();
696 return NS_ERROR_ABORT
;
698 nsresult rv
= CheckHealthOfRequestCallback(aRequest
);
699 NS_ENSURE_SUCCESS(rv
, rv
);
701 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aRequest
, &rv
);
702 NS_ENSURE_SUCCESS(rv
, rv
);
705 rv
= aRequest
->GetStatus(&status
);
706 NS_ENSURE_SUCCESS(rv
, rv
);
708 if (NS_FAILED(status
)) {
709 // EventSource::OnStopRequest will evaluate if it shall either reestablish
710 // or fail the connection
711 return NS_ERROR_ABORT
;
715 rv
= httpChannel
->GetResponseStatus(&httpStatus
);
716 NS_ENSURE_SUCCESS(rv
, rv
);
718 if (httpStatus
!= 200) {
719 DispatchFailConnection();
720 return NS_ERROR_ABORT
;
723 nsAutoCString contentType
;
724 rv
= httpChannel
->GetContentType(contentType
);
725 NS_ENSURE_SUCCESS(rv
, rv
);
727 if (!contentType
.EqualsLiteral(TEXT_EVENT_STREAM
)) {
728 DispatchFailConnection();
729 return NS_ERROR_ABORT
;
732 if (!mIsMainThread
) {
733 // Try to retarget to worker thread, otherwise fall back to main thread.
734 nsCOMPtr
<nsIThreadRetargetableRequest
> rr
= do_QueryInterface(httpChannel
);
736 rv
= rr
->RetargetDeliveryTo(this);
737 if (NS_WARN_IF(NS_FAILED(rv
))) {
738 NS_WARNING("Retargeting failed");
744 auto lock
= mSharedData
.Lock();
745 lock
->mServiceNotifier
= MakeUnique
<EventSourceServiceNotifier
>(
746 this, mHttpChannel
->ChannelId(), mInnerWindowID
);
748 rv
= Dispatch(NewRunnableMethod("dom::EventSourceImpl::AnnounceConnection",
749 this, &EventSourceImpl::AnnounceConnection
),
751 NS_ENSURE_SUCCESS(rv
, rv
);
752 mStatus
= PARSE_STATE_BEGIN_OF_STREAM
;
756 // this method parses the characters as they become available instead of
758 nsresult
EventSourceImpl::StreamReaderFunc(nsIInputStream
* aInputStream
,
760 const char* aFromRawSegment
,
761 uint32_t aToOffset
, uint32_t aCount
,
762 uint32_t* aWriteCount
) {
763 // The EventSourceImpl instance is hold alive on the
764 // synchronously calling stack, so raw pointer is fine here.
765 EventSourceImpl
* thisObject
= static_cast<EventSourceImpl
*>(aClosure
);
766 if (!thisObject
|| !aWriteCount
) {
768 "EventSource cannot read from stream: no aClosure or aWriteCount");
769 return NS_ERROR_FAILURE
;
771 thisObject
->AssertIsOnTargetThread();
772 MOZ_ASSERT(!thisObject
->mIsShutDown
);
773 thisObject
->ParseSegment((const char*)aFromRawSegment
, aCount
);
774 *aWriteCount
= aCount
;
778 void EventSourceImpl::ParseSegment(const char* aBuffer
, uint32_t aLength
) {
779 AssertIsOnTargetThread();
783 char16_t buffer
[1024];
784 auto dst
= Span(buffer
);
785 auto src
= AsBytes(Span(aBuffer
, aLength
));
786 // XXX EOF handling is https://bugzilla.mozilla.org/show_bug.cgi?id=1369018
792 Tie(result
, read
, written
, hadErrors
) =
793 mUnicodeDecoder
->DecodeToUTF16(src
, dst
, false);
795 for (auto c
: dst
.To(written
)) {
796 nsresult rv
= ParseCharacter(c
);
797 NS_ENSURE_SUCCESS_VOID(rv
);
799 if (result
== kInputEmpty
) {
802 src
= src
.From(read
);
807 EventSourceImpl::OnDataAvailable(nsIRequest
* aRequest
,
808 nsIInputStream
* aInputStream
, uint64_t aOffset
,
810 AssertIsOnTargetThread();
811 NS_ENSURE_ARG_POINTER(aInputStream
);
813 return NS_ERROR_ABORT
;
816 nsresult rv
= CheckHealthOfRequestCallback(aRequest
);
817 NS_ENSURE_SUCCESS(rv
, rv
);
820 return aInputStream
->ReadSegments(EventSourceImpl::StreamReaderFunc
, this,
825 EventSourceImpl::OnStopRequest(nsIRequest
* aRequest
, nsresult aStatusCode
) {
826 AssertIsOnMainThread();
829 return NS_ERROR_ABORT
;
832 // "Network errors that prevents the connection from being established in the
833 // first place (e.g. DNS errors), must cause the user agent to asynchronously
834 // reestablish the connection.
836 // (...) the cancelation of the fetch algorithm by the user agent (e.g. in
837 // response to window.stop() or the user canceling the network connection
838 // manually) must cause the user agent to fail the connection.
840 if (NS_FAILED(aStatusCode
) && aStatusCode
!= NS_ERROR_CONNECTION_REFUSED
&&
841 aStatusCode
!= NS_ERROR_NET_TIMEOUT
&&
842 aStatusCode
!= NS_ERROR_NET_RESET
&&
843 aStatusCode
!= NS_ERROR_NET_INTERRUPT
&&
844 aStatusCode
!= NS_ERROR_PROXY_CONNECTION_REFUSED
&&
845 aStatusCode
!= NS_ERROR_DNS_LOOKUP_QUEUE_FULL
) {
846 DispatchFailConnection();
847 return NS_ERROR_ABORT
;
850 nsresult rv
= CheckHealthOfRequestCallback(aRequest
);
851 NS_ENSURE_SUCCESS(rv
, rv
);
854 Dispatch(NewRunnableMethod("dom::EventSourceImpl::ReestablishConnection",
855 this, &EventSourceImpl::ReestablishConnection
),
857 NS_ENSURE_SUCCESS(rv
, rv
);
862 //-----------------------------------------------------------------------------
863 // EventSourceImpl::nsIChannelEventSink
864 //-----------------------------------------------------------------------------
867 EventSourceImpl::AsyncOnChannelRedirect(
868 nsIChannel
* aOldChannel
, nsIChannel
* aNewChannel
, uint32_t aFlags
,
869 nsIAsyncVerifyRedirectCallback
* aCallback
) {
870 AssertIsOnMainThread();
872 return NS_ERROR_ABORT
;
874 nsCOMPtr
<nsIRequest
> aOldRequest
= aOldChannel
;
875 MOZ_ASSERT(aOldRequest
, "Redirect from a null request?");
877 nsresult rv
= CheckHealthOfRequestCallback(aOldRequest
);
878 NS_ENSURE_SUCCESS(rv
, rv
);
880 MOZ_ASSERT(aNewChannel
, "Redirect without a channel?");
882 nsCOMPtr
<nsIURI
> newURI
;
883 rv
= NS_GetFinalChannelURI(aNewChannel
, getter_AddRefs(newURI
));
884 NS_ENSURE_SUCCESS(rv
, rv
);
886 bool isValidScheme
= newURI
->SchemeIs("http") || newURI
->SchemeIs("https");
889 mIsMainThread
? GetEventSource()->CheckCurrentGlobalCorrectness() : NS_OK
;
890 if (NS_FAILED(rv
) || !isValidScheme
) {
891 DispatchFailConnection();
892 return NS_ERROR_DOM_SECURITY_ERR
;
895 // update our channel
897 mHttpChannel
= do_QueryInterface(aNewChannel
);
898 NS_ENSURE_STATE(mHttpChannel
);
901 // The HTTP impl already copies over the referrer info on
902 // redirects, so we don't need to SetupReferrerInfo().
904 if ((aFlags
& nsIChannelEventSink::REDIRECT_PERMANENT
) != 0) {
905 rv
= NS_GetFinalChannelURI(mHttpChannel
, getter_AddRefs(mSrc
));
906 NS_ENSURE_SUCCESS(rv
, rv
);
909 aCallback
->OnRedirectVerifyCallback(NS_OK
);
914 //-----------------------------------------------------------------------------
915 // EventSourceImpl::nsIInterfaceRequestor
916 //-----------------------------------------------------------------------------
919 EventSourceImpl::GetInterface(const nsIID
& aIID
, void** aResult
) {
920 AssertIsOnMainThread();
923 return NS_ERROR_FAILURE
;
926 if (aIID
.Equals(NS_GET_IID(nsIChannelEventSink
))) {
927 *aResult
= static_cast<nsIChannelEventSink
*>(this);
932 if (aIID
.Equals(NS_GET_IID(nsIAuthPrompt
)) ||
933 aIID
.Equals(NS_GET_IID(nsIAuthPrompt2
))) {
935 nsCOMPtr
<nsIPromptFactory
> wwatch
=
936 do_GetService(NS_WINDOWWATCHER_CONTRACTID
, &rv
);
937 NS_ENSURE_SUCCESS(rv
, rv
);
939 nsCOMPtr
<nsPIDOMWindowOuter
> window
;
941 // To avoid a data race we may only access the event target if it lives on
944 auto lock
= mSharedData
.Lock();
945 rv
= lock
->mEventSource
->CheckCurrentGlobalCorrectness();
946 NS_ENSURE_SUCCESS(rv
, NS_ERROR_UNEXPECTED
);
948 if (lock
->mEventSource
->GetOwner()) {
949 window
= lock
->mEventSource
->GetOwner()->GetOuterWindow();
953 // Get the an auth prompter for our window so that the parenting
954 // of the dialogs works as it should when using tabs.
956 return wwatch
->GetPrompt(window
, aIID
, aResult
);
959 return QueryInterface(aIID
, aResult
);
963 EventSourceImpl::IsOnCurrentThread(bool* aResult
) {
964 *aResult
= IsTargetThread();
969 EventSourceImpl::IsOnCurrentThreadInfallible() { return IsTargetThread(); }
971 nsresult
EventSourceImpl::GetBaseURI(nsIURI
** aBaseURI
) {
972 AssertIsOnMainThread();
973 MOZ_ASSERT(!mIsShutDown
);
974 NS_ENSURE_ARG_POINTER(aBaseURI
);
978 nsCOMPtr
<nsIURI
> baseURI
;
980 // first we try from document->GetBaseURI()
981 nsCOMPtr
<Document
> doc
=
982 mIsMainThread
? GetEventSource()->GetDocumentIfCurrent() : nullptr;
984 baseURI
= doc
->GetBaseURI();
987 // otherwise we get from the doc's principal
989 auto* basePrin
= BasePrincipal::Cast(mPrincipal
);
990 nsresult rv
= basePrin
->GetURI(getter_AddRefs(baseURI
));
991 NS_ENSURE_SUCCESS(rv
, rv
);
994 NS_ENSURE_STATE(baseURI
);
996 baseURI
.forget(aBaseURI
);
1000 void EventSourceImpl::SetupHttpChannel() {
1001 AssertIsOnMainThread();
1002 MOZ_ASSERT(!mIsShutDown
);
1003 nsresult rv
= mHttpChannel
->SetRequestMethod("GET"_ns
);
1004 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1006 /* set the http request headers */
1008 rv
= mHttpChannel
->SetRequestHeader(
1009 "Accept"_ns
, nsLiteralCString(TEXT_EVENT_STREAM
), false);
1010 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1012 // LOAD_BYPASS_CACHE already adds the Cache-Control: no-cache header
1014 if (mLastEventID
.IsEmpty()) {
1017 NS_ConvertUTF16toUTF8
eventId(mLastEventID
);
1018 rv
= mHttpChannel
->SetRequestHeader("Last-Event-ID"_ns
, eventId
, false);
1020 if (NS_FAILED(rv
)) {
1021 MOZ_LOG(gEventSourceLog
, LogLevel::Warning
,
1022 ("SetupHttpChannel. rv=%x (%s)", uint32_t(rv
), eventId
.get()));
1028 nsresult
EventSourceImpl::SetupReferrerInfo(
1029 const nsCOMPtr
<Document
>& aDocument
) {
1030 AssertIsOnMainThread();
1031 MOZ_ASSERT(!mIsShutDown
);
1034 auto referrerInfo
= MakeRefPtr
<ReferrerInfo
>(*aDocument
);
1035 nsresult rv
= mHttpChannel
->SetReferrerInfoWithoutClone(referrerInfo
);
1036 NS_ENSURE_SUCCESS(rv
, rv
);
1042 nsresult
EventSourceImpl::InitChannelAndRequestEventSource(
1043 const bool aEventTargetAccessAllowed
) {
1044 AssertIsOnMainThread();
1046 return NS_ERROR_ABORT
;
1049 bool isValidScheme
= mSrc
->SchemeIs("http") || mSrc
->SchemeIs("https");
1051 MOZ_ASSERT_IF(mIsMainThread
, aEventTargetAccessAllowed
);
1053 nsresult rv
= aEventTargetAccessAllowed
1055 // We can't call GetEventSource() because we're not
1056 // allowed to touch the refcount off the worker thread
1057 // due to an assertion, event if it would have otherwise
1059 auto lock
= mSharedData
.Lock();
1060 return lock
->mEventSource
->CheckCurrentGlobalCorrectness();
1063 if (NS_FAILED(rv
) || !isValidScheme
) {
1064 DispatchFailConnection();
1065 return NS_ERROR_DOM_SECURITY_ERR
;
1068 nsCOMPtr
<Document
> doc
;
1069 nsSecurityFlags securityFlags
=
1070 nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT
;
1072 auto lock
= mSharedData
.Lock();
1073 doc
= aEventTargetAccessAllowed
? lock
->mEventSource
->GetDocumentIfCurrent()
1076 if (lock
->mEventSource
->mWithCredentials
) {
1077 securityFlags
|= nsILoadInfo::SEC_COOKIES_INCLUDE
;
1081 // The html spec requires we use fetch cache mode of "no-store". This
1082 // maps to LOAD_BYPASS_CACHE and LOAD_INHIBIT_CACHING in necko.
1083 nsLoadFlags loadFlags
;
1084 loadFlags
= nsIRequest::LOAD_BACKGROUND
| nsIRequest::LOAD_BYPASS_CACHE
|
1085 nsIRequest::INHIBIT_CACHING
;
1087 nsCOMPtr
<nsIChannel
> channel
;
1088 // If we have the document, use it
1090 MOZ_ASSERT(mCookieJarSettings
== doc
->CookieJarSettings());
1092 nsCOMPtr
<nsILoadGroup
> loadGroup
= doc
->GetDocumentLoadGroup();
1093 rv
= NS_NewChannel(getter_AddRefs(channel
), mSrc
, doc
, securityFlags
,
1094 nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE
,
1095 nullptr, // aPerformanceStorage
1097 nullptr, // aCallbacks
1098 loadFlags
); // aLoadFlags
1100 // otherwise use the principal
1101 rv
= NS_NewChannel(getter_AddRefs(channel
), mSrc
, mPrincipal
, securityFlags
,
1102 nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE
,
1104 nullptr, // aPerformanceStorage
1105 nullptr, // loadGroup
1106 nullptr, // aCallbacks
1107 loadFlags
); // aLoadFlags
1110 NS_ENSURE_SUCCESS(rv
, rv
);
1112 mHttpChannel
= do_QueryInterface(channel
);
1113 NS_ENSURE_TRUE(mHttpChannel
, NS_ERROR_NO_INTERFACE
);
1116 rv
= SetupReferrerInfo(doc
);
1117 NS_ENSURE_SUCCESS(rv
, rv
);
1121 nsCOMPtr
<nsIInterfaceRequestor
> notificationCallbacks
;
1122 mHttpChannel
->GetNotificationCallbacks(
1123 getter_AddRefs(notificationCallbacks
));
1124 MOZ_ASSERT(!notificationCallbacks
);
1128 mHttpChannel
->SetNotificationCallbacks(this);
1130 // Start reading from the channel
1131 rv
= mHttpChannel
->AsyncOpen(this);
1132 if (NS_FAILED(rv
)) {
1133 DispatchFailConnection();
1140 void EventSourceImpl::AnnounceConnection() {
1141 AssertIsOnTargetThread();
1142 if (ReadyState() != CONNECTING
) {
1143 NS_WARNING("Unexpected mReadyState!!!");
1148 auto lock
= mSharedData
.Lock();
1149 if (lock
->mServiceNotifier
) {
1150 lock
->mServiceNotifier
->ConnectionOpened();
1154 // When a user agent is to announce the connection, the user agent must set
1155 // the readyState attribute to OPEN and queue a task to fire a simple event
1156 // named open at the EventSource object.
1158 SetReadyState(OPEN
);
1160 nsresult rv
= GetEventSource()->CheckCurrentGlobalCorrectness();
1161 if (NS_FAILED(rv
)) {
1164 // We can't hold the mutex while dispatching the event because the mutex is
1165 // not reentrant, and content might call back into our code.
1166 rv
= GetEventSource()->CreateAndDispatchSimpleEvent(u
"open"_ns
);
1167 if (NS_FAILED(rv
)) {
1168 NS_WARNING("Failed to dispatch the error event!!!");
1173 nsresult
EventSourceImpl::ResetConnection() {
1174 AssertIsOnMainThread();
1176 mHttpChannel
->Cancel(NS_ERROR_ABORT
);
1177 mHttpChannel
= nullptr;
1182 void EventSourceImpl::ResetDecoder() {
1183 AssertIsOnTargetThread();
1184 if (mUnicodeDecoder
) {
1185 UTF_8_ENCODING
->NewDecoderWithBOMRemovalInto(*mUnicodeDecoder
);
1187 mStatus
= PARSE_STATE_OFF
;
1191 class CallRestartConnection final
: public WorkerMainThreadRunnable
{
1193 explicit CallRestartConnection(RefPtr
<EventSourceImpl
>&& aEventSourceImpl
)
1194 : WorkerMainThreadRunnable(aEventSourceImpl
->mWorkerRef
->Private(),
1195 "EventSource :: RestartConnection"_ns
),
1196 mESImpl(std::move(aEventSourceImpl
)) {
1197 mWorkerPrivate
->AssertIsOnWorkerThread();
1198 MOZ_ASSERT(mESImpl
);
1201 bool MainThreadRun() override
{
1202 MOZ_ASSERT(mESImpl
);
1203 mESImpl
->RestartConnection();
1204 // We want to ensure the shortest possible remaining lifetime
1205 // and not depend on the Runnable's destruction.
1211 RefPtr
<EventSourceImpl
> mESImpl
;
1214 nsresult
EventSourceImpl::RestartConnection() {
1215 AssertIsOnMainThread();
1217 return NS_ERROR_ABORT
;
1220 nsresult rv
= ResetConnection();
1221 NS_ENSURE_SUCCESS(rv
, rv
);
1222 rv
= SetReconnectionTimeout();
1223 NS_ENSURE_SUCCESS(rv
, rv
);
1227 void EventSourceImpl::ReestablishConnection() {
1228 AssertIsOnTargetThread();
1234 if (mIsMainThread
) {
1235 rv
= RestartConnection();
1237 RefPtr
<CallRestartConnection
> runnable
= new CallRestartConnection(this);
1239 runnable
->Dispatch(Canceling
, result
);
1240 MOZ_ASSERT(!result
.Failed());
1241 rv
= result
.StealNSResult();
1243 if (NS_FAILED(rv
)) {
1247 rv
= GetEventSource()->CheckCurrentGlobalCorrectness();
1248 if (NS_FAILED(rv
)) {
1252 SetReadyState(CONNECTING
);
1254 // We can't hold the mutex while dispatching the event because the mutex is
1255 // not reentrant, and content might call back into our code.
1256 rv
= GetEventSource()->CreateAndDispatchSimpleEvent(u
"error"_ns
);
1257 if (NS_FAILED(rv
)) {
1258 NS_WARNING("Failed to dispatch the error event!!!");
1263 nsresult
EventSourceImpl::SetReconnectionTimeout() {
1264 AssertIsOnMainThread();
1266 return NS_ERROR_ABORT
;
1269 // the timer will be used whenever the requests are going finished.
1271 mTimer
= NS_NewTimer();
1272 NS_ENSURE_STATE(mTimer
);
1275 MOZ_TRY(mTimer
->InitWithCallback(this, mReconnectionTime
,
1276 nsITimer::TYPE_ONE_SHOT
));
1281 nsresult
EventSourceImpl::PrintErrorOnConsole(
1282 const char* aBundleURI
, const char* aError
,
1283 const nsTArray
<nsString
>& aFormatStrings
) {
1284 AssertIsOnMainThread();
1285 MOZ_ASSERT(!mIsShutDown
);
1286 nsCOMPtr
<nsIStringBundleService
> bundleService
=
1287 mozilla::components::StringBundle::Service();
1288 NS_ENSURE_STATE(bundleService
);
1290 nsCOMPtr
<nsIStringBundle
> strBundle
;
1292 bundleService
->CreateBundle(aBundleURI
, getter_AddRefs(strBundle
));
1293 NS_ENSURE_SUCCESS(rv
, rv
);
1295 nsCOMPtr
<nsIConsoleService
> console(
1296 do_GetService(NS_CONSOLESERVICE_CONTRACTID
, &rv
));
1297 NS_ENSURE_SUCCESS(rv
, rv
);
1299 nsCOMPtr
<nsIScriptError
> errObj(
1300 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID
, &rv
));
1301 NS_ENSURE_SUCCESS(rv
, rv
);
1303 // Localize the error message
1304 nsAutoString message
;
1305 if (!aFormatStrings
.IsEmpty()) {
1306 rv
= strBundle
->FormatStringFromName(aError
, aFormatStrings
, message
);
1308 rv
= strBundle
->GetStringFromName(aError
, message
);
1310 NS_ENSURE_SUCCESS(rv
, rv
);
1312 rv
= errObj
->InitWithWindowID(message
, mScriptFile
, u
""_ns
, mScriptLine
,
1313 mScriptColumn
, nsIScriptError::errorFlag
,
1314 "Event Source", mInnerWindowID
);
1315 NS_ENSURE_SUCCESS(rv
, rv
);
1317 // print the error message directly to the JS console
1318 rv
= console
->LogMessage(errObj
);
1319 NS_ENSURE_SUCCESS(rv
, rv
);
1324 nsresult
EventSourceImpl::ConsoleError() {
1325 AssertIsOnMainThread();
1326 MOZ_ASSERT(!mIsShutDown
);
1327 nsAutoCString targetSpec
;
1328 nsresult rv
= mSrc
->GetSpec(targetSpec
);
1329 NS_ENSURE_SUCCESS(rv
, rv
);
1331 AutoTArray
<nsString
, 1> formatStrings
;
1332 CopyUTF8toUTF16(targetSpec
, *formatStrings
.AppendElement());
1334 if (ReadyState() == CONNECTING
) {
1335 rv
= PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
1336 "connectionFailure", formatStrings
);
1338 rv
= PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
1339 "netInterrupt", formatStrings
);
1341 NS_ENSURE_SUCCESS(rv
, rv
);
1346 void EventSourceImpl::DispatchFailConnection() {
1347 AssertIsOnMainThread();
1351 nsresult rv
= ConsoleError();
1352 if (NS_FAILED(rv
)) {
1353 NS_WARNING("Failed to print to the console error");
1355 rv
= Dispatch(NewRunnableMethod("dom::EventSourceImpl::FailConnection", this,
1356 &EventSourceImpl::FailConnection
),
1357 NS_DISPATCH_NORMAL
);
1358 if (NS_WARN_IF(NS_FAILED(rv
))) {
1359 // if the worker is shutting down, the dispatching of normal WorkerRunnables
1365 void EventSourceImpl::FailConnection() {
1366 AssertIsOnTargetThread();
1370 // Must change state to closed before firing event to content.
1371 SetReadyState(CLOSED
);
1372 // When a user agent is to fail the connection, the user agent must set the
1373 // readyState attribute to CLOSED and queue a task to fire a simple event
1374 // named error at the EventSource object.
1375 nsresult rv
= GetEventSource()->CheckCurrentGlobalCorrectness();
1376 if (NS_SUCCEEDED(rv
)) {
1377 // We can't hold the mutex while dispatching the event because the mutex
1378 // is not reentrant, and content might call back into our code.
1379 rv
= GetEventSource()->CreateAndDispatchSimpleEvent(u
"error"_ns
);
1380 if (NS_FAILED(rv
)) {
1381 NS_WARNING("Failed to dispatch the error event!!!");
1384 // Call CloseInternal in the end of function because it may release
1389 NS_IMETHODIMP
EventSourceImpl::Notify(nsITimer
* aTimer
) {
1390 AssertIsOnMainThread();
1395 MOZ_ASSERT(!mHttpChannel
, "the channel hasn't been cancelled!!");
1398 nsresult rv
= InitChannelAndRequestEventSource(mIsMainThread
);
1399 if (NS_FAILED(rv
)) {
1400 NS_WARNING("InitChannelAndRequestEventSource() failed");
1406 nsresult
EventSourceImpl::Thaw() {
1407 AssertIsOnMainThread();
1408 if (IsClosed() || !mFrozen
) {
1412 MOZ_ASSERT(!mHttpChannel
, "the connection hasn't been closed!!!");
1416 if (!mGoingToDispatchAllMessages
&& mMessagesToDispatch
.GetSize() > 0) {
1417 nsCOMPtr
<nsIRunnable
> event
=
1418 NewRunnableMethod("dom::EventSourceImpl::DispatchAllMessageEvents",
1419 this, &EventSourceImpl::DispatchAllMessageEvents
);
1420 NS_ENSURE_STATE(event
);
1422 mGoingToDispatchAllMessages
= true;
1424 rv
= Dispatch(event
.forget(), NS_DISPATCH_NORMAL
);
1425 NS_ENSURE_SUCCESS(rv
, rv
);
1428 rv
= InitChannelAndRequestEventSource(mIsMainThread
);
1429 NS_ENSURE_SUCCESS(rv
, rv
);
1434 nsresult
EventSourceImpl::Freeze() {
1435 AssertIsOnMainThread();
1436 if (IsClosed() || mFrozen
) {
1440 MOZ_ASSERT(!mHttpChannel
, "the connection hasn't been closed!!!");
1445 nsresult
EventSourceImpl::DispatchCurrentMessageEvent() {
1446 AssertIsOnTargetThread();
1447 MOZ_ASSERT(!mIsShutDown
);
1448 UniquePtr
<Message
> message(std::move(mCurrentMessage
));
1451 if (!message
|| message
->mData
.IsEmpty()) {
1455 // removes the trailing LF from mData
1456 MOZ_ASSERT(message
->mData
.CharAt(message
->mData
.Length() - 1) == LF_CHAR
,
1457 "Invalid trailing character! LF was expected instead.");
1458 message
->mData
.SetLength(message
->mData
.Length() - 1);
1460 if (message
->mEventName
.IsEmpty()) {
1461 message
->mEventName
.AssignLiteral("message");
1464 mMessagesToDispatch
.Push(message
.release());
1466 if (!mGoingToDispatchAllMessages
) {
1467 nsCOMPtr
<nsIRunnable
> event
=
1468 NewRunnableMethod("dom::EventSourceImpl::DispatchAllMessageEvents",
1469 this, &EventSourceImpl::DispatchAllMessageEvents
);
1470 NS_ENSURE_STATE(event
);
1472 mGoingToDispatchAllMessages
= true;
1474 return Dispatch(event
.forget(), NS_DISPATCH_NORMAL
);
1480 void EventSourceImpl::DispatchAllMessageEvents() {
1481 AssertIsOnTargetThread();
1482 mGoingToDispatchAllMessages
= false;
1484 if (IsClosed() || mFrozen
) {
1491 auto lock
= mSharedData
.Lock();
1492 rv
= lock
->mEventSource
->CheckCurrentGlobalCorrectness();
1493 if (NS_FAILED(rv
)) {
1497 if (NS_WARN_IF(!jsapi
.Init(lock
->mEventSource
->GetOwnerGlobal()))) {
1502 JSContext
* cx
= jsapi
.cx();
1504 while (mMessagesToDispatch
.GetSize() > 0) {
1505 UniquePtr
<Message
> message(mMessagesToDispatch
.PopFront());
1507 if (message
->mLastEventID
.isSome()) {
1508 mLastEventID
.Assign(message
->mLastEventID
.value());
1511 if (message
->mLastEventID
.isNothing() && !mLastEventID
.IsEmpty()) {
1512 message
->mLastEventID
= Some(mLastEventID
);
1516 auto lock
= mSharedData
.Lock();
1517 if (lock
->mServiceNotifier
) {
1518 lock
->mServiceNotifier
->EventReceived(message
->mEventName
, mLastEventID
,
1519 message
->mData
, mReconnectionTime
,
1524 // Now we can turn our string into a jsval
1525 JS::Rooted
<JS::Value
> jsData(cx
);
1528 jsString
= JS_NewUCStringCopyN(cx
, message
->mData
.get(),
1529 message
->mData
.Length());
1530 NS_ENSURE_TRUE_VOID(jsString
);
1532 jsData
.setString(jsString
);
1535 // create an event that uses the MessageEvent interface,
1536 // which does not bubble, is not cancelable, and has no default action
1538 RefPtr
<EventSource
> eventSource
= GetEventSource();
1539 RefPtr
<MessageEvent
> event
=
1540 new MessageEvent(eventSource
, nullptr, nullptr);
1542 event
->InitMessageEvent(nullptr, message
->mEventName
, CanBubble::eNo
,
1543 Cancelable::eNo
, jsData
, mOrigin
, mLastEventID
,
1544 nullptr, Sequence
<OwningNonNull
<MessagePort
>>());
1545 event
->SetTrusted(true);
1547 // We can't hold the mutex while dispatching the event because the mutex is
1548 // not reentrant, and content might call back into our code.
1549 IgnoredErrorResult err
;
1550 eventSource
->DispatchEvent(*event
, err
);
1552 NS_WARNING("Failed to dispatch the message event!!!");
1556 if (IsClosed() || mFrozen
) {
1562 void EventSourceImpl::ClearFields() {
1563 AssertIsOnTargetThread();
1564 mCurrentMessage
= nullptr;
1565 mLastFieldName
.Truncate();
1566 mLastFieldValue
.Truncate();
1569 nsresult
EventSourceImpl::SetFieldAndClear() {
1570 MOZ_ASSERT(!mIsShutDown
);
1571 AssertIsOnTargetThread();
1572 if (mLastFieldName
.IsEmpty()) {
1573 mLastFieldValue
.Truncate();
1576 if (!mCurrentMessage
) {
1577 mCurrentMessage
= MakeUnique
<Message
>();
1579 char16_t first_char
;
1580 first_char
= mLastFieldName
.CharAt(0);
1582 // with no case folding performed
1583 switch (first_char
) {
1585 if (mLastFieldName
.EqualsLiteral("data")) {
1586 // If the field name is "data" append the field value to the data
1587 // buffer, then append a single U+000A LINE FEED (LF) character
1588 // to the data buffer.
1589 mCurrentMessage
->mData
.Append(mLastFieldValue
);
1590 mCurrentMessage
->mData
.Append(LF_CHAR
);
1595 if (mLastFieldName
.EqualsLiteral("event")) {
1596 mCurrentMessage
->mEventName
.Assign(mLastFieldValue
);
1601 if (mLastFieldName
.EqualsLiteral("id")) {
1602 mCurrentMessage
->mLastEventID
= Some(mLastFieldValue
);
1607 if (mLastFieldName
.EqualsLiteral("retry")) {
1608 uint32_t newValue
= 0;
1609 uint32_t i
= 0; // we must ensure that there are only digits
1611 for (i
= 0; i
< mLastFieldValue
.Length(); ++i
) {
1612 if (mLastFieldValue
.CharAt(i
) < (char16_t
)'0' ||
1613 mLastFieldValue
.CharAt(i
) > (char16_t
)'9') {
1617 newValue
= newValue
* 10 + (((uint32_t)mLastFieldValue
.CharAt(i
)) -
1618 ((uint32_t)((char16_t
)'0')));
1622 if (newValue
< MIN_RECONNECTION_TIME_VALUE
) {
1623 mReconnectionTime
= MIN_RECONNECTION_TIME_VALUE
;
1624 } else if (newValue
> MAX_RECONNECTION_TIME_VALUE
) {
1625 mReconnectionTime
= MAX_RECONNECTION_TIME_VALUE
;
1627 mReconnectionTime
= newValue
;
1635 mLastFieldName
.Truncate();
1636 mLastFieldValue
.Truncate();
1641 nsresult
EventSourceImpl::CheckHealthOfRequestCallback(
1642 nsIRequest
* aRequestCallback
) {
1643 // This function could be run on target thread if http channel support
1644 // nsIThreadRetargetableRequest. otherwise, it's run on main thread.
1646 // check if we have been closed or if the request has been canceled
1647 // or if we have been frozen
1648 if (IsClosed() || mFrozen
|| !mHttpChannel
) {
1649 return NS_ERROR_ABORT
;
1652 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aRequestCallback
);
1653 NS_ENSURE_STATE(httpChannel
);
1655 if (httpChannel
!= mHttpChannel
) {
1656 NS_WARNING("wrong channel from request callback");
1657 return NS_ERROR_ABORT
;
1663 nsresult
EventSourceImpl::ParseCharacter(char16_t aChr
) {
1664 AssertIsOnTargetThread();
1668 return NS_ERROR_ABORT
;
1672 case PARSE_STATE_OFF
:
1673 NS_ERROR("Invalid state");
1674 return NS_ERROR_FAILURE
;
1677 case PARSE_STATE_BEGIN_OF_STREAM
:
1678 if (aChr
== CR_CHAR
) {
1679 mStatus
= PARSE_STATE_CR_CHAR
;
1680 } else if (aChr
== LF_CHAR
) {
1681 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1682 } else if (aChr
== COLON_CHAR
) {
1683 mStatus
= PARSE_STATE_COMMENT
;
1685 mLastFieldName
+= aChr
;
1686 mStatus
= PARSE_STATE_FIELD_NAME
;
1690 case PARSE_STATE_CR_CHAR
:
1691 if (aChr
== CR_CHAR
) {
1692 rv
= DispatchCurrentMessageEvent(); // there is an empty line (CRCR)
1693 NS_ENSURE_SUCCESS(rv
, rv
);
1694 } else if (aChr
== LF_CHAR
) {
1695 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1696 } else if (aChr
== COLON_CHAR
) {
1697 mStatus
= PARSE_STATE_COMMENT
;
1699 mLastFieldName
+= aChr
;
1700 mStatus
= PARSE_STATE_FIELD_NAME
;
1705 case PARSE_STATE_COMMENT
:
1706 if (aChr
== CR_CHAR
) {
1707 mStatus
= PARSE_STATE_CR_CHAR
;
1708 } else if (aChr
== LF_CHAR
) {
1709 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1714 case PARSE_STATE_FIELD_NAME
:
1715 if (aChr
== CR_CHAR
) {
1716 rv
= SetFieldAndClear();
1717 NS_ENSURE_SUCCESS(rv
, rv
);
1719 mStatus
= PARSE_STATE_CR_CHAR
;
1720 } else if (aChr
== LF_CHAR
) {
1721 rv
= SetFieldAndClear();
1722 NS_ENSURE_SUCCESS(rv
, rv
);
1724 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1725 } else if (aChr
== COLON_CHAR
) {
1726 mStatus
= PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE
;
1728 mLastFieldName
+= aChr
;
1733 case PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE
:
1734 if (aChr
== CR_CHAR
) {
1735 rv
= SetFieldAndClear();
1736 NS_ENSURE_SUCCESS(rv
, rv
);
1738 mStatus
= PARSE_STATE_CR_CHAR
;
1739 } else if (aChr
== LF_CHAR
) {
1740 rv
= SetFieldAndClear();
1741 NS_ENSURE_SUCCESS(rv
, rv
);
1743 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1744 } else if (aChr
== SPACE_CHAR
) {
1745 mStatus
= PARSE_STATE_FIELD_VALUE
;
1747 mLastFieldValue
+= aChr
;
1748 mStatus
= PARSE_STATE_FIELD_VALUE
;
1753 case PARSE_STATE_FIELD_VALUE
:
1754 if (aChr
== CR_CHAR
) {
1755 rv
= SetFieldAndClear();
1756 NS_ENSURE_SUCCESS(rv
, rv
);
1758 mStatus
= PARSE_STATE_CR_CHAR
;
1759 } else if (aChr
== LF_CHAR
) {
1760 rv
= SetFieldAndClear();
1761 NS_ENSURE_SUCCESS(rv
, rv
);
1763 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1764 } else if (aChr
!= 0) {
1765 // Avoid appending the null char to the field value.
1766 mLastFieldValue
+= aChr
;
1767 } else if (mLastFieldName
.EqualsLiteral("id")) {
1768 // Ignore the whole id field if aChr is null
1769 mStatus
= PARSE_STATE_IGNORE_FIELD_VALUE
;
1770 mLastFieldValue
.Truncate();
1775 case PARSE_STATE_IGNORE_FIELD_VALUE
:
1776 if (aChr
== CR_CHAR
) {
1777 mStatus
= PARSE_STATE_CR_CHAR
;
1778 } else if (aChr
== LF_CHAR
) {
1779 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1783 case PARSE_STATE_BEGIN_OF_LINE
:
1784 if (aChr
== CR_CHAR
) {
1785 rv
= DispatchCurrentMessageEvent(); // there is an empty line
1786 NS_ENSURE_SUCCESS(rv
, rv
);
1788 mStatus
= PARSE_STATE_CR_CHAR
;
1789 } else if (aChr
== LF_CHAR
) {
1790 rv
= DispatchCurrentMessageEvent(); // there is an empty line
1791 NS_ENSURE_SUCCESS(rv
, rv
);
1793 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1794 } else if (aChr
== COLON_CHAR
) {
1795 mStatus
= PARSE_STATE_COMMENT
;
1796 } else if (aChr
!= 0) {
1797 // Avoid appending the null char to the field name.
1798 mLastFieldName
+= aChr
;
1799 mStatus
= PARSE_STATE_FIELD_NAME
;
1810 class WorkerRunnableDispatcher final
: public WorkerRunnable
{
1811 RefPtr
<EventSourceImpl
> mEventSourceImpl
;
1814 WorkerRunnableDispatcher(RefPtr
<EventSourceImpl
>&& aImpl
,
1815 WorkerPrivate
* aWorkerPrivate
,
1816 already_AddRefed
<nsIRunnable
> aEvent
)
1817 : WorkerRunnable(aWorkerPrivate
, WorkerThreadUnchangedBusyCount
),
1818 mEventSourceImpl(std::move(aImpl
)),
1819 mEvent(std::move(aEvent
)) {}
1821 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
1822 aWorkerPrivate
->AssertIsOnWorkerThread();
1823 return !NS_FAILED(mEvent
->Run());
1826 void PostRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
,
1827 bool aRunResult
) override
{
1828 // Ensure we drop the RefPtr on the worker thread
1829 // and to not keep us alive longer than needed.
1830 mEventSourceImpl
= nullptr;
1833 bool PreDispatch(WorkerPrivate
* aWorkerPrivate
) override
{
1834 // We don't call WorkerRunnable::PreDispatch because it would assert the
1835 // wrong thing about which thread we're on. We're on whichever thread the
1836 // channel implementation is running on (probably the main thread or
1837 // transport thread).
1841 void PostDispatch(WorkerPrivate
* aWorkerPrivate
,
1842 bool aDispatchResult
) override
{
1843 // We don't call WorkerRunnable::PostDispatch because it would assert the
1844 // wrong thing about which thread we're on. We're on whichever thread the
1845 // channel implementation is running on (probably the main thread or
1846 // transport thread).
1850 nsCOMPtr
<nsIRunnable
> mEvent
;
1855 bool EventSourceImpl::CreateWorkerRef(WorkerPrivate
* aWorkerPrivate
) {
1856 MOZ_ASSERT(!mWorkerRef
);
1857 MOZ_ASSERT(aWorkerPrivate
);
1858 aWorkerPrivate
->AssertIsOnWorkerThread();
1864 RefPtr
<EventSourceImpl
> self
= this;
1865 RefPtr
<StrongWorkerRef
> workerRef
= StrongWorkerRef::Create(
1866 aWorkerPrivate
, "EventSource", [self
]() { self
->Close(); });
1868 if (NS_WARN_IF(!workerRef
)) {
1872 mWorkerRef
= new ThreadSafeWorkerRef(workerRef
);
1876 void EventSourceImpl::ReleaseWorkerRef() {
1877 MOZ_ASSERT(IsClosed());
1878 MOZ_ASSERT(IsCurrentThreadRunningWorker());
1879 mWorkerRef
= nullptr;
1882 //-----------------------------------------------------------------------------
1883 // EventSourceImpl::nsIEventTarget
1884 //-----------------------------------------------------------------------------
1886 EventSourceImpl::DispatchFromScript(nsIRunnable
* aEvent
, uint32_t aFlags
) {
1887 nsCOMPtr
<nsIRunnable
> event(aEvent
);
1888 return Dispatch(event
.forget(), aFlags
);
1892 EventSourceImpl::Dispatch(already_AddRefed
<nsIRunnable
> aEvent
,
1894 nsCOMPtr
<nsIRunnable
> event_ref(aEvent
);
1895 if (mIsMainThread
) {
1896 return NS_DispatchToMainThread(event_ref
.forget());
1900 // We want to avoid clutter about errors in our shutdown logs,
1901 // so just report NS_OK (we have no explicit return value
1906 // If the target is a worker, we have to use a custom WorkerRunnableDispatcher
1908 RefPtr
<WorkerRunnableDispatcher
> event
= new WorkerRunnableDispatcher(
1909 this, mWorkerRef
->Private(), event_ref
.forget());
1911 if (!event
->Dispatch()) {
1912 return NS_ERROR_FAILURE
;
1918 EventSourceImpl::DelayedDispatch(already_AddRefed
<nsIRunnable
> aEvent
,
1919 uint32_t aDelayMs
) {
1920 return NS_ERROR_NOT_IMPLEMENTED
;
1923 //-----------------------------------------------------------------------------
1924 // EventSourceImpl::nsIThreadRetargetableStreamListener
1925 //-----------------------------------------------------------------------------
1927 EventSourceImpl::CheckListenerChain() {
1928 MOZ_ASSERT(NS_IsMainThread(), "Should be on the main thread!");
1931 ////////////////////////////////////////////////////////////////////////////////
1933 ////////////////////////////////////////////////////////////////////////////////
1935 EventSource::EventSource(nsIGlobalObject
* aGlobal
,
1936 nsICookieJarSettings
* aCookieJarSettings
,
1937 bool aWithCredentials
)
1938 : DOMEventTargetHelper(aGlobal
),
1939 mWithCredentials(aWithCredentials
),
1940 mIsMainThread(NS_IsMainThread()) {
1941 MOZ_ASSERT(aGlobal
);
1942 MOZ_ASSERT(aCookieJarSettings
);
1943 mESImpl
= new EventSourceImpl(this, aCookieJarSettings
);
1946 EventSource::~EventSource() = default;
1948 nsresult
EventSource::CreateAndDispatchSimpleEvent(const nsAString
& aName
) {
1949 RefPtr
<Event
> event
= NS_NewDOMEvent(this, nullptr, nullptr);
1950 // it doesn't bubble, and it isn't cancelable
1951 event
->InitEvent(aName
, false, false);
1952 event
->SetTrusted(true);
1954 DispatchEvent(*event
, rv
);
1955 return rv
.StealNSResult();
1959 already_AddRefed
<EventSource
> EventSource::Constructor(
1960 const GlobalObject
& aGlobal
, const nsAString
& aURL
,
1961 const EventSourceInit
& aEventSourceInitDict
, ErrorResult
& aRv
) {
1962 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(aGlobal
.GetAsSupports());
1963 if (NS_WARN_IF(!global
)) {
1964 aRv
.Throw(NS_ERROR_FAILURE
);
1968 nsCOMPtr
<nsICookieJarSettings
> cookieJarSettings
;
1969 nsCOMPtr
<nsPIDOMWindowInner
> ownerWindow
= do_QueryInterface(global
);
1971 Document
* doc
= ownerWindow
->GetExtantDoc();
1972 if (NS_WARN_IF(!doc
)) {
1973 aRv
.Throw(NS_ERROR_FAILURE
);
1977 cookieJarSettings
= doc
->CookieJarSettings();
1980 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
1981 MOZ_ASSERT(workerPrivate
);
1982 cookieJarSettings
= workerPrivate
->CookieJarSettings();
1985 RefPtr
<EventSource
> eventSource
= new EventSource(
1986 global
, cookieJarSettings
, aEventSourceInitDict
.mWithCredentials
);
1988 if (NS_IsMainThread()) {
1989 // Get principal from document and init EventSourceImpl
1990 nsCOMPtr
<nsIScriptObjectPrincipal
> scriptPrincipal
=
1991 do_QueryInterface(aGlobal
.GetAsSupports());
1992 if (!scriptPrincipal
) {
1993 aRv
.Throw(NS_ERROR_FAILURE
);
1996 nsCOMPtr
<nsIPrincipal
> principal
= scriptPrincipal
->GetPrincipal();
1998 aRv
.Throw(NS_ERROR_FAILURE
);
2001 eventSource
->mESImpl
->Init(principal
, aURL
, aRv
);
2002 if (NS_WARN_IF(aRv
.Failed())) {
2006 eventSource
->mESImpl
->InitChannelAndRequestEventSource(true);
2007 return eventSource
.forget();
2012 // Scope for possible failures that need cleanup
2013 auto guardESImpl
= MakeScopeExit([&] { eventSource
->mESImpl
= nullptr; });
2015 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
2016 MOZ_ASSERT(workerPrivate
);
2018 eventSource
->mESImpl
->mInnerWindowID
= workerPrivate
->WindowID();
2020 RefPtr
<InitRunnable
> initRunnable
=
2021 new InitRunnable(workerPrivate
, eventSource
->mESImpl
, aURL
);
2022 initRunnable
->Dispatch(Canceling
, aRv
);
2023 if (NS_WARN_IF(aRv
.Failed())) {
2027 aRv
= initRunnable
->ErrorCode();
2028 if (NS_WARN_IF(aRv
.Failed())) {
2032 // In workers we have to keep the worker alive using a WorkerRef in order
2033 // to dispatch messages correctly.
2034 if (!eventSource
->mESImpl
->CreateWorkerRef(workerPrivate
)) {
2035 // The worker is already shutting down. Let's return an already closed
2036 // object, but marked as Connecting.
2037 // mESImpl is nulled by this call such that EventSourceImpl is
2038 // released before returning the object, otherwise
2039 // it will set EventSource to a CLOSED state in its DTOR..
2040 eventSource
->mESImpl
->Close();
2041 eventSource
->mReadyState
= EventSourceImpl::CONNECTING
;
2043 return eventSource
.forget();
2046 // Let's connect to the server.
2047 RefPtr
<ConnectRunnable
> connectRunnable
=
2048 new ConnectRunnable(workerPrivate
, eventSource
->mESImpl
);
2049 connectRunnable
->Dispatch(Canceling
, aRv
);
2050 if (NS_WARN_IF(aRv
.Failed())) {
2054 // End of scope for possible failures
2055 guardESImpl
.release();
2058 return eventSource
.forget();
2062 JSObject
* EventSource::WrapObject(JSContext
* aCx
,
2063 JS::Handle
<JSObject
*> aGivenProto
) {
2064 return EventSource_Binding::Wrap(aCx
, this, aGivenProto
);
2067 void EventSource::Close() {
2068 AssertIsOnTargetThread();
2070 // Close potentially kills ourself, ensure
2071 // to not access any members afterwards.
2076 //-----------------------------------------------------------------------------
2077 // EventSource::nsISupports
2078 //-----------------------------------------------------------------------------
2080 NS_IMPL_CYCLE_COLLECTION_CLASS(EventSource
)
2082 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(EventSource
,
2083 DOMEventTargetHelper
)
2084 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2086 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(EventSource
,
2087 DOMEventTargetHelper
)
2089 // IsCertainlyaliveForCC will return true and cause the cycle
2090 // collector to skip this instance when mESImpl is non-null and
2091 // points back to ourself.
2092 // mESImpl is initialized to be non-null in the constructor
2093 // and should have been wiped out in our close function.
2094 MOZ_ASSERT_UNREACHABLE("Paranoia cleanup that should never happen.");
2097 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2099 bool EventSource::IsCertainlyAliveForCC() const {
2100 // Until we are double linked forth and back, we want to stay alive.
2104 auto lock
= mESImpl
->mSharedData
.Lock();
2105 return lock
->mEventSource
== this;
2108 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EventSource
)
2109 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
2111 NS_IMPL_ADDREF_INHERITED(EventSource
, DOMEventTargetHelper
)
2112 NS_IMPL_RELEASE_INHERITED(EventSource
, DOMEventTargetHelper
)
2114 } // namespace mozilla::dom