LCOV - code coverage report
Current view: top level - dom/base - EventSource.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 920 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 103 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13