tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

EventSource.cpp (65169B)


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