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