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