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/GlobalFreezeObserver.h"
14 #include "mozilla/dom/EventSource.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/Try.h"
26 #include "mozilla/UniquePtrExtensions.h"
27 #include "nsComponentManagerUtils.h"
28 #include "nsIThreadRetargetableStreamListener.h"
29 #include "nsNetUtil.h"
30 #include "nsIAuthPrompt.h"
31 #include "nsIAuthPrompt2.h"
32 #include "nsIHttpChannel.h"
33 #include "nsIInputStream.h"
34 #include "nsIInterfaceRequestorUtils.h"
35 #include "nsMimeTypes.h"
36 #include "nsIPromptFactory.h"
37 #include "nsIWindowWatcher.h"
38 #include "nsPresContext.h"
39 #include "nsProxyRelease.h"
40 #include "nsContentPolicyUtils.h"
41 #include "nsIStringBundle.h"
42 #include "nsIConsoleService.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 "xpcpublic.h"
50 #include "nsWrapperCacheInlines.h"
51 #include "mozilla/Attributes.h"
52 #include "mozilla/StaticPrefs_dom.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.serverEvents.defaultReconnectionTime
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 nsIChannelEventSink
,
76 public nsIInterfaceRequestor
,
77 public nsISerialEventTarget
,
78 public nsITimerCallback
,
80 public nsIThreadRetargetableStreamListener
,
81 public GlobalTeardownObserver
,
82 public GlobalFreezeObserver
{
84 NS_DECL_THREADSAFE_ISUPPORTS
85 NS_DECL_NSIREQUESTOBSERVER
86 NS_DECL_NSISTREAMLISTENER
87 NS_DECL_NSICHANNELEVENTSINK
88 NS_DECL_NSIINTERFACEREQUESTOR
89 NS_DECL_NSIEVENTTARGET_FULL
90 NS_DECL_NSITIMERCALLBACK
92 NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
94 EventSourceImpl(EventSource
* aEventSource
,
95 nsICookieJarSettings
* aCookieJarSettings
);
97 enum { CONNECTING
= 0U, OPEN
= 1U, CLOSED
= 2U };
101 void Init(nsIGlobalObject
* aWindowGlobal
, nsIPrincipal
* aPrincipal
,
102 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();
120 void DisconnectFromOwner() override
{
122 GlobalTeardownObserver::DisconnectFromOwner();
124 void FrozenCallback(nsIGlobalObject
* aOwner
) override
{
125 DebugOnly
<nsresult
> rv
= Freeze();
126 MOZ_ASSERT(NS_SUCCEEDED(rv
), "Freeze() failed");
128 void ThawedCallback(nsIGlobalObject
* aOwner
) override
{
129 DebugOnly
<nsresult
> rv
= Thaw();
130 MOZ_ASSERT(NS_SUCCEEDED(rv
), "Thaw() failed");
136 nsresult
PrintErrorOnConsole(const char* aBundleURI
, const char* aError
,
137 const nsTArray
<nsString
>& aFormatStrings
);
138 nsresult
ConsoleError();
140 static nsresult
StreamReaderFunc(nsIInputStream
* aInputStream
, void* aClosure
,
141 const char* aFromRawSegment
,
142 uint32_t aToOffset
, uint32_t aCount
,
143 uint32_t* aWriteCount
);
144 void ParseSegment(const char* aBuffer
, uint32_t aLength
);
145 nsresult
SetFieldAndClear();
147 nsresult
ResetEvent();
148 nsresult
DispatchCurrentMessageEvent();
149 nsresult
ParseCharacter(char16_t aChr
);
150 nsresult
CheckHealthOfRequestCallback(nsIRequest
* aRequestCallback
);
151 nsresult
OnRedirectVerifyCallback(nsresult result
);
152 nsresult
ParseURL(const nsAString
& aURL
);
153 nsresult
AddGlobalObservers(nsIGlobalObject
* aGlobal
);
154 void RemoveWindowObservers();
156 void CloseInternal();
157 void CleanupOnMainThread();
159 bool CreateWorkerRef(WorkerPrivate
* aWorkerPrivate
);
160 void ReleaseWorkerRef();
162 void AssertIsOnTargetThread() const {
163 MOZ_DIAGNOSTIC_ASSERT(IsTargetThread());
166 bool IsTargetThread() const { return NS_GetCurrentThread() == mTargetThread
; }
168 uint16_t ReadyState() {
169 auto lock
= mSharedData
.Lock();
170 if (lock
->mEventSource
) {
171 return lock
->mEventSource
->mReadyState
;
173 // EventSourceImpl keeps EventSource alive. If mEventSource is null, it
174 // means that the EventSource has been closed.
178 void SetReadyState(uint16_t aReadyState
) {
179 auto lock
= mSharedData
.Lock();
180 MOZ_ASSERT(lock
->mEventSource
);
181 MOZ_ASSERT(!mIsShutDown
);
182 lock
->mEventSource
->mReadyState
= aReadyState
;
185 bool IsClosed() { return ReadyState() == CLOSED
; }
187 RefPtr
<EventSource
> GetEventSource() {
188 AssertIsOnTargetThread();
189 auto lock
= mSharedData
.Lock();
190 return lock
->mEventSource
;
194 * A simple state machine used to manage the event-source's line buffer
196 * PARSE_STATE_OFF -> PARSE_STATE_BEGIN_OF_STREAM
198 * PARSE_STATE_BEGIN_OF_STREAM -> PARSE_STATE_CR_CHAR |
199 * PARSE_STATE_BEGIN_OF_LINE |
200 * PARSE_STATE_COMMENT |
201 * PARSE_STATE_FIELD_NAME
203 * PARSE_STATE_CR_CHAR -> PARSE_STATE_CR_CHAR |
204 * PARSE_STATE_COMMENT |
205 * PARSE_STATE_FIELD_NAME |
206 * PARSE_STATE_BEGIN_OF_LINE
208 * PARSE_STATE_COMMENT -> PARSE_STATE_CR_CHAR |
209 * PARSE_STATE_BEGIN_OF_LINE
211 * PARSE_STATE_FIELD_NAME -> PARSE_STATE_CR_CHAR |
212 * PARSE_STATE_BEGIN_OF_LINE |
213 * PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE
215 * PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE -> PARSE_STATE_FIELD_VALUE |
216 * PARSE_STATE_CR_CHAR |
217 * PARSE_STATE_BEGIN_OF_LINE
219 * PARSE_STATE_FIELD_VALUE -> PARSE_STATE_CR_CHAR |
220 * PARSE_STATE_BEGIN_OF_LINE
222 * PARSE_STATE_BEGIN_OF_LINE -> PARSE_STATE_CR_CHAR |
223 * PARSE_STATE_COMMENT |
224 * PARSE_STATE_FIELD_NAME |
225 * PARSE_STATE_BEGIN_OF_LINE
227 * Whenever the parser find an empty line or the end-of-file
228 * it dispatches the stacked event.
233 PARSE_STATE_BEGIN_OF_STREAM
,
236 PARSE_STATE_FIELD_NAME
,
237 PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE
,
238 PARSE_STATE_FIELD_VALUE
,
239 PARSE_STATE_IGNORE_FIELD_VALUE
,
240 PARSE_STATE_BEGIN_OF_LINE
243 // Connection related data members. Should only be accessed on main thread.
244 nsCOMPtr
<nsIURI
> mSrc
;
245 uint32_t mReconnectionTime
; // in ms
246 nsCOMPtr
<nsIPrincipal
> mPrincipal
;
248 nsCOMPtr
<nsITimer
> mTimer
;
249 nsCOMPtr
<nsIHttpChannel
> mHttpChannel
;
253 // We need to be able to distinguish between different states of id field:
254 // 1) is not given at all
255 // 2) is given but is empty
256 // 3) is given and has a value
257 // We can't check for the 1st state with a simple nsString.
258 Maybe
<nsString
> mLastEventID
;
262 // Message related data members. May be set / initialized when initializing
263 // EventSourceImpl on target thread but should only be used on target thread.
264 nsString mLastEventID
;
265 UniquePtr
<Message
> mCurrentMessage
;
266 nsDeque
<Message
> mMessagesToDispatch
;
267 ParserStatus mStatus
;
268 mozilla::UniquePtr
<mozilla::Decoder
> mUnicodeDecoder
;
269 nsString mLastFieldName
;
270 nsString mLastFieldValue
;
272 // EventSourceImpl internal states.
273 // WorkerRef to keep the worker alive. (accessed on worker thread only)
274 RefPtr
<ThreadSafeWorkerRef
> mWorkerRef
;
275 // Whether the window is frozen. May be set on main thread and read on target
277 Atomic
<bool> mFrozen
;
278 // There are some messages are going to be dispatched when thaw.
279 bool mGoingToDispatchAllMessages
;
280 // Whether the EventSource is run on main thread.
281 const bool mIsMainThread
;
282 // Whether the EventSourceImpl is going to be destroyed.
283 Atomic
<bool> mIsShutDown
;
285 class EventSourceServiceNotifier final
{
287 EventSourceServiceNotifier(RefPtr
<EventSourceImpl
>&& aEventSourceImpl
,
288 uint64_t aHttpChannelId
, uint64_t aInnerWindowID
)
289 : mEventSourceImpl(std::move(aEventSourceImpl
)),
290 mHttpChannelId(aHttpChannelId
),
291 mInnerWindowID(aInnerWindowID
),
292 mConnectionOpened(false) {
293 AssertIsOnMainThread();
294 mService
= EventSourceEventService::GetOrCreate();
297 void ConnectionOpened() {
298 mEventSourceImpl
->AssertIsOnTargetThread();
299 mService
->EventSourceConnectionOpened(mHttpChannelId
, mInnerWindowID
);
300 mConnectionOpened
= true;
303 void EventReceived(const nsAString
& aEventName
,
304 const nsAString
& aLastEventID
, const nsAString
& aData
,
305 uint32_t aRetry
, DOMHighResTimeStamp aTimeStamp
) {
306 mEventSourceImpl
->AssertIsOnTargetThread();
307 mService
->EventReceived(mHttpChannelId
, mInnerWindowID
, aEventName
,
308 aLastEventID
, aData
, aRetry
, aTimeStamp
);
311 ~EventSourceServiceNotifier() {
312 // It is safe to call this on any thread because
313 // EventSourceConnectionClosed method is thread safe and
314 // NS_ReleaseOnMainThread explicitly releases the service on the main
316 if (mConnectionOpened
) {
317 // We want to notify about connection being closed only if we told
318 // it was ever opened. The check is needed if OnStartRequest is called
319 // on the main thread while close() is called on a worker thread.
320 mService
->EventSourceConnectionClosed(mHttpChannelId
, mInnerWindowID
);
322 NS_ReleaseOnMainThread("EventSourceServiceNotifier::mService",
327 RefPtr
<EventSourceEventService
> mService
;
328 RefPtr
<EventSourceImpl
> mEventSourceImpl
;
329 uint64_t mHttpChannelId
;
330 uint64_t mInnerWindowID
;
331 bool mConnectionOpened
;
335 RefPtr
<EventSource
> mEventSource
;
336 UniquePtr
<EventSourceServiceNotifier
> mServiceNotifier
;
339 DataMutex
<SharedData
> mSharedData
;
341 // Event Source owner information:
342 // - the script file name
343 // - source code line number and column number where the Event Source object
345 // - the ID of the inner window where the script lives. Note that this may not
346 // be the same as the Event Source owner window.
347 // These attributes are used for error reporting. Should only be accessed on
349 JSCallingLocation mCallingLocation
;
350 uint64_t mInnerWindowID
;
353 nsCOMPtr
<nsICookieJarSettings
> mCookieJarSettings
;
355 // Pointer to the target thread for checking whether we are
356 // on the target thread. This is intentionally a non-owning
357 // pointer in order not to affect the thread destruction
358 // sequence. This pointer must only be compared for equality
359 // and must not be dereferenced.
360 nsIThread
* mTargetThread
;
363 EventSourceImpl(const EventSourceImpl
& x
) = delete;
364 EventSourceImpl
& operator=(const EventSourceImpl
& x
) = delete;
369 // If we threw during Init we never called Close
370 SetReadyState(CLOSED
);
375 NS_IMPL_ISUPPORTS(EventSourceImpl
, nsIStreamListener
, nsIRequestObserver
,
376 nsIChannelEventSink
, nsIInterfaceRequestor
,
377 nsISerialEventTarget
, nsIEventTarget
,
378 nsIThreadRetargetableStreamListener
, nsITimerCallback
,
381 EventSourceImpl::EventSourceImpl(EventSource
* aEventSource
,
382 nsICookieJarSettings
* aCookieJarSettings
)
383 : mReconnectionTime(0),
384 mStatus(PARSE_STATE_OFF
),
386 mGoingToDispatchAllMessages(false),
387 mIsMainThread(NS_IsMainThread()),
389 mSharedData(SharedData
{aEventSource
}, "EventSourceImpl::mSharedData"),
391 mCookieJarSettings(aCookieJarSettings
),
392 mTargetThread(NS_GetCurrentThread()) {
393 MOZ_ASSERT(aEventSource
);
394 SetReadyState(CONNECTING
);
397 class CleanupRunnable final
: public WorkerMainThreadRunnable
{
399 explicit CleanupRunnable(RefPtr
<EventSourceImpl
>&& aEventSourceImpl
)
400 : WorkerMainThreadRunnable(GetCurrentThreadWorkerPrivate(),
401 "EventSource :: Cleanup"_ns
),
402 mESImpl(std::move(aEventSourceImpl
)) {
406 bool MainThreadRun() override
{
408 mESImpl
->CleanupOnMainThread();
409 // We want to ensure the shortest possible remaining lifetime
410 // and not depend on the Runnable's destruction.
416 RefPtr
<EventSourceImpl
> mESImpl
;
419 void EventSourceImpl::Close() {
424 SetReadyState(CLOSED
);
425 // CloseInternal potentially kills ourself, ensure
426 // to not access any members afterwards.
430 void EventSourceImpl::CloseInternal() {
431 AssertIsOnTargetThread();
432 MOZ_ASSERT(IsClosed());
434 RefPtr
<EventSource
> myES
;
436 auto lock
= mSharedData
.Lock();
437 // We want to ensure to release ourself even if we have
438 // the shutdown case, thus we put aside a pointer
439 // to the EventSource and null it out right now.
440 myES
= std::move(lock
->mEventSource
);
441 lock
->mEventSource
= nullptr;
442 lock
->mServiceNotifier
= nullptr;
445 MOZ_ASSERT(!mIsShutDown
);
450 // Invoke CleanupOnMainThread before cleaning any members. It will call
451 // ShutDown, which is supposed to be called before cleaning any members.
452 if (NS_IsMainThread()) {
453 CleanupOnMainThread();
456 // run CleanupOnMainThread synchronously on main thread since it touches
457 // observers and members only can be accessed on main thread.
458 RefPtr
<CleanupRunnable
> runnable
= new CleanupRunnable(this);
459 runnable
->Dispatch(GetCurrentThreadWorkerPrivate(), Killing
, rv
);
460 MOZ_ASSERT(!rv
.Failed());
464 while (mMessagesToDispatch
.GetSize() != 0) {
465 delete mMessagesToDispatch
.PopFront();
469 mUnicodeDecoder
= nullptr;
470 // Release the object on its owner. Don't access to any members
472 myES
->mESImpl
= nullptr;
475 void EventSourceImpl::CleanupOnMainThread() {
476 AssertIsOnMainThread();
477 MOZ_ASSERT(IsClosed());
479 // Call ShutDown before cleaning any members.
480 MOZ_ASSERT(!mIsShutDown
);
484 RemoveWindowObservers();
493 mPrincipal
= nullptr;
497 class ConnectRunnable final
: public WorkerMainThreadRunnable
{
499 explicit ConnectRunnable(WorkerPrivate
* aWorkerPrivate
,
500 RefPtr
<EventSourceImpl
> aEventSourceImpl
)
501 : WorkerMainThreadRunnable(aWorkerPrivate
, "EventSource :: Connect"_ns
),
502 mESImpl(std::move(aEventSourceImpl
)) {
503 MOZ_ASSERT(aWorkerPrivate
);
504 aWorkerPrivate
->AssertIsOnWorkerThread();
508 bool MainThreadRun() override
{
510 // We are allowed to access the event target since this runnable is
511 // synchronized with the thread the event target lives on.
512 mESImpl
->InitChannelAndRequestEventSource(true);
513 // We want to ensure the shortest possible remaining lifetime
514 // and not depend on the Runnable's destruction.
520 RefPtr
<EventSourceImpl
> mESImpl
;
523 nsresult
EventSourceImpl::ParseURL(const nsAString
& aURL
) {
524 MOZ_ASSERT(!mIsShutDown
);
526 nsCOMPtr
<nsIURI
> baseURI
;
527 nsresult rv
= GetBaseURI(getter_AddRefs(baseURI
));
528 NS_ENSURE_SUCCESS(rv
, rv
);
530 nsCOMPtr
<nsIURI
> srcURI
;
531 nsCOMPtr
<Document
> doc
=
532 mIsMainThread
? GetEventSource()->GetDocumentIfCurrent() : nullptr;
534 rv
= NS_NewURI(getter_AddRefs(srcURI
), aURL
, doc
->GetDocumentCharacterSet(),
537 rv
= NS_NewURI(getter_AddRefs(srcURI
), aURL
, nullptr, baseURI
);
540 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SYNTAX_ERR
);
543 rv
= nsContentUtils::GetWebExposedOriginSerialization(srcURI
, origin
);
544 NS_ENSURE_SUCCESS(rv
, rv
);
547 rv
= srcURI
->GetSpec(spec
);
548 NS_ENSURE_SUCCESS(rv
, rv
);
550 // This assignment doesn't require extra synchronization because this function
551 // is only ever called from EventSourceImpl::Init(), which is either called
552 // directly if mEventSource was created on the main thread, or via a
553 // synchronous runnable if it was created on a worker thread.
555 // We can't use GetEventSource() here because it would modify the refcount,
556 // and that's not allowed off the owning thread.
557 auto lock
= mSharedData
.Lock();
558 lock
->mEventSource
->mOriginalURL
= NS_ConvertUTF8toUTF16(spec
);
565 nsresult
EventSourceImpl::AddGlobalObservers(nsIGlobalObject
* aGlobal
) {
566 AssertIsOnMainThread();
567 MOZ_ASSERT(mIsMainThread
);
568 MOZ_ASSERT(!mIsShutDown
);
570 GlobalTeardownObserver::BindToOwner(aGlobal
);
571 GlobalFreezeObserver::BindToOwner(aGlobal
);
576 void EventSourceImpl::RemoveWindowObservers() {
577 AssertIsOnMainThread();
578 MOZ_ASSERT(mIsMainThread
);
579 MOZ_ASSERT(IsClosed());
580 GlobalTeardownObserver::DisconnectFromOwner();
581 DisconnectFreezeObserver();
584 void EventSourceImpl::Init(nsIGlobalObject
* aWindowGlobal
,
585 nsIPrincipal
* aPrincipal
, const nsAString
& aURL
,
587 // aWindowGlobal should only exist for main-thread EventSource
588 MOZ_ASSERT_IF(aWindowGlobal
, mIsMainThread
);
589 MOZ_ASSERT(aPrincipal
);
590 MOZ_ASSERT(ReadyState() == CONNECTING
);
591 mPrincipal
= aPrincipal
;
592 aRv
= ParseURL(aURL
);
593 if (NS_WARN_IF(aRv
.Failed())) {
596 // The conditional here is historical and not necessarily sane.
597 if (JSContext
* cx
= nsContentUtils::GetCurrentJSContext()) {
598 mCallingLocation
= JSCallingLocation::Get();
600 mInnerWindowID
= nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(cx
);
605 // We observe when the window freezes and thaws.
607 // This will store raw pointer of us in GTO, which sounds scary as the
608 // object can be accessed cross thread. But it should be safe as GTO and
609 // EventSourceImpl lifetime should be managed in the same main thread.
611 // XXX(krosylight): But what about workers? See bug 1910585.
612 aRv
= AddGlobalObservers(aWindowGlobal
);
613 if (NS_WARN_IF(aRv
.Failed())) {
618 mReconnectionTime
= StaticPrefs::dom_serverEvents_defaultReconnectionTime();
619 if (!mReconnectionTime
) {
620 mReconnectionTime
= DEFAULT_RECONNECTION_TIME_VALUE
;
623 mUnicodeDecoder
= UTF_8_ENCODING
->NewDecoderWithBOMRemoval();
626 //-----------------------------------------------------------------------------
627 // EventSourceImpl::nsIStreamListener
628 //-----------------------------------------------------------------------------
631 EventSourceImpl::OnStartRequest(nsIRequest
* aRequest
) {
632 AssertIsOnMainThread();
634 return NS_ERROR_ABORT
;
636 nsresult rv
= CheckHealthOfRequestCallback(aRequest
);
637 NS_ENSURE_SUCCESS(rv
, rv
);
639 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aRequest
, &rv
);
640 NS_ENSURE_SUCCESS(rv
, rv
);
643 rv
= aRequest
->GetStatus(&status
);
644 NS_ENSURE_SUCCESS(rv
, rv
);
646 if (NS_FAILED(status
)) {
647 // EventSource::OnStopRequest will evaluate if it shall either reestablish
648 // or fail the connection, based on the status.
653 rv
= httpChannel
->GetResponseStatus(&httpStatus
);
654 NS_ENSURE_SUCCESS(rv
, rv
);
656 if (httpStatus
!= 200) {
657 DispatchFailConnection();
658 return NS_ERROR_ABORT
;
661 nsAutoCString contentType
;
662 rv
= httpChannel
->GetContentType(contentType
);
663 NS_ENSURE_SUCCESS(rv
, rv
);
665 if (!contentType
.EqualsLiteral(TEXT_EVENT_STREAM
)) {
666 DispatchFailConnection();
667 return NS_ERROR_ABORT
;
670 if (!mIsMainThread
) {
671 // Try to retarget to worker thread, otherwise fall back to main thread.
672 nsCOMPtr
<nsIThreadRetargetableRequest
> rr
= do_QueryInterface(httpChannel
);
674 rv
= rr
->RetargetDeliveryTo(this);
675 if (NS_WARN_IF(NS_FAILED(rv
))) {
676 NS_WARNING("Retargeting failed");
682 auto lock
= mSharedData
.Lock();
683 lock
->mServiceNotifier
= MakeUnique
<EventSourceServiceNotifier
>(
684 this, mHttpChannel
->ChannelId(), mInnerWindowID
);
686 rv
= Dispatch(NewRunnableMethod("dom::EventSourceImpl::AnnounceConnection",
687 this, &EventSourceImpl::AnnounceConnection
),
689 NS_ENSURE_SUCCESS(rv
, rv
);
690 mStatus
= PARSE_STATE_BEGIN_OF_STREAM
;
694 // this method parses the characters as they become available instead of
696 nsresult
EventSourceImpl::StreamReaderFunc(nsIInputStream
* aInputStream
,
698 const char* aFromRawSegment
,
699 uint32_t aToOffset
, uint32_t aCount
,
700 uint32_t* aWriteCount
) {
701 // The EventSourceImpl instance is hold alive on the
702 // synchronously calling stack, so raw pointer is fine here.
703 EventSourceImpl
* thisObject
= static_cast<EventSourceImpl
*>(aClosure
);
704 if (!thisObject
|| !aWriteCount
) {
706 "EventSource cannot read from stream: no aClosure or aWriteCount");
707 return NS_ERROR_FAILURE
;
709 thisObject
->AssertIsOnTargetThread();
710 MOZ_ASSERT(!thisObject
->mIsShutDown
);
711 thisObject
->ParseSegment((const char*)aFromRawSegment
, aCount
);
712 *aWriteCount
= aCount
;
716 void EventSourceImpl::ParseSegment(const char* aBuffer
, uint32_t aLength
) {
717 AssertIsOnTargetThread();
721 char16_t buffer
[1024];
722 auto dst
= Span(buffer
);
723 auto src
= AsBytes(Span(aBuffer
, aLength
));
724 // XXX EOF handling is https://bugzilla.mozilla.org/show_bug.cgi?id=1369018
729 std::tie(result
, read
, written
, std::ignore
) =
730 mUnicodeDecoder
->DecodeToUTF16(src
, dst
, false);
731 for (auto c
: dst
.To(written
)) {
732 nsresult rv
= ParseCharacter(c
);
733 NS_ENSURE_SUCCESS_VOID(rv
);
735 if (result
== kInputEmpty
) {
738 src
= src
.From(read
);
743 EventSourceImpl::OnDataAvailable(nsIRequest
* aRequest
,
744 nsIInputStream
* aInputStream
, uint64_t aOffset
,
746 AssertIsOnTargetThread();
747 NS_ENSURE_ARG_POINTER(aInputStream
);
749 return NS_ERROR_ABORT
;
752 nsresult rv
= CheckHealthOfRequestCallback(aRequest
);
753 NS_ENSURE_SUCCESS(rv
, rv
);
756 return aInputStream
->ReadSegments(EventSourceImpl::StreamReaderFunc
, this,
761 EventSourceImpl::OnStopRequest(nsIRequest
* aRequest
, nsresult aStatusCode
) {
762 AssertIsOnMainThread();
765 return NS_ERROR_ABORT
;
768 // "Network errors that prevents the connection from being established in the
769 // first place (e.g. DNS errors), must cause the user agent to asynchronously
770 // reestablish the connection.
772 // (...) the cancelation of the fetch algorithm by the user agent (e.g. in
773 // response to window.stop() or the user canceling the network connection
774 // manually) must cause the user agent to fail the connection.
775 // There could be additional network errors that are not covered in the above
778 if (NS_FAILED(aStatusCode
) && aStatusCode
!= NS_ERROR_CONNECTION_REFUSED
&&
779 aStatusCode
!= NS_ERROR_NET_TIMEOUT
&&
780 aStatusCode
!= NS_ERROR_NET_RESET
&&
781 aStatusCode
!= NS_ERROR_NET_INTERRUPT
&&
782 aStatusCode
!= NS_ERROR_NET_PARTIAL_TRANSFER
&&
783 aStatusCode
!= NS_ERROR_NET_TIMEOUT_EXTERNAL
&&
784 aStatusCode
!= NS_ERROR_PROXY_CONNECTION_REFUSED
&&
785 aStatusCode
!= NS_ERROR_DNS_LOOKUP_QUEUE_FULL
&&
786 aStatusCode
!= NS_ERROR_INVALID_CONTENT_ENCODING
) {
787 DispatchFailConnection();
788 return NS_ERROR_ABORT
;
791 nsresult rv
= CheckHealthOfRequestCallback(aRequest
);
792 NS_ENSURE_SUCCESS(rv
, rv
);
795 Dispatch(NewRunnableMethod("dom::EventSourceImpl::ReestablishConnection",
796 this, &EventSourceImpl::ReestablishConnection
),
798 NS_ENSURE_SUCCESS(rv
, rv
);
803 //-----------------------------------------------------------------------------
804 // EventSourceImpl::nsIChannelEventSink
805 //-----------------------------------------------------------------------------
808 EventSourceImpl::AsyncOnChannelRedirect(
809 nsIChannel
* aOldChannel
, nsIChannel
* aNewChannel
, uint32_t aFlags
,
810 nsIAsyncVerifyRedirectCallback
* aCallback
) {
811 AssertIsOnMainThread();
813 return NS_ERROR_ABORT
;
815 nsCOMPtr
<nsIRequest
> aOldRequest
= aOldChannel
;
816 MOZ_ASSERT(aOldRequest
, "Redirect from a null request?");
818 nsresult rv
= CheckHealthOfRequestCallback(aOldRequest
);
819 NS_ENSURE_SUCCESS(rv
, rv
);
821 MOZ_ASSERT(aNewChannel
, "Redirect without a channel?");
823 nsCOMPtr
<nsIURI
> newURI
;
824 rv
= NS_GetFinalChannelURI(aNewChannel
, getter_AddRefs(newURI
));
825 NS_ENSURE_SUCCESS(rv
, rv
);
827 bool isValidScheme
= newURI
->SchemeIs("http") || newURI
->SchemeIs("https");
830 mIsMainThread
? GetEventSource()->CheckCurrentGlobalCorrectness() : NS_OK
;
831 if (NS_FAILED(rv
) || !isValidScheme
) {
832 DispatchFailConnection();
833 return NS_ERROR_DOM_SECURITY_ERR
;
836 // update our channel
838 mHttpChannel
= do_QueryInterface(aNewChannel
);
839 NS_ENSURE_STATE(mHttpChannel
);
842 // The HTTP impl already copies over the referrer info on
843 // redirects, so we don't need to SetupReferrerInfo().
845 if ((aFlags
& nsIChannelEventSink::REDIRECT_PERMANENT
) != 0) {
846 rv
= NS_GetFinalChannelURI(mHttpChannel
, getter_AddRefs(mSrc
));
847 NS_ENSURE_SUCCESS(rv
, rv
);
850 aCallback
->OnRedirectVerifyCallback(NS_OK
);
855 //-----------------------------------------------------------------------------
856 // EventSourceImpl::nsIInterfaceRequestor
857 //-----------------------------------------------------------------------------
860 EventSourceImpl::GetInterface(const nsIID
& aIID
, void** aResult
) {
861 AssertIsOnMainThread();
864 return NS_ERROR_FAILURE
;
867 if (aIID
.Equals(NS_GET_IID(nsIChannelEventSink
))) {
868 *aResult
= static_cast<nsIChannelEventSink
*>(this);
873 if (aIID
.Equals(NS_GET_IID(nsIAuthPrompt
)) ||
874 aIID
.Equals(NS_GET_IID(nsIAuthPrompt2
))) {
876 nsCOMPtr
<nsIPromptFactory
> wwatch
=
877 do_GetService(NS_WINDOWWATCHER_CONTRACTID
, &rv
);
878 NS_ENSURE_SUCCESS(rv
, rv
);
880 nsCOMPtr
<nsPIDOMWindowOuter
> window
;
882 // To avoid a data race we may only access the event target if it lives on
885 auto lock
= mSharedData
.Lock();
886 rv
= lock
->mEventSource
->CheckCurrentGlobalCorrectness();
887 NS_ENSURE_SUCCESS(rv
, NS_ERROR_UNEXPECTED
);
889 if (nsGlobalWindowInner
* win
= lock
->mEventSource
->GetOwnerWindow()) {
890 window
= win
->GetOuterWindow();
894 // Get the an auth prompter for our window so that the parenting
895 // of the dialogs works as it should when using tabs.
897 return wwatch
->GetPrompt(window
, aIID
, aResult
);
900 return QueryInterface(aIID
, aResult
);
904 EventSourceImpl::IsOnCurrentThread(bool* aResult
) {
905 *aResult
= IsTargetThread();
910 EventSourceImpl::IsOnCurrentThreadInfallible() { return IsTargetThread(); }
912 nsresult
EventSourceImpl::GetBaseURI(nsIURI
** aBaseURI
) {
913 MOZ_ASSERT(!mIsShutDown
);
914 NS_ENSURE_ARG_POINTER(aBaseURI
);
918 nsCOMPtr
<nsIURI
> baseURI
;
920 // first we try from document->GetBaseURI()
921 nsCOMPtr
<Document
> doc
=
922 mIsMainThread
? GetEventSource()->GetDocumentIfCurrent() : nullptr;
924 baseURI
= doc
->GetBaseURI();
927 // otherwise we get from the doc's principal
929 auto* basePrin
= BasePrincipal::Cast(mPrincipal
);
930 nsresult rv
= basePrin
->GetURI(getter_AddRefs(baseURI
));
931 NS_ENSURE_SUCCESS(rv
, rv
);
934 NS_ENSURE_STATE(baseURI
);
936 baseURI
.forget(aBaseURI
);
940 void EventSourceImpl::SetupHttpChannel() {
941 AssertIsOnMainThread();
942 MOZ_ASSERT(!mIsShutDown
);
943 nsresult rv
= mHttpChannel
->SetRequestMethod("GET"_ns
);
944 MOZ_ASSERT(NS_SUCCEEDED(rv
));
946 /* set the http request headers */
948 rv
= mHttpChannel
->SetRequestHeader(
949 "Accept"_ns
, nsLiteralCString(TEXT_EVENT_STREAM
), false);
950 MOZ_ASSERT(NS_SUCCEEDED(rv
));
952 // LOAD_BYPASS_CACHE already adds the Cache-Control: no-cache header
954 if (mLastEventID
.IsEmpty()) {
957 NS_ConvertUTF16toUTF8
eventId(mLastEventID
);
958 rv
= mHttpChannel
->SetRequestHeader("Last-Event-ID"_ns
, eventId
, false);
961 MOZ_LOG(gEventSourceLog
, LogLevel::Warning
,
962 ("SetupHttpChannel. rv=%x (%s)", uint32_t(rv
), eventId
.get()));
968 nsresult
EventSourceImpl::SetupReferrerInfo(
969 const nsCOMPtr
<Document
>& aDocument
) {
970 AssertIsOnMainThread();
971 MOZ_ASSERT(!mIsShutDown
);
974 auto referrerInfo
= MakeRefPtr
<ReferrerInfo
>(*aDocument
);
975 nsresult rv
= mHttpChannel
->SetReferrerInfoWithoutClone(referrerInfo
);
976 NS_ENSURE_SUCCESS(rv
, rv
);
982 nsresult
EventSourceImpl::InitChannelAndRequestEventSource(
983 const bool aEventTargetAccessAllowed
) {
984 AssertIsOnMainThread();
986 return NS_ERROR_ABORT
;
989 bool isValidScheme
= mSrc
->SchemeIs("http") || mSrc
->SchemeIs("https");
991 MOZ_ASSERT_IF(mIsMainThread
, aEventTargetAccessAllowed
);
993 nsresult rv
= aEventTargetAccessAllowed
? [this]() {
994 // We can't call GetEventSource() because we're not
995 // allowed to touch the refcount off the worker thread
996 // due to an assertion, event if it would have otherwise
998 auto lock
= mSharedData
.Lock();
999 return lock
->mEventSource
->CheckCurrentGlobalCorrectness();
1002 if (NS_FAILED(rv
) || !isValidScheme
) {
1003 DispatchFailConnection();
1004 return NS_ERROR_DOM_SECURITY_ERR
;
1007 nsCOMPtr
<Document
> doc
;
1008 nsSecurityFlags securityFlags
=
1009 nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT
;
1011 auto lock
= mSharedData
.Lock();
1012 doc
= aEventTargetAccessAllowed
? lock
->mEventSource
->GetDocumentIfCurrent()
1015 if (lock
->mEventSource
->mWithCredentials
) {
1016 securityFlags
|= nsILoadInfo::SEC_COOKIES_INCLUDE
;
1020 // The html spec requires we use fetch cache mode of "no-store". This
1021 // maps to LOAD_BYPASS_CACHE and LOAD_INHIBIT_CACHING in necko.
1022 nsLoadFlags loadFlags
;
1023 loadFlags
= nsIRequest::LOAD_BACKGROUND
| nsIRequest::LOAD_BYPASS_CACHE
|
1024 nsIRequest::INHIBIT_CACHING
;
1026 nsCOMPtr
<nsIChannel
> channel
;
1027 // If we have the document, use it
1029 MOZ_ASSERT(mCookieJarSettings
== doc
->CookieJarSettings());
1031 nsCOMPtr
<nsILoadGroup
> loadGroup
= doc
->GetDocumentLoadGroup();
1032 rv
= NS_NewChannel(getter_AddRefs(channel
), mSrc
, doc
, securityFlags
,
1033 nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE
,
1034 nullptr, // aPerformanceStorage
1036 nullptr, // aCallbacks
1037 loadFlags
); // aLoadFlags
1039 // otherwise use the principal
1040 rv
= NS_NewChannel(getter_AddRefs(channel
), mSrc
, mPrincipal
, securityFlags
,
1041 nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE
,
1043 nullptr, // aPerformanceStorage
1044 nullptr, // loadGroup
1045 nullptr, // aCallbacks
1046 loadFlags
); // aLoadFlags
1049 NS_ENSURE_SUCCESS(rv
, rv
);
1051 mHttpChannel
= do_QueryInterface(channel
);
1052 NS_ENSURE_TRUE(mHttpChannel
, NS_ERROR_NO_INTERFACE
);
1055 rv
= SetupReferrerInfo(doc
);
1056 NS_ENSURE_SUCCESS(rv
, rv
);
1060 nsCOMPtr
<nsIInterfaceRequestor
> notificationCallbacks
;
1061 mHttpChannel
->GetNotificationCallbacks(
1062 getter_AddRefs(notificationCallbacks
));
1063 MOZ_ASSERT(!notificationCallbacks
);
1067 mHttpChannel
->SetNotificationCallbacks(this);
1069 // Start reading from the channel
1070 rv
= mHttpChannel
->AsyncOpen(this);
1071 if (NS_FAILED(rv
)) {
1072 DispatchFailConnection();
1079 void EventSourceImpl::AnnounceConnection() {
1080 AssertIsOnTargetThread();
1081 if (ReadyState() != CONNECTING
) {
1082 NS_WARNING("Unexpected mReadyState!!!");
1087 auto lock
= mSharedData
.Lock();
1088 if (lock
->mServiceNotifier
) {
1089 lock
->mServiceNotifier
->ConnectionOpened();
1093 // When a user agent is to announce the connection, the user agent must set
1094 // the readyState attribute to OPEN and queue a task to fire a simple event
1095 // named open at the EventSource object.
1097 SetReadyState(OPEN
);
1099 nsresult rv
= GetEventSource()->CheckCurrentGlobalCorrectness();
1100 if (NS_FAILED(rv
)) {
1103 // We can't hold the mutex while dispatching the event because the mutex is
1104 // not reentrant, and content might call back into our code.
1105 rv
= GetEventSource()->CreateAndDispatchSimpleEvent(u
"open"_ns
);
1106 if (NS_FAILED(rv
)) {
1107 NS_WARNING("Failed to dispatch the error event!!!");
1112 nsresult
EventSourceImpl::ResetConnection() {
1113 AssertIsOnMainThread();
1115 mHttpChannel
->Cancel(NS_ERROR_ABORT
);
1116 mHttpChannel
= nullptr;
1121 void EventSourceImpl::ResetDecoder() {
1122 AssertIsOnTargetThread();
1123 if (mUnicodeDecoder
) {
1124 UTF_8_ENCODING
->NewDecoderWithBOMRemovalInto(*mUnicodeDecoder
);
1126 mStatus
= PARSE_STATE_OFF
;
1130 class CallRestartConnection final
: public WorkerMainThreadRunnable
{
1132 explicit CallRestartConnection(RefPtr
<EventSourceImpl
>&& aEventSourceImpl
)
1133 : WorkerMainThreadRunnable(aEventSourceImpl
->mWorkerRef
->Private(),
1134 "EventSource :: RestartConnection"_ns
),
1135 mESImpl(std::move(aEventSourceImpl
)) {
1136 MOZ_ASSERT(mESImpl
);
1139 bool MainThreadRun() override
{
1140 MOZ_ASSERT(mESImpl
);
1141 mESImpl
->RestartConnection();
1142 // We want to ensure the shortest possible remaining lifetime
1143 // and not depend on the Runnable's destruction.
1149 RefPtr
<EventSourceImpl
> mESImpl
;
1152 nsresult
EventSourceImpl::RestartConnection() {
1153 AssertIsOnMainThread();
1155 return NS_ERROR_ABORT
;
1158 nsresult rv
= ResetConnection();
1159 NS_ENSURE_SUCCESS(rv
, rv
);
1160 rv
= SetReconnectionTimeout();
1161 NS_ENSURE_SUCCESS(rv
, rv
);
1165 void EventSourceImpl::ReestablishConnection() {
1166 AssertIsOnTargetThread();
1172 if (mIsMainThread
) {
1173 rv
= RestartConnection();
1175 RefPtr
<CallRestartConnection
> runnable
= new CallRestartConnection(this);
1177 runnable
->Dispatch(GetCurrentThreadWorkerPrivate(), Canceling
, result
);
1178 MOZ_ASSERT(!result
.Failed());
1179 rv
= result
.StealNSResult();
1181 if (NS_FAILED(rv
)) {
1185 rv
= GetEventSource()->CheckCurrentGlobalCorrectness();
1186 if (NS_FAILED(rv
)) {
1190 SetReadyState(CONNECTING
);
1192 // We can't hold the mutex while dispatching the event because the mutex is
1193 // not reentrant, and content might call back into our code.
1194 rv
= GetEventSource()->CreateAndDispatchSimpleEvent(u
"error"_ns
);
1195 if (NS_FAILED(rv
)) {
1196 NS_WARNING("Failed to dispatch the error event!!!");
1201 nsresult
EventSourceImpl::SetReconnectionTimeout() {
1202 AssertIsOnMainThread();
1204 return NS_ERROR_ABORT
;
1207 // the timer will be used whenever the requests are going finished.
1209 mTimer
= NS_NewTimer();
1210 NS_ENSURE_STATE(mTimer
);
1213 MOZ_TRY(mTimer
->InitWithCallback(this, mReconnectionTime
,
1214 nsITimer::TYPE_ONE_SHOT
));
1219 nsresult
EventSourceImpl::PrintErrorOnConsole(
1220 const char* aBundleURI
, const char* aError
,
1221 const nsTArray
<nsString
>& aFormatStrings
) {
1222 AssertIsOnMainThread();
1223 MOZ_ASSERT(!mIsShutDown
);
1224 nsCOMPtr
<nsIStringBundleService
> bundleService
=
1225 mozilla::components::StringBundle::Service();
1226 NS_ENSURE_STATE(bundleService
);
1228 nsCOMPtr
<nsIStringBundle
> strBundle
;
1230 bundleService
->CreateBundle(aBundleURI
, getter_AddRefs(strBundle
));
1231 NS_ENSURE_SUCCESS(rv
, rv
);
1233 nsCOMPtr
<nsIConsoleService
> console(
1234 do_GetService(NS_CONSOLESERVICE_CONTRACTID
, &rv
));
1235 NS_ENSURE_SUCCESS(rv
, rv
);
1237 nsCOMPtr
<nsIScriptError
> errObj(
1238 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID
, &rv
));
1239 NS_ENSURE_SUCCESS(rv
, rv
);
1241 // Localize the error message
1242 nsAutoString message
;
1243 if (!aFormatStrings
.IsEmpty()) {
1244 rv
= strBundle
->FormatStringFromName(aError
, aFormatStrings
, message
);
1246 rv
= strBundle
->GetStringFromName(aError
, message
);
1248 NS_ENSURE_SUCCESS(rv
, rv
);
1250 rv
= errObj
->InitWithWindowID(
1251 message
, mCallingLocation
.FileName(), mCallingLocation
.mLine
,
1252 mCallingLocation
.mColumn
, nsIScriptError::errorFlag
, "Event Source",
1254 NS_ENSURE_SUCCESS(rv
, rv
);
1256 // print the error message directly to the JS console
1257 rv
= console
->LogMessage(errObj
);
1258 NS_ENSURE_SUCCESS(rv
, rv
);
1263 nsresult
EventSourceImpl::ConsoleError() {
1264 AssertIsOnMainThread();
1265 MOZ_ASSERT(!mIsShutDown
);
1266 nsAutoCString targetSpec
;
1267 nsresult rv
= mSrc
->GetSpec(targetSpec
);
1268 NS_ENSURE_SUCCESS(rv
, rv
);
1270 AutoTArray
<nsString
, 1> formatStrings
;
1271 CopyUTF8toUTF16(targetSpec
, *formatStrings
.AppendElement());
1273 if (ReadyState() == CONNECTING
) {
1274 rv
= PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
1275 "connectionFailure", formatStrings
);
1277 rv
= PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
1278 "netInterrupt", formatStrings
);
1280 NS_ENSURE_SUCCESS(rv
, rv
);
1285 void EventSourceImpl::DispatchFailConnection() {
1286 AssertIsOnMainThread();
1290 nsresult rv
= ConsoleError();
1291 if (NS_FAILED(rv
)) {
1292 NS_WARNING("Failed to print to the console error");
1294 rv
= Dispatch(NewRunnableMethod("dom::EventSourceImpl::FailConnection", this,
1295 &EventSourceImpl::FailConnection
),
1296 NS_DISPATCH_NORMAL
);
1297 if (NS_WARN_IF(NS_FAILED(rv
))) {
1298 // if the worker is shutting down, the dispatching of normal WorkerRunnables
1304 void EventSourceImpl::FailConnection() {
1305 AssertIsOnTargetThread();
1309 // Must change state to closed before firing event to content.
1310 SetReadyState(CLOSED
);
1311 // When a user agent is to fail the connection, the user agent must set the
1312 // readyState attribute to CLOSED and queue a task to fire a simple event
1313 // named error at the EventSource object.
1314 nsresult rv
= GetEventSource()->CheckCurrentGlobalCorrectness();
1315 if (NS_SUCCEEDED(rv
)) {
1316 // We can't hold the mutex while dispatching the event because the mutex
1317 // is not reentrant, and content might call back into our code.
1318 rv
= GetEventSource()->CreateAndDispatchSimpleEvent(u
"error"_ns
);
1319 if (NS_FAILED(rv
)) {
1320 NS_WARNING("Failed to dispatch the error event!!!");
1323 // Call CloseInternal in the end of function because it may release
1328 NS_IMETHODIMP
EventSourceImpl::Notify(nsITimer
* aTimer
) {
1329 AssertIsOnMainThread();
1334 MOZ_ASSERT(!mHttpChannel
, "the channel hasn't been cancelled!!");
1337 nsresult rv
= InitChannelAndRequestEventSource(mIsMainThread
);
1338 if (NS_FAILED(rv
)) {
1339 NS_WARNING("InitChannelAndRequestEventSource() failed");
1345 NS_IMETHODIMP
EventSourceImpl::GetName(nsACString
& aName
) {
1346 aName
.AssignLiteral("EventSourceImpl");
1350 nsresult
EventSourceImpl::Thaw() {
1351 AssertIsOnMainThread();
1352 if (IsClosed() || !mFrozen
) {
1356 MOZ_ASSERT(!mHttpChannel
, "the connection hasn't been closed!!!");
1360 if (!mGoingToDispatchAllMessages
&& mMessagesToDispatch
.GetSize() > 0) {
1361 nsCOMPtr
<nsIRunnable
> event
=
1362 NewRunnableMethod("dom::EventSourceImpl::DispatchAllMessageEvents",
1363 this, &EventSourceImpl::DispatchAllMessageEvents
);
1364 NS_ENSURE_STATE(event
);
1366 mGoingToDispatchAllMessages
= true;
1368 rv
= Dispatch(event
.forget(), NS_DISPATCH_NORMAL
);
1369 NS_ENSURE_SUCCESS(rv
, rv
);
1372 rv
= InitChannelAndRequestEventSource(mIsMainThread
);
1373 NS_ENSURE_SUCCESS(rv
, rv
);
1378 nsresult
EventSourceImpl::Freeze() {
1379 AssertIsOnMainThread();
1380 if (IsClosed() || mFrozen
) {
1384 MOZ_ASSERT(!mHttpChannel
, "the connection hasn't been closed!!!");
1389 nsresult
EventSourceImpl::DispatchCurrentMessageEvent() {
1390 AssertIsOnTargetThread();
1391 MOZ_ASSERT(!mIsShutDown
);
1392 UniquePtr
<Message
> message(std::move(mCurrentMessage
));
1395 if (!message
|| message
->mData
.IsEmpty()) {
1399 // removes the trailing LF from mData
1400 MOZ_ASSERT(message
->mData
.CharAt(message
->mData
.Length() - 1) == LF_CHAR
,
1401 "Invalid trailing character! LF was expected instead.");
1402 message
->mData
.SetLength(message
->mData
.Length() - 1);
1404 if (message
->mEventName
.IsEmpty()) {
1405 message
->mEventName
.AssignLiteral("message");
1408 mMessagesToDispatch
.Push(message
.release());
1410 if (!mGoingToDispatchAllMessages
) {
1411 nsCOMPtr
<nsIRunnable
> event
=
1412 NewRunnableMethod("dom::EventSourceImpl::DispatchAllMessageEvents",
1413 this, &EventSourceImpl::DispatchAllMessageEvents
);
1414 NS_ENSURE_STATE(event
);
1416 mGoingToDispatchAllMessages
= true;
1418 return Dispatch(event
.forget(), NS_DISPATCH_NORMAL
);
1424 void EventSourceImpl::DispatchAllMessageEvents() {
1425 AssertIsOnTargetThread();
1426 mGoingToDispatchAllMessages
= false;
1428 if (IsClosed() || mFrozen
) {
1435 auto lock
= mSharedData
.Lock();
1436 rv
= lock
->mEventSource
->CheckCurrentGlobalCorrectness();
1437 if (NS_FAILED(rv
)) {
1441 if (NS_WARN_IF(!jsapi
.Init(lock
->mEventSource
->GetOwnerGlobal()))) {
1446 JSContext
* cx
= jsapi
.cx();
1448 while (mMessagesToDispatch
.GetSize() > 0) {
1449 UniquePtr
<Message
> message(mMessagesToDispatch
.PopFront());
1451 if (message
->mLastEventID
.isSome()) {
1452 mLastEventID
.Assign(message
->mLastEventID
.value());
1455 if (message
->mLastEventID
.isNothing() && !mLastEventID
.IsEmpty()) {
1456 message
->mLastEventID
= Some(mLastEventID
);
1460 auto lock
= mSharedData
.Lock();
1461 if (lock
->mServiceNotifier
) {
1462 lock
->mServiceNotifier
->EventReceived(message
->mEventName
, mLastEventID
,
1463 message
->mData
, mReconnectionTime
,
1468 // Now we can turn our string into a jsval
1469 JS::Rooted
<JS::Value
> jsData(cx
);
1472 jsString
= JS_NewUCStringCopyN(cx
, message
->mData
.get(),
1473 message
->mData
.Length());
1474 NS_ENSURE_TRUE_VOID(jsString
);
1476 jsData
.setString(jsString
);
1479 // create an event that uses the MessageEvent interface,
1480 // which does not bubble, is not cancelable, and has no default action
1482 RefPtr
<EventSource
> eventSource
= GetEventSource();
1483 RefPtr
<MessageEvent
> event
=
1484 new MessageEvent(eventSource
, nullptr, nullptr);
1486 event
->InitMessageEvent(nullptr, message
->mEventName
, CanBubble::eNo
,
1487 Cancelable::eNo
, jsData
, mOrigin
, mLastEventID
,
1488 nullptr, Sequence
<OwningNonNull
<MessagePort
>>());
1489 event
->SetTrusted(true);
1491 // We can't hold the mutex while dispatching the event because the mutex is
1492 // not reentrant, and content might call back into our code.
1493 IgnoredErrorResult err
;
1494 eventSource
->DispatchEvent(*event
, err
);
1496 NS_WARNING("Failed to dispatch the message event!!!");
1500 if (IsClosed() || mFrozen
) {
1506 void EventSourceImpl::ClearFields() {
1507 AssertIsOnTargetThread();
1508 mCurrentMessage
= nullptr;
1509 mLastFieldName
.Truncate();
1510 mLastFieldValue
.Truncate();
1513 nsresult
EventSourceImpl::SetFieldAndClear() {
1514 MOZ_ASSERT(!mIsShutDown
);
1515 AssertIsOnTargetThread();
1516 if (mLastFieldName
.IsEmpty()) {
1517 mLastFieldValue
.Truncate();
1520 if (!mCurrentMessage
) {
1521 mCurrentMessage
= MakeUnique
<Message
>();
1523 char16_t first_char
;
1524 first_char
= mLastFieldName
.CharAt(0);
1526 // with no case folding performed
1527 switch (first_char
) {
1529 if (mLastFieldName
.EqualsLiteral("data")) {
1530 // If the field name is "data" append the field value to the data
1531 // buffer, then append a single U+000A LINE FEED (LF) character
1532 // to the data buffer.
1533 mCurrentMessage
->mData
.Append(mLastFieldValue
);
1534 mCurrentMessage
->mData
.Append(LF_CHAR
);
1539 if (mLastFieldName
.EqualsLiteral("event")) {
1540 mCurrentMessage
->mEventName
.Assign(mLastFieldValue
);
1545 if (mLastFieldName
.EqualsLiteral("id")) {
1546 mCurrentMessage
->mLastEventID
= Some(mLastFieldValue
);
1551 if (mLastFieldName
.EqualsLiteral("retry")) {
1552 uint32_t newValue
= 0;
1553 uint32_t i
= 0; // we must ensure that there are only digits
1555 for (i
= 0; i
< mLastFieldValue
.Length(); ++i
) {
1556 if (mLastFieldValue
.CharAt(i
) < (char16_t
)'0' ||
1557 mLastFieldValue
.CharAt(i
) > (char16_t
)'9') {
1561 newValue
= newValue
* 10 + (((uint32_t)mLastFieldValue
.CharAt(i
)) -
1562 ((uint32_t)((char16_t
)'0')));
1566 if (newValue
< MIN_RECONNECTION_TIME_VALUE
) {
1567 mReconnectionTime
= MIN_RECONNECTION_TIME_VALUE
;
1568 } else if (newValue
> MAX_RECONNECTION_TIME_VALUE
) {
1569 mReconnectionTime
= MAX_RECONNECTION_TIME_VALUE
;
1571 mReconnectionTime
= newValue
;
1579 mLastFieldName
.Truncate();
1580 mLastFieldValue
.Truncate();
1585 nsresult
EventSourceImpl::CheckHealthOfRequestCallback(
1586 nsIRequest
* aRequestCallback
) {
1587 // This function could be run on target thread if http channel support
1588 // nsIThreadRetargetableRequest. otherwise, it's run on main thread.
1590 // check if we have been closed or if the request has been canceled
1591 // or if we have been frozen
1592 if (IsClosed() || mFrozen
|| !mHttpChannel
) {
1593 return NS_ERROR_ABORT
;
1596 nsCOMPtr
<nsIHttpChannel
> httpChannel
= do_QueryInterface(aRequestCallback
);
1597 NS_ENSURE_STATE(httpChannel
);
1599 if (httpChannel
!= mHttpChannel
) {
1600 NS_WARNING("wrong channel from request callback");
1601 return NS_ERROR_ABORT
;
1607 nsresult
EventSourceImpl::ParseCharacter(char16_t aChr
) {
1608 AssertIsOnTargetThread();
1612 return NS_ERROR_ABORT
;
1616 case PARSE_STATE_OFF
:
1617 NS_ERROR("Invalid state");
1618 return NS_ERROR_FAILURE
;
1621 case PARSE_STATE_BEGIN_OF_STREAM
:
1622 if (aChr
== CR_CHAR
) {
1623 mStatus
= PARSE_STATE_CR_CHAR
;
1624 } else if (aChr
== LF_CHAR
) {
1625 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1626 } else if (aChr
== COLON_CHAR
) {
1627 mStatus
= PARSE_STATE_COMMENT
;
1629 mLastFieldName
+= aChr
;
1630 mStatus
= PARSE_STATE_FIELD_NAME
;
1634 case PARSE_STATE_CR_CHAR
:
1635 if (aChr
== CR_CHAR
) {
1636 rv
= DispatchCurrentMessageEvent(); // there is an empty line (CRCR)
1637 NS_ENSURE_SUCCESS(rv
, rv
);
1638 } else if (aChr
== LF_CHAR
) {
1639 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1640 } else if (aChr
== COLON_CHAR
) {
1641 mStatus
= PARSE_STATE_COMMENT
;
1643 mLastFieldName
+= aChr
;
1644 mStatus
= PARSE_STATE_FIELD_NAME
;
1649 case PARSE_STATE_COMMENT
:
1650 if (aChr
== CR_CHAR
) {
1651 mStatus
= PARSE_STATE_CR_CHAR
;
1652 } else if (aChr
== LF_CHAR
) {
1653 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1658 case PARSE_STATE_FIELD_NAME
:
1659 if (aChr
== CR_CHAR
) {
1660 rv
= SetFieldAndClear();
1661 NS_ENSURE_SUCCESS(rv
, rv
);
1663 mStatus
= PARSE_STATE_CR_CHAR
;
1664 } else if (aChr
== LF_CHAR
) {
1665 rv
= SetFieldAndClear();
1666 NS_ENSURE_SUCCESS(rv
, rv
);
1668 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1669 } else if (aChr
== COLON_CHAR
) {
1670 mStatus
= PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE
;
1672 mLastFieldName
+= aChr
;
1677 case PARSE_STATE_FIRST_CHAR_OF_FIELD_VALUE
:
1678 if (aChr
== CR_CHAR
) {
1679 rv
= SetFieldAndClear();
1680 NS_ENSURE_SUCCESS(rv
, rv
);
1682 mStatus
= PARSE_STATE_CR_CHAR
;
1683 } else if (aChr
== LF_CHAR
) {
1684 rv
= SetFieldAndClear();
1685 NS_ENSURE_SUCCESS(rv
, rv
);
1687 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1688 } else if (aChr
== SPACE_CHAR
) {
1689 mStatus
= PARSE_STATE_FIELD_VALUE
;
1691 mLastFieldValue
+= aChr
;
1692 mStatus
= PARSE_STATE_FIELD_VALUE
;
1697 case PARSE_STATE_FIELD_VALUE
:
1698 if (aChr
== CR_CHAR
) {
1699 rv
= SetFieldAndClear();
1700 NS_ENSURE_SUCCESS(rv
, rv
);
1702 mStatus
= PARSE_STATE_CR_CHAR
;
1703 } else if (aChr
== LF_CHAR
) {
1704 rv
= SetFieldAndClear();
1705 NS_ENSURE_SUCCESS(rv
, rv
);
1707 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1708 } else if (aChr
!= 0) {
1709 // Avoid appending the null char to the field value.
1710 mLastFieldValue
+= aChr
;
1711 } else if (mLastFieldName
.EqualsLiteral("id")) {
1712 // Ignore the whole id field if aChr is null
1713 mStatus
= PARSE_STATE_IGNORE_FIELD_VALUE
;
1714 mLastFieldValue
.Truncate();
1719 case PARSE_STATE_IGNORE_FIELD_VALUE
:
1720 if (aChr
== CR_CHAR
) {
1721 mStatus
= PARSE_STATE_CR_CHAR
;
1722 } else if (aChr
== LF_CHAR
) {
1723 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1727 case PARSE_STATE_BEGIN_OF_LINE
:
1728 if (aChr
== CR_CHAR
) {
1729 rv
= DispatchCurrentMessageEvent(); // there is an empty line
1730 NS_ENSURE_SUCCESS(rv
, rv
);
1732 mStatus
= PARSE_STATE_CR_CHAR
;
1733 } else if (aChr
== LF_CHAR
) {
1734 rv
= DispatchCurrentMessageEvent(); // there is an empty line
1735 NS_ENSURE_SUCCESS(rv
, rv
);
1737 mStatus
= PARSE_STATE_BEGIN_OF_LINE
;
1738 } else if (aChr
== COLON_CHAR
) {
1739 mStatus
= PARSE_STATE_COMMENT
;
1740 } else if (aChr
!= 0) {
1741 // Avoid appending the null char to the field name.
1742 mLastFieldName
+= aChr
;
1743 mStatus
= PARSE_STATE_FIELD_NAME
;
1754 class WorkerRunnableDispatcher final
: public WorkerThreadRunnable
{
1755 RefPtr
<EventSourceImpl
> mEventSourceImpl
;
1758 WorkerRunnableDispatcher(RefPtr
<EventSourceImpl
>&& aImpl
,
1759 WorkerPrivate
* aWorkerPrivate
,
1760 already_AddRefed
<nsIRunnable
> aEvent
)
1761 : WorkerThreadRunnable("WorkerRunnableDispatcher"),
1762 mEventSourceImpl(std::move(aImpl
)),
1763 mEvent(std::move(aEvent
)) {}
1765 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
1766 aWorkerPrivate
->AssertIsOnWorkerThread();
1767 return !NS_FAILED(mEvent
->Run());
1770 void PostRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
,
1771 bool aRunResult
) override
{
1772 // Ensure we drop the RefPtr on the worker thread
1773 // and to not keep us alive longer than needed.
1774 mEventSourceImpl
= nullptr;
1777 bool PreDispatch(WorkerPrivate
* aWorkerPrivate
) override
{
1778 // We don't call WorkerRunnable::PreDispatch because it would assert the
1779 // wrong thing about which thread we're on. We're on whichever thread the
1780 // channel implementation is running on (probably the main thread or
1781 // transport thread).
1785 void PostDispatch(WorkerPrivate
* aWorkerPrivate
,
1786 bool aDispatchResult
) override
{
1787 // We don't call WorkerRunnable::PostDispatch because it would assert the
1788 // wrong thing about which thread we're on. We're on whichever thread the
1789 // channel implementation is running on (probably the main thread or
1790 // transport thread).
1794 nsCOMPtr
<nsIRunnable
> mEvent
;
1799 bool EventSourceImpl::CreateWorkerRef(WorkerPrivate
* aWorkerPrivate
) {
1800 MOZ_ASSERT(!mWorkerRef
);
1801 MOZ_ASSERT(aWorkerPrivate
);
1802 aWorkerPrivate
->AssertIsOnWorkerThread();
1808 RefPtr
<EventSourceImpl
> self
= this;
1809 RefPtr
<StrongWorkerRef
> workerRef
= StrongWorkerRef::Create(
1810 aWorkerPrivate
, "EventSource", [self
]() { self
->Close(); });
1812 if (NS_WARN_IF(!workerRef
)) {
1816 mWorkerRef
= new ThreadSafeWorkerRef(workerRef
);
1820 void EventSourceImpl::ReleaseWorkerRef() {
1821 MOZ_ASSERT(IsClosed());
1822 MOZ_ASSERT(IsCurrentThreadRunningWorker());
1823 mWorkerRef
= nullptr;
1826 //-----------------------------------------------------------------------------
1827 // EventSourceImpl::nsIEventTarget
1828 //-----------------------------------------------------------------------------
1830 EventSourceImpl::DispatchFromScript(nsIRunnable
* aEvent
, uint32_t aFlags
) {
1831 nsCOMPtr
<nsIRunnable
> event(aEvent
);
1832 return Dispatch(event
.forget(), aFlags
);
1836 EventSourceImpl::Dispatch(already_AddRefed
<nsIRunnable
> aEvent
,
1838 nsCOMPtr
<nsIRunnable
> event_ref(aEvent
);
1839 if (mIsMainThread
) {
1840 return NS_DispatchToMainThread(event_ref
.forget());
1844 // We want to avoid clutter about errors in our shutdown logs,
1845 // so just report NS_OK (we have no explicit return value
1850 // If the target is a worker, we have to use a custom WorkerRunnableDispatcher
1852 RefPtr
<WorkerRunnableDispatcher
> event
= new WorkerRunnableDispatcher(
1853 this, mWorkerRef
->Private(), event_ref
.forget());
1855 if (!event
->Dispatch(mWorkerRef
->Private())) {
1856 return NS_ERROR_FAILURE
;
1862 EventSourceImpl::DelayedDispatch(already_AddRefed
<nsIRunnable
> aEvent
,
1863 uint32_t aDelayMs
) {
1864 return NS_ERROR_NOT_IMPLEMENTED
;
1868 EventSourceImpl::RegisterShutdownTask(nsITargetShutdownTask
*) {
1869 return NS_ERROR_NOT_IMPLEMENTED
;
1873 EventSourceImpl::UnregisterShutdownTask(nsITargetShutdownTask
*) {
1874 return NS_ERROR_NOT_IMPLEMENTED
;
1877 //-----------------------------------------------------------------------------
1878 // EventSourceImpl::nsIThreadRetargetableStreamListener
1879 //-----------------------------------------------------------------------------
1881 EventSourceImpl::CheckListenerChain() {
1882 MOZ_ASSERT(NS_IsMainThread(), "Should be on the main thread!");
1887 EventSourceImpl::OnDataFinished(nsresult
) { return NS_OK
; }
1889 ////////////////////////////////////////////////////////////////////////////////
1891 ////////////////////////////////////////////////////////////////////////////////
1893 EventSource::EventSource(nsIGlobalObject
* aGlobal
,
1894 nsICookieJarSettings
* aCookieJarSettings
,
1895 bool aWithCredentials
)
1896 : DOMEventTargetHelper(aGlobal
),
1897 mWithCredentials(aWithCredentials
),
1898 mIsMainThread(NS_IsMainThread()) {
1899 MOZ_ASSERT(aGlobal
);
1900 MOZ_ASSERT(aCookieJarSettings
);
1901 mESImpl
= new EventSourceImpl(this, aCookieJarSettings
);
1904 EventSource::~EventSource() = default;
1906 nsresult
EventSource::CreateAndDispatchSimpleEvent(const nsAString
& aName
) {
1907 RefPtr
<Event
> event
= NS_NewDOMEvent(this, nullptr, nullptr);
1908 // it doesn't bubble, and it isn't cancelable
1909 event
->InitEvent(aName
, false, false);
1910 event
->SetTrusted(true);
1912 DispatchEvent(*event
, rv
);
1913 return rv
.StealNSResult();
1917 already_AddRefed
<EventSource
> EventSource::Constructor(
1918 const GlobalObject
& aGlobal
, const nsAString
& aURL
,
1919 const EventSourceInit
& aEventSourceInitDict
, ErrorResult
& aRv
) {
1920 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(aGlobal
.GetAsSupports());
1921 if (NS_WARN_IF(!global
)) {
1922 aRv
.Throw(NS_ERROR_FAILURE
);
1926 nsCOMPtr
<nsICookieJarSettings
> cookieJarSettings
;
1927 nsCOMPtr
<nsPIDOMWindowInner
> ownerWindow
= do_QueryInterface(global
);
1929 Document
* doc
= ownerWindow
->GetExtantDoc();
1930 if (NS_WARN_IF(!doc
)) {
1931 aRv
.Throw(NS_ERROR_FAILURE
);
1935 cookieJarSettings
= doc
->CookieJarSettings();
1938 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
1939 if (!workerPrivate
) {
1940 aRv
.Throw(NS_ERROR_FAILURE
);
1944 cookieJarSettings
= workerPrivate
->CookieJarSettings();
1947 RefPtr
<EventSource
> eventSource
= new EventSource(
1948 global
, cookieJarSettings
, aEventSourceInitDict
.mWithCredentials
);
1950 if (NS_IsMainThread()) {
1951 // Get principal from document and init EventSourceImpl
1952 nsCOMPtr
<nsIScriptObjectPrincipal
> scriptPrincipal
=
1953 do_QueryInterface(aGlobal
.GetAsSupports());
1954 if (!scriptPrincipal
) {
1955 aRv
.Throw(NS_ERROR_FAILURE
);
1958 nsCOMPtr
<nsIPrincipal
> principal
= scriptPrincipal
->GetPrincipal();
1960 aRv
.Throw(NS_ERROR_FAILURE
);
1963 eventSource
->mESImpl
->Init(global
, principal
, aURL
, aRv
);
1964 if (NS_WARN_IF(aRv
.Failed())) {
1968 eventSource
->mESImpl
->InitChannelAndRequestEventSource(true);
1969 return eventSource
.forget();
1974 // Scope for possible failures that need cleanup
1975 auto guardESImpl
= MakeScopeExit([&] { eventSource
->mESImpl
= nullptr; });
1977 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
1978 MOZ_ASSERT(workerPrivate
);
1980 eventSource
->mESImpl
->mInnerWindowID
= workerPrivate
->WindowID();
1982 eventSource
->mESImpl
->Init(nullptr, workerPrivate
->GetPrincipal(), aURL
,
1984 if (NS_WARN_IF(aRv
.Failed())) {
1988 // In workers we have to keep the worker alive using a WorkerRef in order
1989 // to dispatch messages correctly.
1990 if (!eventSource
->mESImpl
->CreateWorkerRef(workerPrivate
)) {
1991 // The worker is already shutting down. Let's return an already closed
1992 // object, but marked as Connecting.
1993 if (eventSource
->mESImpl
) {
1994 // mESImpl is nulled by this call such that EventSourceImpl is
1995 // released before returning the object, otherwise
1996 // it will set EventSource to a CLOSED state in its DTOR..
1997 eventSource
->mESImpl
->Close();
1999 eventSource
->mReadyState
= EventSourceImpl::CONNECTING
;
2001 guardESImpl
.release();
2002 return eventSource
.forget();
2005 // Let's connect to the server.
2006 RefPtr
<ConnectRunnable
> connectRunnable
=
2007 new ConnectRunnable(workerPrivate
, eventSource
->mESImpl
);
2008 connectRunnable
->Dispatch(workerPrivate
, Canceling
, aRv
);
2009 if (NS_WARN_IF(aRv
.Failed())) {
2013 // End of scope for possible failures
2014 guardESImpl
.release();
2017 return eventSource
.forget();
2021 JSObject
* EventSource::WrapObject(JSContext
* aCx
,
2022 JS::Handle
<JSObject
*> aGivenProto
) {
2023 return EventSource_Binding::Wrap(aCx
, this, aGivenProto
);
2026 void EventSource::Close() {
2027 AssertIsOnTargetThread();
2029 // Close potentially kills ourself, ensure
2030 // to not access any members afterwards.
2035 //-----------------------------------------------------------------------------
2036 // EventSource::nsISupports
2037 //-----------------------------------------------------------------------------
2039 NS_IMPL_CYCLE_COLLECTION_CLASS(EventSource
)
2041 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(EventSource
,
2042 DOMEventTargetHelper
)
2043 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
2045 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(EventSource
,
2046 DOMEventTargetHelper
)
2048 // IsCertainlyaliveForCC will return true and cause the cycle
2049 // collector to skip this instance when mESImpl is non-null and
2050 // points back to ourself.
2051 // mESImpl is initialized to be non-null in the constructor
2052 // and should have been wiped out in our close function.
2053 MOZ_ASSERT_UNREACHABLE("Paranoia cleanup that should never happen.");
2056 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
2058 bool EventSource::IsCertainlyAliveForCC() const {
2059 // Until we are double linked forth and back, we want to stay alive.
2063 auto lock
= mESImpl
->mSharedData
.Lock();
2064 return lock
->mEventSource
== this;
2067 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EventSource
)
2068 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
2070 NS_IMPL_ADDREF_INHERITED(EventSource
, DOMEventTargetHelper
)
2071 NS_IMPL_RELEASE_INHERITED(EventSource
, DOMEventTargetHelper
)
2073 } // namespace mozilla::dom