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/Try.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
{
60 static LazyLogModule
gEventSourceLog("EventSource");
63 #define SPACE_CHAR (char16_t)0x0020
64 #define CR_CHAR (char16_t)0x000D
65 #define LF_CHAR (char16_t)0x000A
66 #define COLON_CHAR (char16_t)0x003A
68 // Reconnection time related values in milliseconds. The default one is equal
69 // to the default value of the pref dom.server-events.default-reconnection-time
70 #define MIN_RECONNECTION_TIME_VALUE 500
71 #define DEFAULT_RECONNECTION_TIME_VALUE 5000
72 #define MAX_RECONNECTION_TIME_VALUE \
73 PR_IntervalToMilliseconds(DELAY_INTERVAL_LIMIT)
75 class EventSourceImpl final
: public nsIObserver
,
76 public nsIChannelEventSink
,
77 public nsIInterfaceRequestor
,
78 public nsSupportsWeakReference
,
79 public nsISerialEventTarget
,
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 nsISerialEventTarget
, nsIEventTarget
,
368 nsIThreadRetargetableStreamListener
, nsITimerCallback
,
371 EventSourceImpl::EventSourceImpl(EventSource
* aEventSource
,
372 nsICookieJarSettings
* aCookieJarSettings
)
373 : mReconnectionTime(0),
374 mStatus(PARSE_STATE_OFF
),
376 mGoingToDispatchAllMessages(false),
377 mIsMainThread(NS_IsMainThread()),
379 mSharedData(SharedData
{aEventSource
}, "EventSourceImpl::mSharedData"),
383 mCookieJarSettings(aCookieJarSettings
),
384 mTargetThread(NS_GetCurrentThread()) {
385 MOZ_ASSERT(aEventSource
);
386 SetReadyState(CONNECTING
);
389 class CleanupRunnable final
: public WorkerMainThreadRunnable
{
391 explicit CleanupRunnable(RefPtr
<EventSourceImpl
>&& aEventSourceImpl
)
392 : WorkerMainThreadRunnable(GetCurrentThreadWorkerPrivate(),
393 "EventSource :: Cleanup"_ns
),
394 mESImpl(std::move(aEventSourceImpl
)) {
396 mWorkerPrivate
->AssertIsOnWorkerThread();
399 bool MainThreadRun() override
{
401 mESImpl
->CleanupOnMainThread();
402 // We want to ensure the shortest possible remaining lifetime
403 // and not depend on the Runnable's destruction.
409 RefPtr
<EventSourceImpl
> mESImpl
;
412 void EventSourceImpl::Close() {
417 SetReadyState(CLOSED
);
418 // CloseInternal potentially kills ourself, ensure
419 // to not access any members afterwards.
423 void EventSourceImpl::CloseInternal() {
424 AssertIsOnTargetThread();
425 MOZ_ASSERT(IsClosed());
427 RefPtr
<EventSource
> myES
;
429 auto lock
= mSharedData
.Lock();
430 // We want to ensure to release ourself even if we have
431 // the shutdown case, thus we put aside a pointer
432 // to the EventSource and null it out right now.
433 myES
= std::move(lock
->mEventSource
);
434 lock
->mEventSource
= nullptr;
435 lock
->mServiceNotifier
= nullptr;
438 MOZ_ASSERT(!mIsShutDown
);
443 // Invoke CleanupOnMainThread before cleaning any members. It will call
444 // ShutDown, which is supposed to be called before cleaning any members.
445 if (NS_IsMainThread()) {
446 CleanupOnMainThread();
449 // run CleanupOnMainThread synchronously on main thread since it touches
450 // observers and members only can be accessed on main thread.
451 RefPtr
<CleanupRunnable
> runnable
= new CleanupRunnable(this);
452 runnable
->Dispatch(Killing
, rv
);
453 MOZ_ASSERT(!rv
.Failed());
457 while (mMessagesToDispatch
.GetSize() != 0) {
458 delete mMessagesToDispatch
.PopFront();
462 mUnicodeDecoder
= nullptr;
463 // Release the object on its owner. Don't access to any members
465 myES
->mESImpl
= nullptr;
468 void EventSourceImpl::CleanupOnMainThread() {
469 AssertIsOnMainThread();
470 MOZ_ASSERT(IsClosed());
472 // Call ShutDown before cleaning any members.
473 MOZ_ASSERT(!mIsShutDown
);
477 RemoveWindowObservers();
486 mPrincipal
= nullptr;
490 class InitRunnable final
: public WorkerMainThreadRunnable
{
492 InitRunnable(WorkerPrivate
* aWorkerPrivate
,
493 RefPtr
<EventSourceImpl
> aEventSourceImpl
, const nsAString
& aURL
)
494 : WorkerMainThreadRunnable(aWorkerPrivate
, "EventSource :: Init"_ns
),
495 mESImpl(std::move(aEventSourceImpl
)),
497 mRv(NS_ERROR_NOT_INITIALIZED
) {
498 MOZ_ASSERT(aWorkerPrivate
);
499 aWorkerPrivate
->AssertIsOnWorkerThread();
503 bool MainThreadRun() override
{
504 // Get principal from worker's owner document or from worker.
505 WorkerPrivate
* wp
= mWorkerPrivate
;
506 while (wp
->GetParent()) {
507 wp
= wp
->GetParent();
509 nsPIDOMWindowInner
* window
= wp
->GetWindow();
510 Document
* doc
= window
? window
->GetExtantDoc() : nullptr;
511 nsCOMPtr
<nsIPrincipal
> principal
=
512 doc
? doc
->NodePrincipal() : wp
->GetPrincipal();
514 mRv
= NS_ERROR_FAILURE
;
518 mESImpl
->Init(principal
, mURL
, rv
);
519 mRv
= rv
.StealNSResult();
521 // We want to ensure that EventSourceImpl's lifecycle
522 // does not depend on this Runnable's one.
528 nsresult
ErrorCode() const { return mRv
; }
531 RefPtr
<EventSourceImpl
> mESImpl
;
532 const nsAString
& mURL
;
536 class ConnectRunnable final
: public WorkerMainThreadRunnable
{
538 explicit ConnectRunnable(WorkerPrivate
* aWorkerPrivate
,
539 RefPtr
<EventSourceImpl
> aEventSourceImpl
)
540 : WorkerMainThreadRunnable(aWorkerPrivate
, "EventSource :: Connect"_ns
),
541 mESImpl(std::move(aEventSourceImpl
)) {
542 MOZ_ASSERT(aWorkerPrivate
);
543 aWorkerPrivate
->AssertIsOnWorkerThread();
547 bool MainThreadRun() override
{
549 // We are allowed to access the event target since this runnable is
550 // synchronized with the thread the event target lives on.
551 mESImpl
->InitChannelAndRequestEventSource(true);
552 // We want to ensure the shortest possible remaining lifetime
553 // and not depend on the Runnable's destruction.
559 RefPtr
<EventSourceImpl
> mESImpl
;
562 nsresult
EventSourceImpl::ParseURL(const nsAString
& aURL
) {
563 AssertIsOnMainThread();
564 MOZ_ASSERT(!mIsShutDown
);
566 nsCOMPtr
<nsIURI
> baseURI
;
567 nsresult rv
= GetBaseURI(getter_AddRefs(baseURI
));
568 NS_ENSURE_SUCCESS(rv
, rv
);
570 nsCOMPtr
<nsIURI
> srcURI
;
571 nsCOMPtr
<Document
> doc
=
572 mIsMainThread
? GetEventSource()->GetDocumentIfCurrent() : nullptr;
574 rv
= NS_NewURI(getter_AddRefs(srcURI
), aURL
, doc
->GetDocumentCharacterSet(),
577 rv
= NS_NewURI(getter_AddRefs(srcURI
), aURL
, nullptr, baseURI
);
580 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SYNTAX_ERR
);
583 rv
= nsContentUtils::GetWebExposedOriginSerialization(srcURI
, origin
);
584 NS_ENSURE_SUCCESS(rv
, rv
);
587 rv
= srcURI
->GetSpec(spec
);
588 NS_ENSURE_SUCCESS(rv
, rv
);
590 // This assignment doesn't require extra synchronization because this function
591 // is only ever called from EventSourceImpl::Init(), which is either called
592 // directly if mEventSource was created on the main thread, or via a
593 // synchronous runnable if it was created on a worker thread.
595 // We can't use GetEventSource() here because it would modify the refcount,
596 // and that's not allowed off the owning thread.
597 auto lock
= mSharedData
.Lock();
598 lock
->mEventSource
->mOriginalURL
= NS_ConvertUTF8toUTF16(spec
);
605 nsresult
EventSourceImpl::AddWindowObservers() {
606 AssertIsOnMainThread();
607 MOZ_ASSERT(mIsMainThread
);
608 MOZ_ASSERT(!mIsShutDown
);
609 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
612 nsresult rv
= os
->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC
, true);
613 NS_ENSURE_SUCCESS(rv
, rv
);
614 rv
= os
->AddObserver(this, DOM_WINDOW_FROZEN_TOPIC
, true);
615 NS_ENSURE_SUCCESS(rv
, rv
);
616 rv
= os
->AddObserver(this, DOM_WINDOW_THAWED_TOPIC
, true);
617 NS_ENSURE_SUCCESS(rv
, rv
);
621 void EventSourceImpl::RemoveWindowObservers() {
622 AssertIsOnMainThread();
623 MOZ_ASSERT(mIsMainThread
);
624 MOZ_ASSERT(IsClosed());
625 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
627 os
->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC
);
628 os
->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC
);
629 os
->RemoveObserver(this, DOM_WINDOW_THAWED_TOPIC
);
633 void EventSourceImpl::Init(nsIPrincipal
* aPrincipal
, const nsAString
& aURL
,
635 AssertIsOnMainThread();
636 MOZ_ASSERT(aPrincipal
);
637 MOZ_ASSERT(ReadyState() == CONNECTING
);
638 mPrincipal
= aPrincipal
;
639 aRv
= ParseURL(aURL
);
640 if (NS_WARN_IF(aRv
.Failed())) {
643 // The conditional here is historical and not necessarily sane.
644 if (JSContext
* cx
= nsContentUtils::GetCurrentJSContext()) {
645 nsJSUtils::GetCallingLocation(cx
, mScriptFile
, &mScriptLine
,
647 mInnerWindowID
= nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx
);
651 // we observe when the window freezes and thaws
652 aRv
= AddWindowObservers();
653 if (NS_WARN_IF(aRv
.Failed())) {
659 Preferences::GetInt("dom.server-events.default-reconnection-time",
660 DEFAULT_RECONNECTION_TIME_VALUE
);
662 mUnicodeDecoder
= UTF_8_ENCODING
->NewDecoderWithBOMRemoval();
665 //-----------------------------------------------------------------------------
666 // EventSourceImpl::nsIObserver
667 //-----------------------------------------------------------------------------
670 EventSourceImpl::Observe(nsISupports
* aSubject
, const char* aTopic
,
671 const char16_t
* aData
) {
672 AssertIsOnMainThread();
677 nsCOMPtr
<nsPIDOMWindowInner
> window
= do_QueryInterface(aSubject
);
678 MOZ_ASSERT(mIsMainThread
);
680 auto lock
= mSharedData
.Lock();
681 if (!lock
->mEventSource
->GetOwner() ||
682 window
!= lock
->mEventSource
->GetOwner()) {
687 DebugOnly
<nsresult
> rv
;
688 if (strcmp(aTopic
, DOM_WINDOW_FROZEN_TOPIC
) == 0) {
690 MOZ_ASSERT(NS_SUCCEEDED(rv
), "Freeze() failed");
691 } else if (strcmp(aTopic
, DOM_WINDOW_THAWED_TOPIC
) == 0) {
693 MOZ_ASSERT(NS_SUCCEEDED(rv
), "Thaw() failed");
694 } else if (strcmp(aTopic
, DOM_WINDOW_DESTROYED_TOPIC
) == 0) {
701 //-----------------------------------------------------------------------------
702 // EventSourceImpl::nsIStreamListener
703 //-----------------------------------------------------------------------------
706 EventSourceImpl::OnStartRequest(nsIRequest
* aRequest
) {
707 AssertIsOnMainThread();
709 return NS_ERROR_ABORT
;
711 nsresult rv
= CheckHealthOfRequestCallback(aRequest
);
712 NS_ENSURE_SUCCESS(rv
, rv
);
714 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aRequest
, &rv
);
715 NS_ENSURE_SUCCESS(rv
, rv
);
718 rv
= aRequest
->GetStatus(&status
);
719 NS_ENSURE_SUCCESS(rv
, rv
);
721 if (NS_FAILED(status
)) {
722 // EventSource::OnStopRequest will evaluate if it shall either reestablish
723 // or fail the connection, based on the status.
728 rv
= httpChannel
->GetResponseStatus(&httpStatus
);
729 NS_ENSURE_SUCCESS(rv
, rv
);
731 if (httpStatus
!= 200) {
732 DispatchFailConnection();
733 return NS_ERROR_ABORT
;
736 nsAutoCString contentType
;
737 rv
= httpChannel
->GetContentType(contentType
);
738 NS_ENSURE_SUCCESS(rv
, rv
);
740 if (!contentType
.EqualsLiteral(TEXT_EVENT_STREAM
)) {
741 DispatchFailConnection();
742 return NS_ERROR_ABORT
;
745 if (!mIsMainThread
) {
746 // Try to retarget to worker thread, otherwise fall back to main thread.
747 nsCOMPtr
<nsIThreadRetargetableRequest
> rr
= do_QueryInterface(httpChannel
);
749 rv
= rr
->RetargetDeliveryTo(this);
750 if (NS_WARN_IF(NS_FAILED(rv
))) {
751 NS_WARNING("Retargeting failed");
757 auto lock
= mSharedData
.Lock();
758 lock
->mServiceNotifier
= MakeUnique
<EventSourceServiceNotifier
>(
759 this, mHttpChannel
->ChannelId(), mInnerWindowID
);
761 rv
= Dispatch(NewRunnableMethod("dom::EventSourceImpl::AnnounceConnection",
762 this, &EventSourceImpl::AnnounceConnection
),
764 NS_ENSURE_SUCCESS(rv
, rv
);
765 mStatus
= PARSE_STATE_BEGIN_OF_STREAM
;
769 // this method parses the characters as they become available instead of
771 nsresult
EventSourceImpl::StreamReaderFunc(nsIInputStream
* aInputStream
,
773 const char* aFromRawSegment
,
774 uint32_t aToOffset
, uint32_t aCount
,
775 uint32_t* aWriteCount
) {
776 // The EventSourceImpl instance is hold alive on the
777 // synchronously calling stack, so raw pointer is fine here.
778 EventSourceImpl
* thisObject
= static_cast<EventSourceImpl
*>(aClosure
);
779 if (!thisObject
|| !aWriteCount
) {
781 "EventSource cannot read from stream: no aClosure or aWriteCount");
782 return NS_ERROR_FAILURE
;
784 thisObject
->AssertIsOnTargetThread();
785 MOZ_ASSERT(!thisObject
->mIsShutDown
);
786 thisObject
->ParseSegment((const char*)aFromRawSegment
, aCount
);
787 *aWriteCount
= aCount
;
791 void EventSourceImpl::ParseSegment(const char* aBuffer
, uint32_t aLength
) {
792 AssertIsOnTargetThread();
796 char16_t buffer
[1024];
797 auto dst
= Span(buffer
);
798 auto src
= AsBytes(Span(aBuffer
, aLength
));
799 // XXX EOF handling is https://bugzilla.mozilla.org/show_bug.cgi?id=1369018
804 std::tie(result
, read
, written
, std::ignore
) =
805 mUnicodeDecoder
->DecodeToUTF16(src
, dst
, false);
806 for (auto c
: dst
.To(written
)) {
807 nsresult rv
= ParseCharacter(c
);
808 NS_ENSURE_SUCCESS_VOID(rv
);
810 if (result
== kInputEmpty
) {
813 src
= src
.From(read
);
818 EventSourceImpl::OnDataAvailable(nsIRequest
* aRequest
,
819 nsIInputStream
* aInputStream
, uint64_t aOffset
,
821 AssertIsOnTargetThread();
822 NS_ENSURE_ARG_POINTER(aInputStream
);
824 return NS_ERROR_ABORT
;
827 nsresult rv
= CheckHealthOfRequestCallback(aRequest
);
828 NS_ENSURE_SUCCESS(rv
, rv
);
831 return aInputStream
->ReadSegments(EventSourceImpl::StreamReaderFunc
, this,
836 EventSourceImpl::OnStopRequest(nsIRequest
* aRequest
, nsresult aStatusCode
) {
837 AssertIsOnMainThread();
840 return NS_ERROR_ABORT
;
843 // "Network errors that prevents the connection from being established in the
844 // first place (e.g. DNS errors), must cause the user agent to asynchronously
845 // reestablish the connection.
847 // (...) the cancelation of the fetch algorithm by the user agent (e.g. in
848 // response to window.stop() or the user canceling the network connection
849 // manually) must cause the user agent to fail the connection.
850 // There could be additional network errors that are not covered in the above
853 if (NS_FAILED(aStatusCode
) && aStatusCode
!= NS_ERROR_CONNECTION_REFUSED
&&
854 aStatusCode
!= NS_ERROR_NET_TIMEOUT
&&
855 aStatusCode
!= NS_ERROR_NET_RESET
&&
856 aStatusCode
!= NS_ERROR_NET_INTERRUPT
&&
857 aStatusCode
!= NS_ERROR_NET_PARTIAL_TRANSFER
&&
858 aStatusCode
!= NS_ERROR_NET_TIMEOUT_EXTERNAL
&&
859 aStatusCode
!= NS_ERROR_PROXY_CONNECTION_REFUSED
&&
860 aStatusCode
!= NS_ERROR_DNS_LOOKUP_QUEUE_FULL
&&
861 aStatusCode
!= NS_ERROR_INVALID_CONTENT_ENCODING
) {
862 DispatchFailConnection();
863 return NS_ERROR_ABORT
;
866 nsresult rv
= CheckHealthOfRequestCallback(aRequest
);
867 NS_ENSURE_SUCCESS(rv
, rv
);
870 Dispatch(NewRunnableMethod("dom::EventSourceImpl::ReestablishConnection",
871 this, &EventSourceImpl::ReestablishConnection
),
873 NS_ENSURE_SUCCESS(rv
, rv
);
878 //-----------------------------------------------------------------------------
879 // EventSourceImpl::nsIChannelEventSink
880 //-----------------------------------------------------------------------------
883 EventSourceImpl::AsyncOnChannelRedirect(
884 nsIChannel
* aOldChannel
, nsIChannel
* aNewChannel
, uint32_t aFlags
,
885 nsIAsyncVerifyRedirectCallback
* aCallback
) {
886 AssertIsOnMainThread();
888 return NS_ERROR_ABORT
;
890 nsCOMPtr
<nsIRequest
> aOldRequest
= aOldChannel
;
891 MOZ_ASSERT(aOldRequest
, "Redirect from a null request?");
893 nsresult rv
= CheckHealthOfRequestCallback(aOldRequest
);
894 NS_ENSURE_SUCCESS(rv
, rv
);
896 MOZ_ASSERT(aNewChannel
, "Redirect without a channel?");
898 nsCOMPtr
<nsIURI
> newURI
;
899 rv
= NS_GetFinalChannelURI(aNewChannel
, getter_AddRefs(newURI
));
900 NS_ENSURE_SUCCESS(rv
, rv
);
902 bool isValidScheme
= newURI
->SchemeIs("http") || newURI
->SchemeIs("https");
905 mIsMainThread
? GetEventSource()->CheckCurrentGlobalCorrectness() : NS_OK
;
906 if (NS_FAILED(rv
) || !isValidScheme
) {
907 DispatchFailConnection();
908 return NS_ERROR_DOM_SECURITY_ERR
;
911 // update our channel
913 mHttpChannel
= do_QueryInterface(aNewChannel
);
914 NS_ENSURE_STATE(mHttpChannel
);
917 // The HTTP impl already copies over the referrer info on
918 // redirects, so we don't need to SetupReferrerInfo().
920 if ((aFlags
& nsIChannelEventSink::REDIRECT_PERMANENT
) != 0) {
921 rv
= NS_GetFinalChannelURI(mHttpChannel
, getter_AddRefs(mSrc
));
922 NS_ENSURE_SUCCESS(rv
, rv
);
925 aCallback
->OnRedirectVerifyCallback(NS_OK
);
930 //-----------------------------------------------------------------------------
931 // EventSourceImpl::nsIInterfaceRequestor
932 //-----------------------------------------------------------------------------
935 EventSourceImpl::GetInterface(const nsIID
& aIID
, void** aResult
) {
936 AssertIsOnMainThread();
939 return NS_ERROR_FAILURE
;
942 if (aIID
.Equals(NS_GET_IID(nsIChannelEventSink
))) {
943 *aResult
= static_cast<nsIChannelEventSink
*>(this);
948 if (aIID
.Equals(NS_GET_IID(nsIAuthPrompt
)) ||
949 aIID
.Equals(NS_GET_IID(nsIAuthPrompt2
))) {
951 nsCOMPtr
<nsIPromptFactory
> wwatch
=
952 do_GetService(NS_WINDOWWATCHER_CONTRACTID
, &rv
);
953 NS_ENSURE_SUCCESS(rv
, rv
);
955 nsCOMPtr
<nsPIDOMWindowOuter
> window
;
957 // To avoid a data race we may only access the event target if it lives on
960 auto lock
= mSharedData
.Lock();
961 rv
= lock
->mEventSource
->CheckCurrentGlobalCorrectness();
962 NS_ENSURE_SUCCESS(rv
, NS_ERROR_UNEXPECTED
);
964 if (lock
->mEventSource
->GetOwner()) {
965 window
= lock
->mEventSource
->GetOwner()->GetOuterWindow();
969 // Get the an auth prompter for our window so that the parenting
970 // of the dialogs works as it should when using tabs.
972 return wwatch
->GetPrompt(window
, aIID
, aResult
);
975 return QueryInterface(aIID
, aResult
);
979 EventSourceImpl::IsOnCurrentThread(bool* aResult
) {
980 *aResult
= IsTargetThread();
985 EventSourceImpl::IsOnCurrentThreadInfallible() { return IsTargetThread(); }
987 nsresult
EventSourceImpl::GetBaseURI(nsIURI
** aBaseURI
) {
988 AssertIsOnMainThread();
989 MOZ_ASSERT(!mIsShutDown
);
990 NS_ENSURE_ARG_POINTER(aBaseURI
);
994 nsCOMPtr
<nsIURI
> baseURI
;
996 // first we try from document->GetBaseURI()
997 nsCOMPtr
<Document
> doc
=
998 mIsMainThread
? GetEventSource()->GetDocumentIfCurrent() : nullptr;
1000 baseURI
= doc
->GetBaseURI();
1003 // otherwise we get from the doc's principal
1005 auto* basePrin
= BasePrincipal::Cast(mPrincipal
);
1006 nsresult rv
= basePrin
->GetURI(getter_AddRefs(baseURI
));
1007 NS_ENSURE_SUCCESS(rv
, rv
);
1010 NS_ENSURE_STATE(baseURI
);
1012 baseURI
.forget(aBaseURI
);
1016 void EventSourceImpl::SetupHttpChannel() {
1017 AssertIsOnMainThread();
1018 MOZ_ASSERT(!mIsShutDown
);
1019 nsresult rv
= mHttpChannel
->SetRequestMethod("GET"_ns
);
1020 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1022 /* set the http request headers */
1024 rv
= mHttpChannel
->SetRequestHeader(
1025 "Accept"_ns
, nsLiteralCString(TEXT_EVENT_STREAM
), false);
1026 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1028 // LOAD_BYPASS_CACHE already adds the Cache-Control: no-cache header
1030 if (mLastEventID
.IsEmpty()) {
1033 NS_ConvertUTF16toUTF8
eventId(mLastEventID
);
1034 rv
= mHttpChannel
->SetRequestHeader("Last-Event-ID"_ns
, eventId
, false);
1036 if (NS_FAILED(rv
)) {
1037 MOZ_LOG(gEventSourceLog
, LogLevel::Warning
,
1038 ("SetupHttpChannel. rv=%x (%s)", uint32_t(rv
), eventId
.get()));
1044 nsresult
EventSourceImpl::SetupReferrerInfo(
1045 const nsCOMPtr
<Document
>& aDocument
) {
1046 AssertIsOnMainThread();
1047 MOZ_ASSERT(!mIsShutDown
);
1050 auto referrerInfo
= MakeRefPtr
<ReferrerInfo
>(*aDocument
);
1051 nsresult rv
= mHttpChannel
->SetReferrerInfoWithoutClone(referrerInfo
);
1052 NS_ENSURE_SUCCESS(rv
, rv
);
1058 nsresult
EventSourceImpl::InitChannelAndRequestEventSource(
1059 const bool aEventTargetAccessAllowed
) {
1060 AssertIsOnMainThread();
1062 return NS_ERROR_ABORT
;
1065 bool isValidScheme
= mSrc
->SchemeIs("http") || mSrc
->SchemeIs("https");
1067 MOZ_ASSERT_IF(mIsMainThread
, aEventTargetAccessAllowed
);
1069 nsresult rv
= aEventTargetAccessAllowed
? [this]() {
1070 // We can't call GetEventSource() because we're not
1071 // allowed to touch the refcount off the worker thread
1072 // due to an assertion, event if it would have otherwise
1074 auto lock
= mSharedData
.Lock();
1075 return lock
->mEventSource
->CheckCurrentGlobalCorrectness();
1078 if (NS_FAILED(rv
) || !isValidScheme
) {
1079 DispatchFailConnection();
1080 return NS_ERROR_DOM_SECURITY_ERR
;
1083 nsCOMPtr
<Document
> doc
;
1084 nsSecurityFlags securityFlags
=
1085 nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT
;
1087 auto lock
= mSharedData
.Lock();
1088 doc
= aEventTargetAccessAllowed
? lock
->mEventSource
->GetDocumentIfCurrent()
1091 if (lock
->mEventSource
->mWithCredentials
) {
1092 securityFlags
|= nsILoadInfo::SEC_COOKIES_INCLUDE
;
1096 // The html spec requires we use fetch cache mode of "no-store". This
1097 // maps to LOAD_BYPASS_CACHE and LOAD_INHIBIT_CACHING in necko.
1098 nsLoadFlags loadFlags
;
1099 loadFlags
= nsIRequest::LOAD_BACKGROUND
| nsIRequest::LOAD_BYPASS_CACHE
|
1100 nsIRequest::INHIBIT_CACHING
;
1102 nsCOMPtr
<nsIChannel
> channel
;
1103 // If we have the document, use it
1105 MOZ_ASSERT(mCookieJarSettings
== doc
->CookieJarSettings());
1107 nsCOMPtr
<nsILoadGroup
> loadGroup
= doc
->GetDocumentLoadGroup();
1108 rv
= NS_NewChannel(getter_AddRefs(channel
), mSrc
, doc
, securityFlags
,
1109 nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE
,
1110 nullptr, // aPerformanceStorage
1112 nullptr, // aCallbacks
1113 loadFlags
); // aLoadFlags
1115 // otherwise use the principal
1116 rv
= NS_NewChannel(getter_AddRefs(channel
), mSrc
, mPrincipal
, securityFlags
,
1117 nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE
,
1119 nullptr, // aPerformanceStorage
1120 nullptr, // loadGroup
1121 nullptr, // aCallbacks
1122 loadFlags
); // aLoadFlags
1125 NS_ENSURE_SUCCESS(rv
, rv
);
1127 mHttpChannel
= do_QueryInterface(channel
);
1128 NS_ENSURE_TRUE(mHttpChannel
, NS_ERROR_NO_INTERFACE
);
1131 rv
= SetupReferrerInfo(doc
);
1132 NS_ENSURE_SUCCESS(rv
, rv
);
1136 nsCOMPtr
<nsIInterfaceRequestor
> notificationCallbacks
;
1137 mHttpChannel
->GetNotificationCallbacks(
1138 getter_AddRefs(notificationCallbacks
));
1139 MOZ_ASSERT(!notificationCallbacks
);
1143 mHttpChannel
->SetNotificationCallbacks(this);
1145 // Start reading from the channel
1146 rv
= mHttpChannel
->AsyncOpen(this);
1147 if (NS_FAILED(rv
)) {
1148 DispatchFailConnection();
1155 void EventSourceImpl::AnnounceConnection() {
1156 AssertIsOnTargetThread();
1157 if (ReadyState() != CONNECTING
) {
1158 NS_WARNING("Unexpected mReadyState!!!");
1163 auto lock
= mSharedData
.Lock();
1164 if (lock
->mServiceNotifier
) {
1165 lock
->mServiceNotifier
->ConnectionOpened();
1169 // When a user agent is to announce the connection, the user agent must set
1170 // the readyState attribute to OPEN and queue a task to fire a simple event
1171 // named open at the EventSource object.
1173 SetReadyState(OPEN
);
1175 nsresult rv
= GetEventSource()->CheckCurrentGlobalCorrectness();
1176 if (NS_FAILED(rv
)) {
1179 // We can't hold the mutex while dispatching the event because the mutex is
1180 // not reentrant, and content might call back into our code.
1181 rv
= GetEventSource()->CreateAndDispatchSimpleEvent(u
"open"_ns
);
1182 if (NS_FAILED(rv
)) {
1183 NS_WARNING("Failed to dispatch the error event!!!");
1188 nsresult
EventSourceImpl::ResetConnection() {
1189 AssertIsOnMainThread();
1191 mHttpChannel
->Cancel(NS_ERROR_ABORT
);
1192 mHttpChannel
= nullptr;
1197 void EventSourceImpl::ResetDecoder() {
1198 AssertIsOnTargetThread();
1199 if (mUnicodeDecoder
) {
1200 UTF_8_ENCODING
->NewDecoderWithBOMRemovalInto(*mUnicodeDecoder
);
1202 mStatus
= PARSE_STATE_OFF
;
1206 class CallRestartConnection final
: public WorkerMainThreadRunnable
{
1208 explicit CallRestartConnection(RefPtr
<EventSourceImpl
>&& aEventSourceImpl
)
1209 : WorkerMainThreadRunnable(aEventSourceImpl
->mWorkerRef
->Private(),
1210 "EventSource :: RestartConnection"_ns
),
1211 mESImpl(std::move(aEventSourceImpl
)) {
1212 mWorkerPrivate
->AssertIsOnWorkerThread();
1213 MOZ_ASSERT(mESImpl
);
1216 bool MainThreadRun() override
{
1217 MOZ_ASSERT(mESImpl
);
1218 mESImpl
->RestartConnection();
1219 // We want to ensure the shortest possible remaining lifetime
1220 // and not depend on the Runnable's destruction.
1226 RefPtr
<EventSourceImpl
> mESImpl
;
1229 nsresult
EventSourceImpl::RestartConnection() {
1230 AssertIsOnMainThread();
1232 return NS_ERROR_ABORT
;
1235 nsresult rv
= ResetConnection();
1236 NS_ENSURE_SUCCESS(rv
, rv
);
1237 rv
= SetReconnectionTimeout();
1238 NS_ENSURE_SUCCESS(rv
, rv
);
1242 void EventSourceImpl::ReestablishConnection() {
1243 AssertIsOnTargetThread();
1249 if (mIsMainThread
) {
1250 rv
= RestartConnection();
1252 RefPtr
<CallRestartConnection
> runnable
= new CallRestartConnection(this);
1254 runnable
->Dispatch(Canceling
, result
);
1255 MOZ_ASSERT(!result
.Failed());
1256 rv
= result
.StealNSResult();
1258 if (NS_FAILED(rv
)) {
1262 rv
= GetEventSource()->CheckCurrentGlobalCorrectness();
1263 if (NS_FAILED(rv
)) {
1267 SetReadyState(CONNECTING
);
1269 // We can't hold the mutex while dispatching the event because the mutex is
1270 // not reentrant, and content might call back into our code.
1271 rv
= GetEventSource()->CreateAndDispatchSimpleEvent(u
"error"_ns
);
1272 if (NS_FAILED(rv
)) {
1273 NS_WARNING("Failed to dispatch the error event!!!");
1278 nsresult
EventSourceImpl::SetReconnectionTimeout() {
1279 AssertIsOnMainThread();
1281 return NS_ERROR_ABORT
;
1284 // the timer will be used whenever the requests are going finished.
1286 mTimer
= NS_NewTimer();
1287 NS_ENSURE_STATE(mTimer
);
1290 MOZ_TRY(mTimer
->InitWithCallback(this, mReconnectionTime
,
1291 nsITimer::TYPE_ONE_SHOT
));
1296 nsresult
EventSourceImpl::PrintErrorOnConsole(
1297 const char* aBundleURI
, const char* aError
,
1298 const nsTArray
<nsString
>& aFormatStrings
) {
1299 AssertIsOnMainThread();
1300 MOZ_ASSERT(!mIsShutDown
);
1301 nsCOMPtr
<nsIStringBundleService
> bundleService
=
1302 mozilla::components::StringBundle::Service();
1303 NS_ENSURE_STATE(bundleService
);
1305 nsCOMPtr
<nsIStringBundle
> strBundle
;
1307 bundleService
->CreateBundle(aBundleURI
, getter_AddRefs(strBundle
));
1308 NS_ENSURE_SUCCESS(rv
, rv
);
1310 nsCOMPtr
<nsIConsoleService
> console(
1311 do_GetService(NS_CONSOLESERVICE_CONTRACTID
, &rv
));
1312 NS_ENSURE_SUCCESS(rv
, rv
);
1314 nsCOMPtr
<nsIScriptError
> errObj(
1315 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID
, &rv
));
1316 NS_ENSURE_SUCCESS(rv
, rv
);
1318 // Localize the error message
1319 nsAutoString message
;
1320 if (!aFormatStrings
.IsEmpty()) {
1321 rv
= strBundle
->FormatStringFromName(aError
, aFormatStrings
, message
);
1323 rv
= strBundle
->GetStringFromName(aError
, message
);
1325 NS_ENSURE_SUCCESS(rv
, rv
);
1327 rv
= errObj
->InitWithWindowID(message
, mScriptFile
, u
""_ns
, mScriptLine
,
1328 mScriptColumn
, nsIScriptError::errorFlag
,
1329 "Event Source", mInnerWindowID
);
1330 NS_ENSURE_SUCCESS(rv
, rv
);
1332 // print the error message directly to the JS console
1333 rv
= console
->LogMessage(errObj
);
1334 NS_ENSURE_SUCCESS(rv
, rv
);
1339 nsresult
EventSourceImpl::ConsoleError() {
1340 AssertIsOnMainThread();
1341 MOZ_ASSERT(!mIsShutDown
);
1342 nsAutoCString targetSpec
;
1343 nsresult rv
= mSrc
->GetSpec(targetSpec
);
1344 NS_ENSURE_SUCCESS(rv
, rv
);
1346 AutoTArray
<nsString
, 1> formatStrings
;
1347 CopyUTF8toUTF16(targetSpec
, *formatStrings
.AppendElement());
1349 if (ReadyState() == CONNECTING
) {
1350 rv
= PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
1351 "connectionFailure", formatStrings
);
1353 rv
= PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
1354 "netInterrupt", formatStrings
);
1356 NS_ENSURE_SUCCESS(rv
, rv
);
1361 void EventSourceImpl::DispatchFailConnection() {
1362 AssertIsOnMainThread();
1366 nsresult rv
= ConsoleError();
1367 if (NS_FAILED(rv
)) {
1368 NS_WARNING("Failed to print to the console error");
1370 rv
= Dispatch(NewRunnableMethod("dom::EventSourceImpl::FailConnection", this,
1371 &EventSourceImpl::FailConnection
),
1372 NS_DISPATCH_NORMAL
);
1373 if (NS_WARN_IF(NS_FAILED(rv
))) {
1374 // if the worker is shutting down, the dispatching of normal WorkerRunnables
1380 void EventSourceImpl::FailConnection() {
1381 AssertIsOnTargetThread();
1385 // Must change state to closed before firing event to content.
1386 SetReadyState(CLOSED
);
1387 // When a user agent is to fail the connection, the user agent must set the
1388 // readyState attribute to CLOSED and queue a task to fire a simple event
1389 // named error at the EventSource object.
1390 nsresult rv
= GetEventSource()->CheckCurrentGlobalCorrectness();
1391 if (NS_SUCCEEDED(rv
)) {
1392 // We can't hold the mutex while dispatching the event because the mutex
1393 // is not reentrant, and content might call back into our code.
1394 rv
= GetEventSource()->CreateAndDispatchSimpleEvent(u
"error"_ns
);
1395 if (NS_FAILED(rv
)) {
1396 NS_WARNING("Failed to dispatch the error event!!!");
1399 // Call CloseInternal in the end of function because it may release
1404 NS_IMETHODIMP
EventSourceImpl::Notify(nsITimer
* aTimer
) {
1405 AssertIsOnMainThread();
1410 MOZ_ASSERT(!mHttpChannel
, "the channel hasn't been cancelled!!");
1413 nsresult rv
= InitChannelAndRequestEventSource(mIsMainThread
);
1414 if (NS_FAILED(rv
)) {
1415 NS_WARNING("InitChannelAndRequestEventSource() failed");
1421 NS_IMETHODIMP
EventSourceImpl::GetName(nsACString
& aName
) {
1422 aName
.AssignLiteral("EventSourceImpl");
1426 nsresult
EventSourceImpl::Thaw() {
1427 AssertIsOnMainThread();
1428 if (IsClosed() || !mFrozen
) {
1432 MOZ_ASSERT(!mHttpChannel
, "the connection hasn't been closed!!!");
1436 if (!mGoingToDispatchAllMessages
&& mMessagesToDispatch
.GetSize() > 0) {
1437 nsCOMPtr
<nsIRunnable
> event
=
1438 NewRunnableMethod("dom::EventSourceImpl::DispatchAllMessageEvents",
1439 this, &EventSourceImpl::DispatchAllMessageEvents
);
1440 NS_ENSURE_STATE(event
);
1442 mGoingToDispatchAllMessages
= true;
1444 rv
= Dispatch(event
.forget(), NS_DISPATCH_NORMAL
);
1445 NS_ENSURE_SUCCESS(rv
, rv
);
1448 rv
= InitChannelAndRequestEventSource(mIsMainThread
);
1449 NS_ENSURE_SUCCESS(rv
, rv
);
1454 nsresult
EventSourceImpl::Freeze() {
1455 AssertIsOnMainThread();
1456 if (IsClosed() || mFrozen
) {
1460 MOZ_ASSERT(!mHttpChannel
, "the connection hasn't been closed!!!");
1465 nsresult
EventSourceImpl::DispatchCurrentMessageEvent() {
1466 AssertIsOnTargetThread();
1467 MOZ_ASSERT(!mIsShutDown
);
1468 UniquePtr
<Message
> message(std::move(mCurrentMessage
));
1471 if (!message
|| message
->mData
.IsEmpty()) {
1475 // removes the trailing LF from mData
1476 MOZ_ASSERT(message
->mData
.CharAt(message
->mData
.Length() - 1) == LF_CHAR
,
1477 "Invalid trailing character! LF was expected instead.");
1478 message
->mData
.SetLength(message
->mData
.Length() - 1);
1480 if (message
->mEventName
.IsEmpty()) {
1481 message
->mEventName
.AssignLiteral("message");
1484 mMessagesToDispatch
.Push(message
.release());
1486 if (!mGoingToDispatchAllMessages
) {
1487 nsCOMPtr
<nsIRunnable
> event
=
1488 NewRunnableMethod("dom::EventSourceImpl::DispatchAllMessageEvents",
1489 this, &EventSourceImpl::DispatchAllMessageEvents
);
1490 NS_ENSURE_STATE(event
);
1492 mGoingToDispatchAllMessages
= true;
1494 return Dispatch(event
.forget(), NS_DISPATCH_NORMAL
);
1500 void EventSourceImpl::DispatchAllMessageEvents() {
1501 AssertIsOnTargetThread();
1502 mGoingToDispatchAllMessages
= false;
1504 if (IsClosed() || mFrozen
) {
1511 auto lock
= mSharedData
.Lock();
1512 rv
= lock
->mEventSource
->CheckCurrentGlobalCorrectness();
1513 if (NS_FAILED(rv
)) {
1517 if (NS_WARN_IF(!jsapi
.Init(lock
->mEventSource
->GetOwnerGlobal()))) {
1522 JSContext
* cx
= jsapi
.cx();
1524 while (mMessagesToDispatch
.GetSize() > 0) {
1525 UniquePtr
<Message
> message(mMessagesToDispatch
.PopFront());
1527 if (message
->mLastEventID
.isSome()) {
1528 mLastEventID
.Assign(message
->mLastEventID
.value());
1531 if (message
->mLastEventID
.isNothing() && !mLastEventID
.IsEmpty()) {
1532 message
->mLastEventID
= Some(mLastEventID
);
1536 auto lock
= mSharedData
.Lock();
1537 if (lock
->mServiceNotifier
) {
1538 lock
->mServiceNotifier
->EventReceived(message
->mEventName
, mLastEventID
,
1539 message
->mData
, mReconnectionTime
,
1544 // Now we can turn our string into a jsval
1545 JS::Rooted
<JS::Value
> jsData(cx
);
1548 jsString
= JS_NewUCStringCopyN(cx
, message
->mData
.get(),
1549 message
->mData
.Length());
1550 NS_ENSURE_TRUE_VOID(jsString
);
1552 jsData
.setString(jsString
);
1555 // create an event that uses the MessageEvent interface,
1556 // which does not bubble, is not cancelable, and has no default action
1558 RefPtr
<EventSource
> eventSource
= GetEventSource();
1559 RefPtr
<MessageEvent
> event
=
1560 new MessageEvent(eventSource
, nullptr, nullptr);
1562 event
->InitMessageEvent(nullptr, message
->mEventName
, CanBubble::eNo
,
1563 Cancelable::eNo
, jsData
, mOrigin
, mLastEventID
,
1564 nullptr, Sequence
<OwningNonNull
<MessagePort
>>());
1565 event
->SetTrusted(true);
1567 // We can't hold the mutex while dispatching the event because the mutex is
1568 // not reentrant, and content might call back into our code.
1569 IgnoredErrorResult err
;
1570 eventSource
->DispatchEvent(*event
, err
);
1572 NS_WARNING("Failed to dispatch the message event!!!");
1576 if (IsClosed() || mFrozen
) {
1582 void EventSourceImpl::ClearFields() {
1583 AssertIsOnTargetThread();
1584 mCurrentMessage
= nullptr;
1585 mLastFieldName
.Truncate();
1586 mLastFieldValue
.Truncate();
1589 nsresult
EventSourceImpl::SetFieldAndClear() {
1590 MOZ_ASSERT(!mIsShutDown
);
1591 AssertIsOnTargetThread();
1592 if (mLastFieldName
.IsEmpty()) {
1593 mLastFieldValue
.Truncate();
1596 if (!mCurrentMessage
) {
1597 mCurrentMessage
= MakeUnique
<Message
>();
1599 char16_t first_char
;
1600 first_char
= mLastFieldName
.CharAt(0);
1602 // with no case folding performed
1603 switch (first_char
) {
1605 if (mLastFieldName
.EqualsLiteral("data")) {
1606 // If the field name is "data" append the field value to the data
1607 // buffer, then append a single U+000A LINE FEED (LF) character
1608 // to the data buffer.
1609 mCurrentMessage
->mData
.Append(mLastFieldValue
);
1610 mCurrentMessage
->mData
.Append(LF_CHAR
);
1615 if (mLastFieldName
.EqualsLiteral("event")) {
1616 mCurrentMessage
->mEventName
.Assign(mLastFieldValue
);
1621 if (mLastFieldName
.EqualsLiteral("id")) {
1622 mCurrentMessage
->mLastEventID
= Some(mLastFieldValue
);
1627 if (mLastFieldName
.EqualsLiteral("retry")) {
1628 uint32_t newValue
= 0;
1629 uint32_t i
= 0; // we must ensure that there are only digits
1631 for (i
= 0; i
< mLastFieldValue
.Length(); ++i
) {
1632 if (mLastFieldValue
.CharAt(i
) < (char16_t
)'0' ||
1633 mLastFieldValue
.CharAt(i
) > (char16_t
)'9') {
1637 newValue
= newValue
* 10 + (((uint32_t)mLastFieldValue
.CharAt(i
)) -
1638 ((uint32_t)((char16_t
)'0')));
1642 if (newValue
< MIN_RECONNECTION_TIME_VALUE
) {
1643 mReconnectionTime
= MIN_RECONNECTION_TIME_VALUE
;
1644 } else if (newValue
> MAX_RECONNECTION_TIME_VALUE
) {
1645 mReconnectionTime
= MAX_RECONNECTION_TIME_VALUE
;
1647 mReconnectionTime
= newValue
;
1655 mLastFieldName
.Truncate();
1656 mLastFieldValue
.Truncate();
1661 nsresult
EventSourceImpl::CheckHealthOfRequestCallback(
1662 nsIRequest
* aRequestCallback
) {
1663 // This function could be run on target thread if http channel support
1664 // nsIThreadRetargetableRequest. otherwise, it's run on main thread.
1666 // check if we have been closed or if the request has been canceled
1667 // or if we have been frozen
1668 if (IsClosed() || mFrozen
|| !mHttpChannel
) {
1669 return NS_ERROR_ABORT
;
1672 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aRequestCallback
);
1673 NS_ENSURE_STATE(httpChannel
);
1675 if (httpChannel
!= mHttpChannel
) {
1676 NS_WARNING("wrong channel from request callback");
1677 return NS_ERROR_ABORT
;
1683 nsresult
EventSourceImpl::ParseCharacter(char16_t aChr
) {
1684 AssertIsOnTargetThread();
1688 return NS_ERROR_ABORT
;
1692 case PARSE_STATE_OFF
:
1693 NS_ERROR("Invalid state");
1694 return NS_ERROR_FAILURE
;
1697 case PARSE_STATE_BEGIN_OF_STREAM
:
1698 if (aChr
== CR_CHAR
) {
1699 mStatus
= PARSE_STATE_CR_CHAR
;
1700 } else if (aChr
== LF_CHAR
) {
1701 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1702 } else if (aChr
== COLON_CHAR
) {
1703 mStatus
= PARSE_STATE_COMMENT
;
1705 mLastFieldName
+= aChr
;
1706 mStatus
= PARSE_STATE_FIELD_NAME
;
1710 case PARSE_STATE_CR_CHAR
:
1711 if (aChr
== CR_CHAR
) {
1712 rv
= DispatchCurrentMessageEvent(); // there is an empty line (CRCR)
1713 NS_ENSURE_SUCCESS(rv
, rv
);
1714 } else if (aChr
== LF_CHAR
) {
1715 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1716 } else if (aChr
== COLON_CHAR
) {
1717 mStatus
= PARSE_STATE_COMMENT
;
1719 mLastFieldName
+= aChr
;
1720 mStatus
= PARSE_STATE_FIELD_NAME
;
1725 case PARSE_STATE_COMMENT
:
1726 if (aChr
== CR_CHAR
) {
1727 mStatus
= PARSE_STATE_CR_CHAR
;
1728 } else if (aChr
== LF_CHAR
) {
1729 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1734 case PARSE_STATE_FIELD_NAME
:
1735 if (aChr
== CR_CHAR
) {
1736 rv
= SetFieldAndClear();
1737 NS_ENSURE_SUCCESS(rv
, rv
);
1739 mStatus
= PARSE_STATE_CR_CHAR
;
1740 } else if (aChr
== LF_CHAR
) {
1741 rv
= SetFieldAndClear();
1742 NS_ENSURE_SUCCESS(rv
, rv
);
1744 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1745 } else if (aChr
== COLON_CHAR
) {
1746 mStatus
= PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE
;
1748 mLastFieldName
+= aChr
;
1753 case PARSE_STATE_FIRST_CHAR_OF_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
== SPACE_CHAR
) {
1765 mStatus
= PARSE_STATE_FIELD_VALUE
;
1767 mLastFieldValue
+= aChr
;
1768 mStatus
= PARSE_STATE_FIELD_VALUE
;
1773 case PARSE_STATE_FIELD_VALUE
:
1774 if (aChr
== CR_CHAR
) {
1775 rv
= SetFieldAndClear();
1776 NS_ENSURE_SUCCESS(rv
, rv
);
1778 mStatus
= PARSE_STATE_CR_CHAR
;
1779 } else if (aChr
== LF_CHAR
) {
1780 rv
= SetFieldAndClear();
1781 NS_ENSURE_SUCCESS(rv
, rv
);
1783 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1784 } else if (aChr
!= 0) {
1785 // Avoid appending the null char to the field value.
1786 mLastFieldValue
+= aChr
;
1787 } else if (mLastFieldName
.EqualsLiteral("id")) {
1788 // Ignore the whole id field if aChr is null
1789 mStatus
= PARSE_STATE_IGNORE_FIELD_VALUE
;
1790 mLastFieldValue
.Truncate();
1795 case PARSE_STATE_IGNORE_FIELD_VALUE
:
1796 if (aChr
== CR_CHAR
) {
1797 mStatus
= PARSE_STATE_CR_CHAR
;
1798 } else if (aChr
== LF_CHAR
) {
1799 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1803 case PARSE_STATE_BEGIN_OF_LINE
:
1804 if (aChr
== CR_CHAR
) {
1805 rv
= DispatchCurrentMessageEvent(); // there is an empty line
1806 NS_ENSURE_SUCCESS(rv
, rv
);
1808 mStatus
= PARSE_STATE_CR_CHAR
;
1809 } else if (aChr
== LF_CHAR
) {
1810 rv
= DispatchCurrentMessageEvent(); // there is an empty line
1811 NS_ENSURE_SUCCESS(rv
, rv
);
1813 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1814 } else if (aChr
== COLON_CHAR
) {
1815 mStatus
= PARSE_STATE_COMMENT
;
1816 } else if (aChr
!= 0) {
1817 // Avoid appending the null char to the field name.
1818 mLastFieldName
+= aChr
;
1819 mStatus
= PARSE_STATE_FIELD_NAME
;
1830 class WorkerRunnableDispatcher final
: public WorkerRunnable
{
1831 RefPtr
<EventSourceImpl
> mEventSourceImpl
;
1834 WorkerRunnableDispatcher(RefPtr
<EventSourceImpl
>&& aImpl
,
1835 WorkerPrivate
* aWorkerPrivate
,
1836 already_AddRefed
<nsIRunnable
> aEvent
)
1837 : WorkerRunnable(aWorkerPrivate
, "WorkerRunnableDispatcher"),
1838 mEventSourceImpl(std::move(aImpl
)),
1839 mEvent(std::move(aEvent
)) {}
1841 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
1842 aWorkerPrivate
->AssertIsOnWorkerThread();
1843 return !NS_FAILED(mEvent
->Run());
1846 void PostRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
,
1847 bool aRunResult
) override
{
1848 // Ensure we drop the RefPtr on the worker thread
1849 // and to not keep us alive longer than needed.
1850 mEventSourceImpl
= nullptr;
1853 bool PreDispatch(WorkerPrivate
* aWorkerPrivate
) override
{
1854 // We don't call WorkerRunnable::PreDispatch 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 void PostDispatch(WorkerPrivate
* aWorkerPrivate
,
1862 bool aDispatchResult
) override
{
1863 // We don't call WorkerRunnable::PostDispatch because it would assert the
1864 // wrong thing about which thread we're on. We're on whichever thread the
1865 // channel implementation is running on (probably the main thread or
1866 // transport thread).
1870 nsCOMPtr
<nsIRunnable
> mEvent
;
1875 bool EventSourceImpl::CreateWorkerRef(WorkerPrivate
* aWorkerPrivate
) {
1876 MOZ_ASSERT(!mWorkerRef
);
1877 MOZ_ASSERT(aWorkerPrivate
);
1878 aWorkerPrivate
->AssertIsOnWorkerThread();
1884 RefPtr
<EventSourceImpl
> self
= this;
1885 RefPtr
<StrongWorkerRef
> workerRef
= StrongWorkerRef::Create(
1886 aWorkerPrivate
, "EventSource", [self
]() { self
->Close(); });
1888 if (NS_WARN_IF(!workerRef
)) {
1892 mWorkerRef
= new ThreadSafeWorkerRef(workerRef
);
1896 void EventSourceImpl::ReleaseWorkerRef() {
1897 MOZ_ASSERT(IsClosed());
1898 MOZ_ASSERT(IsCurrentThreadRunningWorker());
1899 mWorkerRef
= nullptr;
1902 //-----------------------------------------------------------------------------
1903 // EventSourceImpl::nsIEventTarget
1904 //-----------------------------------------------------------------------------
1906 EventSourceImpl::DispatchFromScript(nsIRunnable
* aEvent
, uint32_t aFlags
) {
1907 nsCOMPtr
<nsIRunnable
> event(aEvent
);
1908 return Dispatch(event
.forget(), aFlags
);
1912 EventSourceImpl::Dispatch(already_AddRefed
<nsIRunnable
> aEvent
,
1914 nsCOMPtr
<nsIRunnable
> event_ref(aEvent
);
1915 if (mIsMainThread
) {
1916 return NS_DispatchToMainThread(event_ref
.forget());
1920 // We want to avoid clutter about errors in our shutdown logs,
1921 // so just report NS_OK (we have no explicit return value
1926 // If the target is a worker, we have to use a custom WorkerRunnableDispatcher
1928 RefPtr
<WorkerRunnableDispatcher
> event
= new WorkerRunnableDispatcher(
1929 this, mWorkerRef
->Private(), event_ref
.forget());
1931 if (!event
->Dispatch()) {
1932 return NS_ERROR_FAILURE
;
1938 EventSourceImpl::DelayedDispatch(already_AddRefed
<nsIRunnable
> aEvent
,
1939 uint32_t aDelayMs
) {
1940 return NS_ERROR_NOT_IMPLEMENTED
;
1944 EventSourceImpl::RegisterShutdownTask(nsITargetShutdownTask
*) {
1945 return NS_ERROR_NOT_IMPLEMENTED
;
1949 EventSourceImpl::UnregisterShutdownTask(nsITargetShutdownTask
*) {
1950 return NS_ERROR_NOT_IMPLEMENTED
;
1953 //-----------------------------------------------------------------------------
1954 // EventSourceImpl::nsIThreadRetargetableStreamListener
1955 //-----------------------------------------------------------------------------
1957 EventSourceImpl::CheckListenerChain() {
1958 MOZ_ASSERT(NS_IsMainThread(), "Should be on the main thread!");
1963 EventSourceImpl::OnDataFinished(nsresult
) { return NS_OK
; }
1965 ////////////////////////////////////////////////////////////////////////////////
1967 ////////////////////////////////////////////////////////////////////////////////
1969 EventSource::EventSource(nsIGlobalObject
* aGlobal
,
1970 nsICookieJarSettings
* aCookieJarSettings
,
1971 bool aWithCredentials
)
1972 : DOMEventTargetHelper(aGlobal
),
1973 mWithCredentials(aWithCredentials
),
1974 mIsMainThread(NS_IsMainThread()) {
1975 MOZ_ASSERT(aGlobal
);
1976 MOZ_ASSERT(aCookieJarSettings
);
1977 mESImpl
= new EventSourceImpl(this, aCookieJarSettings
);
1980 EventSource::~EventSource() = default;
1982 nsresult
EventSource::CreateAndDispatchSimpleEvent(const nsAString
& aName
) {
1983 RefPtr
<Event
> event
= NS_NewDOMEvent(this, nullptr, nullptr);
1984 // it doesn't bubble, and it isn't cancelable
1985 event
->InitEvent(aName
, false, false);
1986 event
->SetTrusted(true);
1988 DispatchEvent(*event
, rv
);
1989 return rv
.StealNSResult();
1993 already_AddRefed
<EventSource
> EventSource::Constructor(
1994 const GlobalObject
& aGlobal
, const nsAString
& aURL
,
1995 const EventSourceInit
& aEventSourceInitDict
, ErrorResult
& aRv
) {
1996 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(aGlobal
.GetAsSupports());
1997 if (NS_WARN_IF(!global
)) {
1998 aRv
.Throw(NS_ERROR_FAILURE
);
2002 nsCOMPtr
<nsICookieJarSettings
> cookieJarSettings
;
2003 nsCOMPtr
<nsPIDOMWindowInner
> ownerWindow
= do_QueryInterface(global
);
2005 Document
* doc
= ownerWindow
->GetExtantDoc();
2006 if (NS_WARN_IF(!doc
)) {
2007 aRv
.Throw(NS_ERROR_FAILURE
);
2011 cookieJarSettings
= doc
->CookieJarSettings();
2014 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
2015 if (!workerPrivate
) {
2016 aRv
.Throw(NS_ERROR_FAILURE
);
2020 cookieJarSettings
= workerPrivate
->CookieJarSettings();
2023 RefPtr
<EventSource
> eventSource
= new EventSource(
2024 global
, cookieJarSettings
, aEventSourceInitDict
.mWithCredentials
);
2026 if (NS_IsMainThread()) {
2027 // Get principal from document and init EventSourceImpl
2028 nsCOMPtr
<nsIScriptObjectPrincipal
> scriptPrincipal
=
2029 do_QueryInterface(aGlobal
.GetAsSupports());
2030 if (!scriptPrincipal
) {
2031 aRv
.Throw(NS_ERROR_FAILURE
);
2034 nsCOMPtr
<nsIPrincipal
> principal
= scriptPrincipal
->GetPrincipal();
2036 aRv
.Throw(NS_ERROR_FAILURE
);
2039 eventSource
->mESImpl
->Init(principal
, aURL
, aRv
);
2040 if (NS_WARN_IF(aRv
.Failed())) {
2044 eventSource
->mESImpl
->InitChannelAndRequestEventSource(true);
2045 return eventSource
.forget();
2050 // Scope for possible failures that need cleanup
2051 auto guardESImpl
= MakeScopeExit([&] { eventSource
->mESImpl
= nullptr; });
2053 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
2054 MOZ_ASSERT(workerPrivate
);
2056 eventSource
->mESImpl
->mInnerWindowID
= workerPrivate
->WindowID();
2058 RefPtr
<InitRunnable
> initRunnable
=
2059 new InitRunnable(workerPrivate
, eventSource
->mESImpl
, aURL
);
2060 initRunnable
->Dispatch(Canceling
, aRv
);
2061 if (NS_WARN_IF(aRv
.Failed())) {
2065 aRv
= initRunnable
->ErrorCode();
2066 if (NS_WARN_IF(aRv
.Failed())) {
2070 // In workers we have to keep the worker alive using a WorkerRef in order
2071 // to dispatch messages correctly.
2072 // Note, initRunnable->Dispatch may have cleared mESImpl.
2073 if (!eventSource
->mESImpl
||
2074 !eventSource
->mESImpl
->CreateWorkerRef(workerPrivate
)) {
2075 // The worker is already shutting down. Let's return an already closed
2076 // object, but marked as Connecting.
2077 if (eventSource
->mESImpl
) {
2078 // mESImpl is nulled by this call such that EventSourceImpl is
2079 // released before returning the object, otherwise
2080 // it will set EventSource to a CLOSED state in its DTOR..
2081 eventSource
->mESImpl
->Close();
2083 eventSource
->mReadyState
= EventSourceImpl::CONNECTING
;
2085 guardESImpl
.release();
2086 return eventSource
.forget();
2089 // Let's connect to the server.
2090 RefPtr
<ConnectRunnable
> connectRunnable
=
2091 new ConnectRunnable(workerPrivate
, eventSource
->mESImpl
);
2092 connectRunnable
->Dispatch(Canceling
, aRv
);
2093 if (NS_WARN_IF(aRv
.Failed())) {
2097 // End of scope for possible failures
2098 guardESImpl
.release();
2101 return eventSource
.forget();
2105 JSObject
* EventSource::WrapObject(JSContext
* aCx
,
2106 JS::Handle
<JSObject
*> aGivenProto
) {
2107 return EventSource_Binding::Wrap(aCx
, this, aGivenProto
);
2110 void EventSource::Close() {
2111 AssertIsOnTargetThread();
2113 // Close potentially kills ourself, ensure
2114 // to not access any members afterwards.
2119 //-----------------------------------------------------------------------------
2120 // EventSource::nsISupports
2121 //-----------------------------------------------------------------------------
2123 NS_IMPL_CYCLE_COLLECTION_CLASS(EventSource
)
2125 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(EventSource
,
2126 DOMEventTargetHelper
)
2127 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2129 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(EventSource
,
2130 DOMEventTargetHelper
)
2132 // IsCertainlyaliveForCC will return true and cause the cycle
2133 // collector to skip this instance when mESImpl is non-null and
2134 // points back to ourself.
2135 // mESImpl is initialized to be non-null in the constructor
2136 // and should have been wiped out in our close function.
2137 MOZ_ASSERT_UNREACHABLE("Paranoia cleanup that should never happen.");
2140 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2142 bool EventSource::IsCertainlyAliveForCC() const {
2143 // Until we are double linked forth and back, we want to stay alive.
2147 auto lock
= mESImpl
->mSharedData
.Lock();
2148 return lock
->mEventSource
== this;
2151 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EventSource
)
2152 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
2154 NS_IMPL_ADDREF_INHERITED(EventSource
, DOMEventTargetHelper
)
2155 NS_IMPL_RELEASE_INHERITED(EventSource
, DOMEventTargetHelper
)
2157 } // namespace mozilla::dom