tor-browser

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

WebSocket.cpp (89142B)


      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 "WebSocket.h"
      8 
      9 #include "ErrorList.h"
     10 #include "js/ColumnNumber.h"  // JS::ColumnNumberOneOrigin
     11 #include "jsapi.h"
     12 #include "jsfriendapi.h"
     13 #include "mozilla/Atomics.h"
     14 #include "mozilla/BasePrincipal.h"
     15 #include "mozilla/DOMEventTargetHelper.h"
     16 #include "mozilla/LoadInfo.h"
     17 #include "mozilla/Preferences.h"
     18 #include "mozilla/StaticPrefs_dom.h"
     19 #include "mozilla/dom/CloseEvent.h"
     20 #include "mozilla/dom/Document.h"
     21 #include "mozilla/dom/File.h"
     22 #include "mozilla/dom/MessageEvent.h"
     23 #include "mozilla/dom/MessageEventBinding.h"
     24 #include "mozilla/dom/ScriptSettings.h"
     25 #include "mozilla/dom/SerializedStackHolder.h"
     26 #include "mozilla/dom/TypedArray.h"
     27 #include "mozilla/dom/UnionTypes.h"
     28 #include "mozilla/dom/WebSocketBinding.h"
     29 #include "mozilla/dom/WindowContext.h"
     30 #include "mozilla/dom/WorkerPrivate.h"
     31 #include "mozilla/dom/WorkerRef.h"
     32 #include "mozilla/dom/WorkerRunnable.h"
     33 #include "mozilla/dom/WorkerScope.h"
     34 #include "mozilla/dom/nsCSPContext.h"
     35 #include "mozilla/dom/nsCSPUtils.h"
     36 #include "mozilla/dom/nsHTTPSOnlyUtils.h"
     37 #include "mozilla/dom/nsMixedContentBlocker.h"
     38 #include "mozilla/net/WebSocketChannel.h"
     39 #include "mozilla/net/WebSocketEventService.h"
     40 #include "nsContentPolicyUtils.h"
     41 #include "nsContentUtils.h"
     42 #include "nsError.h"
     43 #include "nsGlobalWindowInner.h"
     44 #include "nsIAuthPrompt.h"
     45 #include "nsIAuthPrompt2.h"
     46 #include "nsIConsoleService.h"
     47 #include "nsICookieJarSettings.h"
     48 #include "nsIEventTarget.h"
     49 #include "nsIInterfaceRequestor.h"
     50 #include "nsILoadGroup.h"
     51 #include "nsIPrompt.h"
     52 #include "nsIPromptFactory.h"
     53 #include "nsIRequest.h"
     54 #include "nsIScriptError.h"
     55 #include "nsIScriptGlobalObject.h"
     56 #include "nsIScriptObjectPrincipal.h"
     57 #include "nsIStringBundle.h"
     58 #include "nsIThreadRetargetableRequest.h"
     59 #include "nsIURIMutator.h"
     60 #include "nsIURL.h"
     61 #include "nsIWebSocketChannel.h"
     62 #include "nsIWebSocketImpl.h"
     63 #include "nsIWebSocketListener.h"
     64 #include "nsIWindowWatcher.h"
     65 #include "nsJSUtils.h"
     66 #include "nsNetUtil.h"
     67 #include "nsProxyRelease.h"
     68 #include "nsThreadUtils.h"
     69 #include "nsWrapperCacheInlines.h"
     70 #include "nsXPCOM.h"
     71 #include "xpcpublic.h"
     72 
     73 #define OPEN_EVENT_STRING u"open"_ns
     74 #define MESSAGE_EVENT_STRING u"message"_ns
     75 #define ERROR_EVENT_STRING u"error"_ns
     76 #define CLOSE_EVENT_STRING u"close"_ns
     77 
     78 using namespace mozilla::net;
     79 
     80 namespace mozilla::dom {
     81 
     82 class WebSocketImpl;
     83 
     84 // This class is responsible for proxying nsIObserver and nsIWebSocketImpl
     85 // interfaces to WebSocketImpl. WebSocketImplProxy should be only accessed on
     86 // main thread, so we can let it support weak reference.
     87 class WebSocketImplProxy final : public nsIWebSocketImpl,
     88                                 public GlobalTeardownObserver,
     89                                 public GlobalFreezeObserver {
     90 public:
     91  NS_DECL_ISUPPORTS
     92  NS_DECL_NSIWEBSOCKETIMPL
     93 
     94  explicit WebSocketImplProxy(WebSocketImpl* aOwner) : mOwner(aOwner) {
     95    MOZ_ASSERT(NS_IsMainThread());
     96  }
     97 
     98  void Disconnect() {
     99    MOZ_ASSERT(NS_IsMainThread());
    100 
    101    mOwner = nullptr;
    102  }
    103 
    104  void BindToOwner(nsIGlobalObject* aOwner) {
    105    GlobalTeardownObserver::BindToOwner(aOwner);
    106    GlobalFreezeObserver::BindToOwner(aOwner);
    107  }
    108 
    109  void DisconnectFromOwner() override;
    110  void FrozenCallback(nsIGlobalObject* aGlobal) override;
    111 
    112 private:
    113  ~WebSocketImplProxy() = default;
    114 
    115  RefPtr<WebSocketImpl> mOwner;
    116 };
    117 
    118 class WebSocketImpl final : public nsIInterfaceRequestor,
    119                            public nsIWebSocketListener,
    120                            public nsIRequest,
    121                            public nsISerialEventTarget,
    122                            public nsIWebSocketImpl,
    123                            public GlobalTeardownObserver,
    124                            public GlobalFreezeObserver {
    125 public:
    126  NS_DECL_NSIINTERFACEREQUESTOR
    127  NS_DECL_NSIWEBSOCKETLISTENER
    128  NS_DECL_NSIREQUEST
    129  NS_DECL_THREADSAFE_ISUPPORTS
    130  NS_DECL_NSIEVENTTARGET_FULL
    131  NS_DECL_NSIWEBSOCKETIMPL
    132 
    133  explicit WebSocketImpl(WebSocket* aWebSocket)
    134      : mWebSocket(aWebSocket),
    135        mIsServerSide(false),
    136        mSecure(false),
    137        mOnCloseScheduled(false),
    138        mFailed(false),
    139        mDisconnectingOrDisconnected(false),
    140        mCloseEventWasClean(false),
    141        mCloseEventCode(nsIWebSocketChannel::CLOSE_ABNORMAL),
    142        mPort(0),
    143        mScriptLine(0),
    144        mScriptColumn(1),
    145        mInnerWindowID(0),
    146        mPrivateBrowsing(false),
    147        mIsChromeContext(false),
    148        mIsMainThread(true),
    149        mMutex("WebSocketImpl::mMutex"),
    150        mWorkerShuttingDown(false) {
    151    if (!NS_IsMainThread()) {
    152      mIsMainThread = false;
    153    }
    154  }
    155 
    156  void AssertIsOnTargetThread() const { MOZ_ASSERT(IsTargetThread()); }
    157 
    158  bool IsTargetThread() const;
    159 
    160  nsresult Init(nsIGlobalObject* aWindowGlobal, JSContext* aCx, bool aIsSecure,
    161                nsIPrincipal* aPrincipal, const Maybe<ClientInfo>& aClientInfo,
    162                nsICSPEventListener* aCSPEventListener, bool aIsServerSide,
    163                const nsAString& aURL, nsTArray<nsString>& aProtocolArray,
    164                const nsACString& aScriptFile, uint32_t aScriptLine,
    165                uint32_t aScriptColumn);
    166 
    167  nsresult AsyncOpen(nsIPrincipal* aPrincipal, uint64_t aInnerWindowID,
    168                     nsITransportProvider* aTransportProvider,
    169                     const nsACString& aNegotiatedExtensions,
    170                     UniquePtr<SerializedStackHolder> aOriginStack);
    171 
    172  nsresult ParseURL(const nsAString& aURL, nsIURI* aBaseURI);
    173  nsresult InitializeConnection(nsIPrincipal* aPrincipal,
    174                                nsICookieJarSettings* aCookieJarSettings);
    175 
    176  // These methods when called can release the WebSocket object
    177  void FailConnection(const RefPtr<WebSocketImpl>& aProofOfRef,
    178                      uint16_t reasonCode,
    179                      const nsACString& aReasonString = ""_ns);
    180  nsresult CloseConnection(const RefPtr<WebSocketImpl>& aProofOfRef,
    181                           uint16_t reasonCode,
    182                           const nsACString& aReasonString = ""_ns);
    183  void Disconnect(const RefPtr<WebSocketImpl>& aProofOfRef);
    184  void DisconnectInternal();
    185 
    186  nsresult ConsoleError();
    187  void PrintErrorOnConsole(const char* aBundleURI, const char* aError,
    188                           nsTArray<nsString>&& aFormatStrings);
    189 
    190  nsresult DoOnMessageAvailable(const nsACString& aMsg, bool isBinary) const;
    191 
    192  // ConnectionCloseEvents: 'error' event if needed, then 'close' event.
    193  nsresult ScheduleConnectionCloseEvents(nsISupports* aContext,
    194                                         nsresult aStatusCode);
    195  // 2nd half of ScheduleConnectionCloseEvents, run in its own event.
    196  void DispatchConnectionCloseEvents(const RefPtr<WebSocketImpl>& aProofOfRef);
    197 
    198  nsresult UpdateURI();
    199 
    200  void AddRefObject();
    201  void ReleaseObject();
    202 
    203  bool RegisterWorkerRef(WorkerPrivate* aWorkerPrivate);
    204  void UnregisterWorkerRef();
    205 
    206  nsresult CancelInternal();
    207 
    208  nsresult IsSecure(bool* aValue);
    209 
    210  void DisconnectFromOwner() override {
    211    RefPtr<WebSocketImpl> self(this);
    212    CloseConnection(self, nsIWebSocketChannel::CLOSE_GOING_AWAY);
    213  }
    214  void FrozenCallback(nsIGlobalObject* aGlobal) override {
    215    RefPtr<WebSocketImpl> self(this);
    216    CloseConnection(self, nsIWebSocketChannel::CLOSE_GOING_AWAY);
    217  }
    218 
    219  RefPtr<WebSocket> mWebSocket;
    220 
    221  nsCOMPtr<nsIWebSocketChannel> mChannel;
    222 
    223  bool mIsServerSide;  // True if we're implementing the server side of a
    224                       // websocket connection
    225 
    226  bool mSecure;  // if true it is using SSL and the wss scheme,
    227                 // otherwise it is using the ws scheme with no SSL
    228 
    229  bool mOnCloseScheduled;
    230  bool mFailed;
    231  Atomic<bool> mDisconnectingOrDisconnected;
    232 
    233  // Set attributes of DOM 'onclose' message
    234  bool mCloseEventWasClean;
    235  nsString mCloseEventReason;
    236  uint16_t mCloseEventCode;
    237 
    238  nsCString mAsciiHost;  // hostname
    239  uint32_t mPort;
    240  nsCString mResource;  // [filepath[?query]]
    241  nsString mUTF16Origin;
    242 
    243  nsCString mURI;
    244  nsCString mRequestedProtocolList;
    245 
    246  WeakPtr<Document> mOriginDocument;
    247 
    248  // Web Socket owner information:
    249  // - the script file name, UTF8 encoded.
    250  // - source code line number and 1-origin column number where the Web Socket
    251  //   object was constructed.
    252  // - the ID of the Web Socket owner window. Note that this may not
    253  //   be the same as the inner window where the script lives.
    254  //   e.g within iframes
    255  // These attributes are used for error reporting.
    256  nsCString mScriptFile;
    257  uint32_t mScriptLine;
    258  uint32_t mScriptColumn;
    259  uint64_t mInnerWindowID;
    260  bool mPrivateBrowsing;
    261  bool mIsChromeContext;
    262 
    263  RefPtr<ThreadSafeWorkerRef> mWorkerRef;
    264 
    265  nsWeakPtr mWeakLoadGroup;
    266 
    267  bool mIsMainThread;
    268 
    269  // This mutex protects mWorkerShuttingDown.
    270  mozilla::Mutex mMutex;
    271  bool mWorkerShuttingDown MOZ_GUARDED_BY(mMutex);
    272 
    273  RefPtr<WebSocketEventService> mService;
    274  nsCOMPtr<nsIPrincipal> mLoadingPrincipal;
    275 
    276  RefPtr<WebSocketImplProxy> mImplProxy;
    277 
    278 private:
    279  ~WebSocketImpl() {
    280    MOZ_RELEASE_ASSERT(NS_IsMainThread() == mIsMainThread ||
    281                       mDisconnectingOrDisconnected);
    282 
    283    // If we threw during Init we never called disconnect
    284    if (!mDisconnectingOrDisconnected) {
    285      RefPtr<WebSocketImpl> self(this);
    286      Disconnect(self);
    287    }
    288  }
    289 };
    290 
    291 NS_IMPL_ISUPPORTS(WebSocketImplProxy, nsIWebSocketImpl)
    292 
    293 void WebSocketImplProxy::DisconnectFromOwner() {
    294  if (!mOwner) {
    295    return;
    296  }
    297 
    298  mOwner->DisconnectFromOwner();
    299  GlobalTeardownObserver::DisconnectFromOwner();
    300 }
    301 
    302 void WebSocketImplProxy::FrozenCallback(nsIGlobalObject* aGlobal) {
    303  if (!mOwner) {
    304    return;
    305  }
    306 
    307  mOwner->FrozenCallback(aGlobal);
    308 }
    309 
    310 NS_IMETHODIMP
    311 WebSocketImplProxy::SendMessage(const nsAString& aMessage) {
    312  if (!mOwner) {
    313    return NS_OK;
    314  }
    315 
    316  return mOwner->SendMessage(aMessage);
    317 }
    318 
    319 NS_IMPL_ISUPPORTS(WebSocketImpl, nsIInterfaceRequestor, nsIWebSocketListener,
    320                  nsIRequest, nsIEventTarget, nsISerialEventTarget,
    321                  nsIWebSocketImpl)
    322 
    323 class CallDispatchConnectionCloseEvents final : public DiscardableRunnable {
    324 public:
    325  explicit CallDispatchConnectionCloseEvents(WebSocketImpl* aWebSocketImpl)
    326      : DiscardableRunnable("dom::CallDispatchConnectionCloseEvents"),
    327        mWebSocketImpl(aWebSocketImpl) {
    328    aWebSocketImpl->AssertIsOnTargetThread();
    329  }
    330 
    331  NS_IMETHOD Run() override {
    332    mWebSocketImpl->AssertIsOnTargetThread();
    333    mWebSocketImpl->DispatchConnectionCloseEvents(mWebSocketImpl);
    334    return NS_OK;
    335  }
    336 
    337 private:
    338  RefPtr<WebSocketImpl> mWebSocketImpl;
    339 };
    340 
    341 //-----------------------------------------------------------------------------
    342 // WebSocketImpl
    343 //-----------------------------------------------------------------------------
    344 
    345 namespace {
    346 
    347 class PrintErrorOnConsoleRunnable final : public WorkerMainThreadRunnable {
    348 public:
    349  PrintErrorOnConsoleRunnable(WebSocketImpl* aImpl, const char* aBundleURI,
    350                              const char* aError,
    351                              nsTArray<nsString>&& aFormatStrings)
    352      : WorkerMainThreadRunnable(aImpl->mWorkerRef->Private(),
    353                                 "WebSocket :: print error on console"_ns),
    354        mImpl(aImpl),
    355        mBundleURI(aBundleURI),
    356        mError(aError),
    357        mFormatStrings(std::move(aFormatStrings)) {}
    358 
    359  bool MainThreadRun() override {
    360    mImpl->PrintErrorOnConsole(mBundleURI, mError, std::move(mFormatStrings));
    361    return true;
    362  }
    363 
    364 private:
    365  // Raw pointer because this runnable is sync.
    366  WebSocketImpl* mImpl;
    367 
    368  const char* mBundleURI;
    369  const char* mError;
    370  nsTArray<nsString> mFormatStrings;
    371 };
    372 
    373 }  // namespace
    374 
    375 void WebSocketImpl::PrintErrorOnConsole(const char* aBundleURI,
    376                                        const char* aError,
    377                                        nsTArray<nsString>&& aFormatStrings) {
    378  // This method must run on the main thread.
    379 
    380  if (!NS_IsMainThread()) {
    381    MOZ_ASSERT(mWorkerRef);
    382 
    383    RefPtr<PrintErrorOnConsoleRunnable> runnable =
    384        new PrintErrorOnConsoleRunnable(this, aBundleURI, aError,
    385                                        std::move(aFormatStrings));
    386    ErrorResult rv;
    387    runnable->Dispatch(mWorkerRef->Private(), Killing, rv);
    388    // XXXbz this seems totally broken.  We should be propagating this out, but
    389    // none of our callers really propagate anything usefully.  Come to think of
    390    // it, why is this a syncrunnable anyway?  Can't this be a fire-and-forget
    391    // runnable??
    392    rv.SuppressException();
    393    return;
    394  }
    395 
    396  nsresult rv;
    397  nsCOMPtr<nsIStringBundleService> bundleService =
    398      do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
    399  NS_ENSURE_SUCCESS_VOID(rv);
    400 
    401  nsCOMPtr<nsIStringBundle> strBundle;
    402  rv = bundleService->CreateBundle(aBundleURI, getter_AddRefs(strBundle));
    403  NS_ENSURE_SUCCESS_VOID(rv);
    404 
    405  nsCOMPtr<nsIConsoleService> console(
    406      do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv));
    407  NS_ENSURE_SUCCESS_VOID(rv);
    408 
    409  nsCOMPtr<nsIScriptError> errorObject(
    410      do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv));
    411  NS_ENSURE_SUCCESS_VOID(rv);
    412 
    413  // Localize the error message
    414  nsAutoString message;
    415  if (!aFormatStrings.IsEmpty()) {
    416    rv = strBundle->FormatStringFromName(aError, aFormatStrings, message);
    417  } else {
    418    rv = strBundle->GetStringFromName(aError, message);
    419  }
    420  NS_ENSURE_SUCCESS_VOID(rv);
    421 
    422  if (mInnerWindowID) {
    423    rv = errorObject->InitWithWindowID(message, mScriptFile, mScriptLine,
    424                                       mScriptColumn, nsIScriptError::errorFlag,
    425                                       "Web Socket"_ns, mInnerWindowID);
    426  } else {
    427    rv = errorObject->Init(message, mScriptFile, mScriptLine, mScriptColumn,
    428                           nsIScriptError::errorFlag, "Web Socket"_ns,
    429                           mPrivateBrowsing, mIsChromeContext);
    430  }
    431 
    432  NS_ENSURE_SUCCESS_VOID(rv);
    433 
    434  // print the error message directly to the JS console
    435  rv = console->LogMessage(errorObject);
    436  NS_ENSURE_SUCCESS_VOID(rv);
    437 }
    438 
    439 namespace {
    440 
    441 class CancelWebSocketRunnable final : public Runnable {
    442 public:
    443  CancelWebSocketRunnable(nsIWebSocketChannel* aChannel, uint16_t aReasonCode,
    444                          const nsACString& aReasonString)
    445      : Runnable("dom::CancelWebSocketRunnable"),
    446        mChannel(aChannel),
    447        mReasonCode(aReasonCode),
    448        mReasonString(aReasonString) {}
    449 
    450  NS_IMETHOD Run() override {
    451    nsresult rv = mChannel->Close(mReasonCode, mReasonString);
    452    if (NS_FAILED(rv)) {
    453      NS_WARNING("Failed to dispatch the close message");
    454    }
    455    return NS_OK;
    456  }
    457 
    458 private:
    459  nsCOMPtr<nsIWebSocketChannel> mChannel;
    460  uint16_t mReasonCode;
    461  nsCString mReasonString;
    462 };
    463 
    464 class MOZ_STACK_CLASS MaybeDisconnect {
    465 public:
    466  explicit MaybeDisconnect(WebSocketImpl* aImpl) : mImpl(aImpl) {}
    467 
    468  ~MaybeDisconnect() {
    469    bool toDisconnect = false;
    470 
    471    {
    472      MutexAutoLock lock(mImpl->mMutex);
    473      toDisconnect = mImpl->mWorkerShuttingDown;
    474    }
    475 
    476    if (toDisconnect) {
    477      mImpl->Disconnect(mImpl);
    478    }
    479  }
    480 
    481 private:
    482  RefPtr<WebSocketImpl> mImpl;
    483 };
    484 
    485 class CloseConnectionRunnable final : public Runnable {
    486 public:
    487  CloseConnectionRunnable(WebSocketImpl* aImpl, uint16_t aReasonCode,
    488                          const nsACString& aReasonString)
    489      : Runnable("dom::CloseConnectionRunnable"),
    490        mImpl(aImpl),
    491        mReasonCode(aReasonCode),
    492        mReasonString(aReasonString) {}
    493 
    494  NS_IMETHOD Run() override {
    495    return mImpl->CloseConnection(mImpl, mReasonCode, mReasonString);
    496  }
    497 
    498 private:
    499  RefPtr<WebSocketImpl> mImpl;
    500  uint16_t mReasonCode;
    501  const nsCString mReasonString;
    502 };
    503 
    504 }  // namespace
    505 
    506 nsresult WebSocketImpl::CloseConnection(
    507    const RefPtr<WebSocketImpl>& aProofOfRef, uint16_t aReasonCode,
    508    const nsACString& aReasonString) {
    509  if (!IsTargetThread()) {
    510    nsCOMPtr<nsIRunnable> runnable =
    511        new CloseConnectionRunnable(this, aReasonCode, aReasonString);
    512    return Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
    513  }
    514 
    515  AssertIsOnTargetThread();
    516 
    517  if (mDisconnectingOrDisconnected) {
    518    return NS_OK;
    519  }
    520 
    521  // If this method is called because the worker is going away, we will not
    522  // receive the OnStop() method and we have to disconnect the WebSocket and
    523  // release the ThreadSafeWorkerRef.
    524  MaybeDisconnect md(this);
    525 
    526  uint16_t readyState = mWebSocket->ReadyState();
    527  if (readyState == WebSocket::CLOSING || readyState == WebSocket::CLOSED) {
    528    return NS_OK;
    529  }
    530 
    531  // The common case...
    532  if (mChannel) {
    533    mWebSocket->SetReadyState(WebSocket::CLOSING);
    534 
    535    // The channel has to be closed on the main-thread.
    536 
    537    if (NS_IsMainThread()) {
    538      return mChannel->Close(aReasonCode, aReasonString);
    539    }
    540 
    541    RefPtr<CancelWebSocketRunnable> runnable =
    542        new CancelWebSocketRunnable(mChannel, aReasonCode, aReasonString);
    543    return NS_DispatchToMainThread(runnable);
    544  }
    545 
    546  // No channel, but not disconnected: canceled or failed early
    547  MOZ_ASSERT(readyState == WebSocket::CONNECTING,
    548             "Should only get here for early websocket cancel/error");
    549 
    550  // Server won't be sending us a close code, so use what's passed in here.
    551  mCloseEventCode = aReasonCode;
    552  CopyUTF8toUTF16(aReasonString, mCloseEventReason);
    553 
    554  mWebSocket->SetReadyState(WebSocket::CLOSING);
    555 
    556  ScheduleConnectionCloseEvents(
    557      nullptr, (aReasonCode == nsIWebSocketChannel::CLOSE_NORMAL ||
    558                aReasonCode == nsIWebSocketChannel::CLOSE_GOING_AWAY)
    559                   ? NS_OK
    560                   : NS_ERROR_FAILURE);
    561 
    562  return NS_OK;
    563 }
    564 
    565 nsresult WebSocketImpl::ConsoleError() {
    566  AssertIsOnTargetThread();
    567 
    568  {
    569    MutexAutoLock lock(mMutex);
    570    if (mWorkerShuttingDown) {
    571      // Too late to report anything, bail out.
    572      return NS_OK;
    573    }
    574  }
    575 
    576  nsTArray<nsString> formatStrings;
    577  CopyUTF8toUTF16(mURI, *formatStrings.AppendElement());
    578 
    579  if (mWebSocket->ReadyState() < WebSocket::OPEN) {
    580    PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
    581                        "connectionFailure", std::move(formatStrings));
    582  } else {
    583    PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
    584                        "netInterrupt", std::move(formatStrings));
    585  }
    586  /// todo some specific errors - like for message too large
    587  return NS_OK;
    588 }
    589 
    590 void WebSocketImpl::FailConnection(const RefPtr<WebSocketImpl>& aProofOfRef,
    591                                   uint16_t aReasonCode,
    592                                   const nsACString& aReasonString) {
    593  AssertIsOnTargetThread();
    594 
    595  if (mDisconnectingOrDisconnected) {
    596    return;
    597  }
    598 
    599  ConsoleError();
    600  mFailed = true;
    601  CloseConnection(aProofOfRef, aReasonCode, aReasonString);
    602 
    603  if (NS_IsMainThread() && mImplProxy) {
    604    mImplProxy->Disconnect();
    605    mImplProxy = nullptr;
    606  }
    607 }
    608 
    609 namespace {
    610 
    611 class DisconnectInternalRunnable final : public WorkerMainThreadRunnable {
    612 public:
    613  explicit DisconnectInternalRunnable(WebSocketImpl* aImpl)
    614      : WorkerMainThreadRunnable(GetCurrentThreadWorkerPrivate(),
    615                                 "WebSocket :: disconnect"_ns),
    616        mImpl(aImpl) {}
    617 
    618  bool MainThreadRun() override {
    619    mImpl->DisconnectInternal();
    620    return true;
    621  }
    622 
    623 private:
    624  // NOTE: WebSocketImpl may be it the middle of being destroyed.
    625  // We can't just hold this as a RefPtr, since after the runnable ends
    626  // the sync caller will be released, and can finish destroying WebSocketImpl
    627  // before a ref here could be dropped.
    628  WebSocketImpl* mImpl;
    629 };
    630 
    631 }  // namespace
    632 
    633 void WebSocketImpl::Disconnect(const RefPtr<WebSocketImpl>& aProofOfRef) {
    634  MOZ_RELEASE_ASSERT(NS_IsMainThread() == mIsMainThread);
    635 
    636  if (mDisconnectingOrDisconnected) {
    637    return;
    638  }
    639 
    640  // DontKeepAliveAnyMore() and DisconnectInternal() can release the
    641  // object.  aProofOfRef ensures we're holding a reference to this until
    642  // the end of the method.
    643 
    644  // Disconnect can be called from some control event (such as a callback from
    645  // StrongWorkerRef). This will be scheduled before any other sync/async
    646  // runnable. In order to prevent some double Disconnect() calls, we use this
    647  // boolean.
    648  mDisconnectingOrDisconnected = true;
    649 
    650  // DisconnectInternal touches observers and nsILoadGroup and it must run on
    651  // the main thread.
    652 
    653  if (NS_IsMainThread()) {
    654    DisconnectInternal();
    655  } else {
    656    RefPtr<DisconnectInternalRunnable> runnable =
    657        new DisconnectInternalRunnable(this);
    658    ErrorResult rv;
    659    runnable->Dispatch(GetCurrentThreadWorkerPrivate(), Killing, rv);
    660    // XXXbz this seems totally broken.  We should be propagating this out, but
    661    // where to, exactly?
    662    rv.SuppressException();
    663  }
    664 
    665  // If we haven't called WebSocket::DisconnectFromOwner yet, update
    666  // web socket count here.
    667  if (nsIGlobalObject* global = mWebSocket->GetOwnerGlobal()) {
    668    global->UpdateWebSocketCount(-1);
    669  }
    670 
    671  NS_ReleaseOnMainThread("WebSocketImpl::mChannel", mChannel.forget());
    672  NS_ReleaseOnMainThread("WebSocketImpl::mService", mService.forget());
    673 
    674  mWebSocket->DontKeepAliveAnyMore();
    675  mWebSocket->mImpl = nullptr;
    676 
    677  if (mWorkerRef) {
    678    UnregisterWorkerRef();
    679  }
    680 
    681  // We want to release the WebSocket in the correct thread.
    682  mWebSocket = nullptr;
    683 }
    684 
    685 void WebSocketImpl::DisconnectInternal() {
    686  AssertIsOnMainThread();
    687 
    688  nsCOMPtr<nsILoadGroup> loadGroup = do_QueryReferent(mWeakLoadGroup);
    689  if (loadGroup) {
    690    loadGroup->RemoveRequest(this, nullptr, NS_OK);
    691    // mWeakLoadGroup has to be released on main-thread because WeakReferences
    692    // are not thread-safe.
    693    mWeakLoadGroup = nullptr;
    694  }
    695 
    696  if (!mWorkerRef) {
    697    GlobalTeardownObserver::DisconnectFromOwner();
    698    DisconnectFreezeObserver();
    699  }
    700 
    701  if (mImplProxy) {
    702    mImplProxy->Disconnect();
    703    mImplProxy = nullptr;
    704  }
    705 }
    706 
    707 //-----------------------------------------------------------------------------
    708 // WebSocketImpl::nsIWebSocketImpl
    709 //-----------------------------------------------------------------------------
    710 
    711 NS_IMETHODIMP
    712 WebSocketImpl::SendMessage(const nsAString& aMessage) {
    713  nsString message(aMessage);
    714  nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableFunction(
    715      "WebSocketImpl::SendMessage",
    716      [self = RefPtr<WebSocketImpl>(this), message = std::move(message)]() {
    717        ErrorResult IgnoredErrorResult;
    718        self->mWebSocket->Send(message, IgnoredErrorResult);
    719      });
    720  return Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
    721 }
    722 
    723 //-----------------------------------------------------------------------------
    724 // WebSocketImpl::nsIWebSocketListener methods:
    725 //-----------------------------------------------------------------------------
    726 
    727 nsresult WebSocketImpl::DoOnMessageAvailable(const nsACString& aMsg,
    728                                             bool isBinary) const {
    729  AssertIsOnTargetThread();
    730 
    731  if (mDisconnectingOrDisconnected) {
    732    return NS_OK;
    733  }
    734 
    735  int16_t readyState = mWebSocket->ReadyState();
    736  if (readyState == WebSocket::CLOSED) {
    737    NS_ERROR("Received message after CLOSED");
    738    return NS_ERROR_UNEXPECTED;
    739  }
    740 
    741  if (readyState == WebSocket::OPEN) {
    742    // Dispatch New Message
    743    nsresult rv = mWebSocket->CreateAndDispatchMessageEvent(aMsg, isBinary);
    744    if (NS_FAILED(rv)) {
    745      NS_WARNING("Failed to dispatch the message event");
    746    }
    747 
    748    return NS_OK;
    749  }
    750 
    751  // CLOSING should be the only other state where it's possible to get msgs
    752  // from channel: Spec says to drop them.
    753  MOZ_ASSERT(readyState == WebSocket::CLOSING,
    754             "Received message while CONNECTING or CLOSED");
    755  return NS_OK;
    756 }
    757 
    758 NS_IMETHODIMP
    759 WebSocketImpl::OnMessageAvailable(nsISupports* aContext,
    760                                  const nsACString& aMsg) {
    761  AssertIsOnTargetThread();
    762 
    763  if (mDisconnectingOrDisconnected) {
    764    return NS_OK;
    765  }
    766 
    767  return DoOnMessageAvailable(aMsg, false);
    768 }
    769 
    770 NS_IMETHODIMP
    771 WebSocketImpl::OnBinaryMessageAvailable(nsISupports* aContext,
    772                                        const nsACString& aMsg) {
    773  AssertIsOnTargetThread();
    774 
    775  if (mDisconnectingOrDisconnected) {
    776    return NS_OK;
    777  }
    778 
    779  return DoOnMessageAvailable(aMsg, true);
    780 }
    781 
    782 NS_IMETHODIMP
    783 WebSocketImpl::OnStart(nsISupports* aContext) {
    784  if (!IsTargetThread()) {
    785    nsCOMPtr<nsISupports> context = aContext;
    786    return Dispatch(NS_NewRunnableFunction("WebSocketImpl::OnStart",
    787                                           [self = RefPtr{this}, context]() {
    788                                             (void)self->OnStart(context);
    789                                           }),
    790                    NS_DISPATCH_NORMAL);
    791  }
    792 
    793  AssertIsOnTargetThread();
    794 
    795  if (mDisconnectingOrDisconnected) {
    796    return NS_OK;
    797  }
    798 
    799  int16_t readyState = mWebSocket->ReadyState();
    800 
    801  // This is the only function that sets OPEN, and should be called only once
    802  MOZ_ASSERT(readyState != WebSocket::OPEN,
    803             "readyState already OPEN! OnStart called twice?");
    804 
    805  // Nothing to do if we've already closed/closing
    806  if (readyState != WebSocket::CONNECTING) {
    807    return NS_OK;
    808  }
    809 
    810  // Attempt to kill "ghost" websocket: but usually too early for check to fail
    811  nsresult rv = mWebSocket->CheckCurrentGlobalCorrectness();
    812  if (NS_FAILED(rv)) {
    813    RefPtr<WebSocketImpl> self(this);
    814    CloseConnection(self, nsIWebSocketChannel::CLOSE_GOING_AWAY);
    815    return rv;
    816  }
    817 
    818  if (!mRequestedProtocolList.IsEmpty()) {
    819    rv = mChannel->GetProtocol(mWebSocket->mEstablishedProtocol);
    820    MOZ_ASSERT(NS_SUCCEEDED(rv));
    821  }
    822 
    823  rv = mChannel->GetExtensions(mWebSocket->mEstablishedExtensions);
    824  MOZ_ASSERT(NS_SUCCEEDED(rv));
    825  UpdateURI();
    826 
    827  mWebSocket->SetReadyState(WebSocket::OPEN);
    828 
    829  mService->WebSocketOpened(
    830      mChannel->Serial(), mInnerWindowID, mWebSocket->mEffectiveURL,
    831      mWebSocket->mEstablishedProtocol, mWebSocket->mEstablishedExtensions,
    832      mChannel->HttpChannelId());
    833 
    834  // Let's keep the object alive because the webSocket can be CCed in the
    835  // onopen callback
    836  RefPtr<WebSocket> webSocket = mWebSocket;
    837 
    838  // Call 'onopen'
    839  rv = webSocket->CreateAndDispatchSimpleEvent(OPEN_EVENT_STRING);
    840  if (NS_FAILED(rv)) {
    841    NS_WARNING("Failed to dispatch the open event");
    842  }
    843 
    844  webSocket->UpdateMustKeepAlive();
    845  return NS_OK;
    846 }
    847 
    848 NS_IMETHODIMP
    849 WebSocketImpl::OnStop(nsISupports* aContext, nsresult aStatusCode) {
    850  AssertIsOnTargetThread();
    851 
    852  if (mDisconnectingOrDisconnected) {
    853    return NS_OK;
    854  }
    855 
    856  // We can be CONNECTING here if connection failed.
    857  // We can be OPEN if we have encountered a fatal protocol error
    858  // We can be CLOSING if close() was called and/or server initiated close.
    859  MOZ_ASSERT(mWebSocket->ReadyState() != WebSocket::CLOSED,
    860             "Shouldn't already be CLOSED when OnStop called");
    861 
    862  return ScheduleConnectionCloseEvents(aContext, aStatusCode);
    863 }
    864 
    865 nsresult WebSocketImpl::ScheduleConnectionCloseEvents(nsISupports* aContext,
    866                                                      nsresult aStatusCode) {
    867  AssertIsOnTargetThread();
    868 
    869  // no-op if some other code has already initiated close event
    870  if (!mOnCloseScheduled) {
    871    mCloseEventWasClean = NS_SUCCEEDED(aStatusCode);
    872 
    873    if (aStatusCode == NS_BASE_STREAM_CLOSED) {
    874      // don't generate an error event just because of an unclean close
    875      aStatusCode = NS_OK;
    876    }
    877 
    878    if (aStatusCode == NS_ERROR_NET_INADEQUATE_SECURITY) {
    879      // TLS negotiation failed so we need to set status code to 1015.
    880      mCloseEventCode = 1015;
    881    }
    882 
    883    if (NS_FAILED(aStatusCode)) {
    884      ConsoleError();
    885      mFailed = true;
    886    }
    887 
    888    mOnCloseScheduled = true;
    889 
    890    NS_DispatchToCurrentThread(new CallDispatchConnectionCloseEvents(this));
    891  }
    892 
    893  return NS_OK;
    894 }
    895 
    896 NS_IMETHODIMP
    897 WebSocketImpl::OnAcknowledge(nsISupports* aContext, uint32_t aSize) {
    898  AssertIsOnTargetThread();
    899 
    900  if (mDisconnectingOrDisconnected) {
    901    return NS_OK;
    902  }
    903 
    904  MOZ_RELEASE_ASSERT(mWebSocket->mOutgoingBufferedAmount.isValid());
    905  if (aSize > mWebSocket->mOutgoingBufferedAmount.value()) {
    906    return NS_ERROR_UNEXPECTED;
    907  }
    908 
    909  CheckedUint64 outgoingBufferedAmount = mWebSocket->mOutgoingBufferedAmount;
    910  outgoingBufferedAmount -= aSize;
    911  if (!outgoingBufferedAmount.isValid()) {
    912    return NS_ERROR_UNEXPECTED;
    913  }
    914 
    915  mWebSocket->mOutgoingBufferedAmount = outgoingBufferedAmount;
    916  MOZ_RELEASE_ASSERT(mWebSocket->mOutgoingBufferedAmount.isValid());
    917 
    918  return NS_OK;
    919 }
    920 
    921 NS_IMETHODIMP
    922 WebSocketImpl::OnServerClose(nsISupports* aContext, uint16_t aCode,
    923                             const nsACString& aReason) {
    924  AssertIsOnTargetThread();
    925 
    926  if (mDisconnectingOrDisconnected) {
    927    return NS_OK;
    928  }
    929 
    930  int16_t readyState = mWebSocket->ReadyState();
    931 
    932  MOZ_ASSERT(readyState != WebSocket::CONNECTING,
    933             "Received server close before connected?");
    934  MOZ_ASSERT(readyState != WebSocket::CLOSED,
    935             "Received server close after already closed!");
    936 
    937  // store code/string for onclose DOM event
    938  mCloseEventCode = aCode;
    939  CopyUTF8toUTF16(aReason, mCloseEventReason);
    940 
    941  if (readyState == WebSocket::OPEN) {
    942    // Server initiating close.
    943    // RFC 6455, 5.5.1: "When sending a Close frame in response, the endpoint
    944    // typically echos the status code it received".
    945    // But never send certain codes, per section 7.4.1
    946    RefPtr<WebSocketImpl> self(this);
    947    if (aCode == 1005 || aCode == 1006 || aCode == 1015) {
    948      CloseConnection(self, 0, ""_ns);
    949    } else {
    950      CloseConnection(self, aCode, aReason);
    951    }
    952  } else {
    953    // We initiated close, and server has replied: OnStop does rest of the work.
    954    MOZ_ASSERT(readyState == WebSocket::CLOSING, "unknown state");
    955  }
    956 
    957  return NS_OK;
    958 }
    959 
    960 NS_IMETHODIMP
    961 WebSocketImpl::OnError() {
    962  if (!IsTargetThread()) {
    963    return Dispatch(
    964        NS_NewRunnableFunction("dom::FailConnectionRunnable",
    965                               [self = RefPtr{this}]() {
    966                                 self->FailConnection(
    967                                     self, nsIWebSocketChannel::CLOSE_ABNORMAL);
    968                               }),
    969        NS_DISPATCH_NORMAL);
    970  }
    971 
    972  AssertIsOnTargetThread();
    973  RefPtr<WebSocketImpl> self(this);
    974  FailConnection(self, nsIWebSocketChannel::CLOSE_ABNORMAL);
    975  return NS_OK;
    976 }
    977 
    978 //-----------------------------------------------------------------------------
    979 // WebSocketImpl::nsIInterfaceRequestor
    980 //-----------------------------------------------------------------------------
    981 
    982 NS_IMETHODIMP
    983 WebSocketImpl::GetInterface(const nsIID& aIID, void** aResult) {
    984  AssertIsOnMainThread();
    985 
    986  if (!mWebSocket || mWebSocket->ReadyState() == WebSocket::CLOSED) {
    987    return NS_ERROR_FAILURE;
    988  }
    989 
    990  if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
    991      aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
    992    nsCOMPtr<nsPIDOMWindowInner> win = mWebSocket->GetWindowIfCurrent();
    993    if (!win) {
    994      return NS_ERROR_NOT_AVAILABLE;
    995    }
    996 
    997    nsresult rv;
    998    nsCOMPtr<nsIPromptFactory> wwatch =
    999        do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
   1000    NS_ENSURE_SUCCESS(rv, rv);
   1001 
   1002    nsCOMPtr<nsPIDOMWindowOuter> outerWindow = win->GetOuterWindow();
   1003    return wwatch->GetPrompt(outerWindow, aIID, aResult);
   1004  }
   1005 
   1006  return QueryInterface(aIID, aResult);
   1007 }
   1008 
   1009 ////////////////////////////////////////////////////////////////////////////////
   1010 // WebSocket
   1011 ////////////////////////////////////////////////////////////////////////////////
   1012 
   1013 WebSocket::WebSocket(nsIGlobalObject* aGlobal)
   1014    : DOMEventTargetHelper(aGlobal),
   1015      mIsMainThread(true),
   1016      mKeepingAlive(false),
   1017      mCheckMustKeepAlive(true),
   1018      mOutgoingBufferedAmount(0),
   1019      mBinaryType(dom::BinaryType::Blob),
   1020      mMutex("WebSocket::mMutex"),
   1021      mReadyState(CONNECTING) {
   1022  MOZ_ASSERT(aGlobal);
   1023 
   1024  mImpl = new WebSocketImpl(this);
   1025  mIsMainThread = mImpl->mIsMainThread;
   1026 }
   1027 
   1028 WebSocket::~WebSocket() = default;
   1029 
   1030 mozilla::Maybe<EventCallbackDebuggerNotificationType>
   1031 WebSocket::GetDebuggerNotificationType() const {
   1032  return mozilla::Some(EventCallbackDebuggerNotificationType::Websocket);
   1033 }
   1034 
   1035 JSObject* WebSocket::WrapObject(JSContext* cx,
   1036                                JS::Handle<JSObject*> aGivenProto) {
   1037  return WebSocket_Binding::Wrap(cx, this, aGivenProto);
   1038 }
   1039 
   1040 //---------------------------------------------------------------------------
   1041 // WebIDL
   1042 //---------------------------------------------------------------------------
   1043 
   1044 // Constructor:
   1045 already_AddRefed<WebSocket> WebSocket::Constructor(
   1046    const GlobalObject& aGlobal, const nsAString& aUrl,
   1047    const StringOrStringSequence& aProtocols, ErrorResult& aRv) {
   1048  if (aProtocols.IsStringSequence()) {
   1049    return WebSocket::ConstructorCommon(
   1050        aGlobal, aUrl, aProtocols.GetAsStringSequence(), nullptr, ""_ns, aRv);
   1051  }
   1052 
   1053  Sequence<nsString> protocols;
   1054  if (!protocols.AppendElement(aProtocols.GetAsString(), fallible)) {
   1055    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
   1056    return nullptr;
   1057  }
   1058 
   1059  return WebSocket::ConstructorCommon(aGlobal, aUrl, protocols, nullptr, ""_ns,
   1060                                      aRv);
   1061 }
   1062 
   1063 already_AddRefed<WebSocket> WebSocket::CreateServerWebSocket(
   1064    const GlobalObject& aGlobal, const nsAString& aUrl,
   1065    const Sequence<nsString>& aProtocols,
   1066    nsITransportProvider* aTransportProvider,
   1067    const nsAString& aNegotiatedExtensions, ErrorResult& aRv) {
   1068  return WebSocket::ConstructorCommon(
   1069      aGlobal, aUrl, aProtocols, aTransportProvider,
   1070      NS_ConvertUTF16toUTF8(aNegotiatedExtensions), aRv);
   1071 }
   1072 
   1073 namespace {
   1074 
   1075 // This class is used to clear any exception.
   1076 class MOZ_STACK_CLASS ClearException {
   1077 public:
   1078  explicit ClearException(JSContext* aCx) : mCx(aCx) {}
   1079 
   1080  ~ClearException() { JS_ClearPendingException(mCx); }
   1081 
   1082 private:
   1083  JSContext* mCx;
   1084 };
   1085 
   1086 class WebSocketMainThreadRunnable : public WorkerMainThreadRunnable {
   1087 public:
   1088  WebSocketMainThreadRunnable(WorkerPrivate* aWorkerPrivate,
   1089                              const nsACString& aTelemetryKey)
   1090      : WorkerMainThreadRunnable(aWorkerPrivate, aTelemetryKey) {
   1091    MOZ_ASSERT(aWorkerPrivate);
   1092    aWorkerPrivate->AssertIsOnWorkerThread();
   1093  }
   1094 
   1095  bool MainThreadRun() override {
   1096    AssertIsOnMainThread();
   1097    MOZ_ASSERT(mWorkerRef);
   1098 
   1099    // Walk up to our containing page
   1100    WorkerPrivate* wp = mWorkerRef->Private()->GetTopLevelWorker();
   1101 
   1102    nsPIDOMWindowInner* window = wp->GetWindow();
   1103    if (window) {
   1104      return InitWithWindow(window);
   1105    }
   1106 
   1107    return InitWindowless(wp);
   1108  }
   1109 
   1110 protected:
   1111  virtual bool InitWithWindow(nsPIDOMWindowInner* aWindow) = 0;
   1112 
   1113  virtual bool InitWindowless(WorkerPrivate* aTopLevelWorkerPrivate) = 0;
   1114 };
   1115 
   1116 class InitRunnable final : public WebSocketMainThreadRunnable {
   1117 public:
   1118  InitRunnable(WorkerPrivate* aWorkerPrivate, WebSocketImpl* aImpl,
   1119               const Maybe<mozilla::dom::ClientInfo>& aClientInfo,
   1120               bool aIsServerSide, const nsAString& aURL,
   1121               nsTArray<nsString>& aProtocolArray,
   1122               const nsACString& aScriptFile, uint32_t aScriptLine,
   1123               uint32_t aScriptColumn)
   1124      : WebSocketMainThreadRunnable(aWorkerPrivate, "WebSocket :: init"_ns),
   1125        mImpl(aImpl),
   1126        mClientInfo(aClientInfo),
   1127        mIsServerSide(aIsServerSide),
   1128        mURL(aURL),
   1129        mProtocolArray(aProtocolArray),
   1130        mScriptFile(aScriptFile),
   1131        mScriptLine(aScriptLine),
   1132        mScriptColumn(aScriptColumn),
   1133        mErrorCode(NS_OK) {
   1134    aWorkerPrivate->AssertIsOnWorkerThread();
   1135  }
   1136 
   1137  nsresult ErrorCode() const { return mErrorCode; }
   1138 
   1139 protected:
   1140  virtual bool InitWithWindow(nsPIDOMWindowInner* aWindow) override {
   1141    AutoJSAPI jsapi;
   1142    if (NS_WARN_IF(!jsapi.Init(aWindow))) {
   1143      mErrorCode = NS_ERROR_FAILURE;
   1144      return true;
   1145    }
   1146 
   1147    ClearException ce(jsapi.cx());
   1148 
   1149    Document* doc = aWindow->GetExtantDoc();
   1150    if (!doc) {
   1151      mErrorCode = NS_ERROR_FAILURE;
   1152      return true;
   1153    }
   1154 
   1155    MOZ_ASSERT(mWorkerRef);
   1156 
   1157    nsIPrincipal* principal = mWorkerRef->Private()->GetPrincipal();
   1158    mErrorCode = mImpl->Init(
   1159        nullptr, jsapi.cx(), principal->SchemeIs("https"), principal,
   1160        mClientInfo, mWorkerRef->Private()->CSPEventListener(), mIsServerSide,
   1161        mURL, mProtocolArray, mScriptFile, mScriptLine, mScriptColumn);
   1162    return true;
   1163  }
   1164 
   1165  virtual bool InitWindowless(WorkerPrivate* aTopLevelWorkerPrivate) override {
   1166    MOZ_ASSERT(NS_IsMainThread());
   1167    MOZ_ASSERT(aTopLevelWorkerPrivate && !aTopLevelWorkerPrivate->GetWindow());
   1168    MOZ_ASSERT(mWorkerRef);
   1169 
   1170    WorkerPrivate* workerPrivate = mWorkerRef->Private();
   1171 
   1172    mErrorCode = mImpl->Init(
   1173        nullptr, nullptr, workerPrivate->GetPrincipal()->SchemeIs("https"),
   1174        aTopLevelWorkerPrivate->GetPrincipal(), mClientInfo,
   1175        workerPrivate->CSPEventListener(), mIsServerSide, mURL, mProtocolArray,
   1176        mScriptFile, mScriptLine, mScriptColumn);
   1177    return true;
   1178  }
   1179 
   1180  // Raw pointer. This worker runnable runs synchronously.
   1181  WebSocketImpl* mImpl;
   1182 
   1183  Maybe<ClientInfo> mClientInfo;
   1184  bool mIsServerSide;
   1185  const nsAString& mURL;
   1186  nsTArray<nsString>& mProtocolArray;
   1187  nsCString mScriptFile;
   1188  uint32_t mScriptLine;
   1189  uint32_t mScriptColumn;
   1190  nsresult mErrorCode;
   1191 };
   1192 
   1193 class ConnectRunnable final : public WebSocketMainThreadRunnable {
   1194 public:
   1195  ConnectRunnable(WorkerPrivate* aWorkerPrivate, WebSocketImpl* aImpl)
   1196      : WebSocketMainThreadRunnable(aWorkerPrivate, "WebSocket :: init"_ns),
   1197        mImpl(aImpl),
   1198        mConnectionFailed(true) {
   1199    MOZ_ASSERT(aWorkerPrivate);
   1200    aWorkerPrivate->AssertIsOnWorkerThread();
   1201  }
   1202 
   1203  bool ConnectionFailed() const { return mConnectionFailed; }
   1204 
   1205 protected:
   1206  virtual bool InitWithWindow(nsPIDOMWindowInner* aWindow) override {
   1207    MOZ_ASSERT(mWorkerRef);
   1208 
   1209    Document* doc = aWindow->GetExtantDoc();
   1210    if (!doc) {
   1211      return true;
   1212    }
   1213 
   1214    mConnectionFailed = NS_FAILED(mImpl->InitializeConnection(
   1215        doc->NodePrincipal(), mWorkerRef->Private()->CookieJarSettings()));
   1216    return true;
   1217  }
   1218 
   1219  virtual bool InitWindowless(WorkerPrivate* aTopLevelWorkerPrivate) override {
   1220    MOZ_ASSERT(NS_IsMainThread());
   1221    MOZ_ASSERT(aTopLevelWorkerPrivate && !aTopLevelWorkerPrivate->GetWindow());
   1222    MOZ_ASSERT(mWorkerRef);
   1223 
   1224    mConnectionFailed = NS_FAILED(mImpl->InitializeConnection(
   1225        aTopLevelWorkerPrivate->GetPrincipal(),
   1226        mWorkerRef->Private()->CookieJarSettings()));
   1227    return true;
   1228  }
   1229 
   1230  // Raw pointer. This worker runnable runs synchronously.
   1231  WebSocketImpl* mImpl;
   1232 
   1233  bool mConnectionFailed;
   1234 };
   1235 
   1236 class AsyncOpenRunnable final : public WebSocketMainThreadRunnable {
   1237 public:
   1238  explicit AsyncOpenRunnable(WebSocketImpl* aImpl,
   1239                             UniquePtr<SerializedStackHolder> aOriginStack)
   1240      : WebSocketMainThreadRunnable(aImpl->mWorkerRef->Private(),
   1241                                    "WebSocket :: AsyncOpen"_ns),
   1242        mImpl(aImpl),
   1243        mOriginStack(std::move(aOriginStack)),
   1244        mErrorCode(NS_OK) {
   1245    MOZ_ASSERT(aImpl->mWorkerRef);
   1246    aImpl->mWorkerRef->Private()->AssertIsOnWorkerThread();
   1247  }
   1248 
   1249  nsresult ErrorCode() const { return mErrorCode; }
   1250 
   1251 protected:
   1252  virtual bool InitWithWindow(nsPIDOMWindowInner* aWindow) override {
   1253    AssertIsOnMainThread();
   1254    MOZ_ASSERT(aWindow);
   1255 
   1256    Document* doc = aWindow->GetExtantDoc();
   1257    if (!doc) {
   1258      mErrorCode = NS_ERROR_FAILURE;
   1259      return true;
   1260    }
   1261 
   1262    nsCOMPtr<nsIPrincipal> principal = doc->PartitionedPrincipal();
   1263    if (!principal) {
   1264      mErrorCode = NS_ERROR_FAILURE;
   1265      return true;
   1266    }
   1267 
   1268    uint64_t windowID = 0;
   1269    if (WindowContext* wc = aWindow->GetWindowContext()) {
   1270      windowID = wc->InnerWindowId();
   1271    }
   1272 
   1273    mErrorCode = mImpl->AsyncOpen(principal, windowID, nullptr, ""_ns,
   1274                                  std::move(mOriginStack));
   1275    return true;
   1276  }
   1277 
   1278  virtual bool InitWindowless(WorkerPrivate* aTopLevelWorkerPrivate) override {
   1279    MOZ_ASSERT(NS_IsMainThread());
   1280    MOZ_ASSERT(aTopLevelWorkerPrivate && !aTopLevelWorkerPrivate->GetWindow());
   1281 
   1282    mErrorCode =
   1283        mImpl->AsyncOpen(aTopLevelWorkerPrivate->GetPartitionedPrincipal(), 0,
   1284                         nullptr, ""_ns, nullptr);
   1285    return true;
   1286  }
   1287 
   1288 private:
   1289  // Raw pointer. This worker runs synchronously.
   1290  WebSocketImpl* mImpl;
   1291 
   1292  UniquePtr<SerializedStackHolder> mOriginStack;
   1293 
   1294  nsresult mErrorCode;
   1295 };
   1296 
   1297 }  // namespace
   1298 
   1299 // Check a protocol entry contains only valid characters
   1300 bool WebSocket::IsValidProtocolString(const nsString& aValue) {
   1301  // RFC 6455 (4.1): "not including separator characters as defined in RFC 2616"
   1302  const char16_t illegalCharacters[] = {0x28, 0x29, 0x3C, 0x3E, 0x40, 0x2C,
   1303                                        0x3B, 0x3A, 0x5C, 0x22, 0x2F, 0x5B,
   1304                                        0x5D, 0x3F, 0x3D, 0x7B, 0x7D};
   1305 
   1306  // Cannot be empty string
   1307  if (aValue.IsEmpty()) {
   1308    return false;
   1309  }
   1310 
   1311  const auto* start = aValue.BeginReading();
   1312  const auto* end = aValue.EndReading();
   1313 
   1314  auto charFilter = [&](char16_t c) {
   1315    // RFC 6455 (4.1 P18): "in the range U+0021 to U+007E"
   1316    if (c < 0x21 || c > 0x7E) {
   1317      return true;
   1318    }
   1319 
   1320    return std::find(std::begin(illegalCharacters), std::end(illegalCharacters),
   1321                     c) != std::end(illegalCharacters);
   1322  };
   1323 
   1324  return std::find_if(start, end, charFilter) == end;
   1325 }
   1326 
   1327 already_AddRefed<WebSocket> WebSocket::ConstructorCommon(
   1328    const GlobalObject& aGlobal, const nsAString& aUrl,
   1329    const Sequence<nsString>& aProtocols,
   1330    nsITransportProvider* aTransportProvider,
   1331    const nsACString& aNegotiatedExtensions, ErrorResult& aRv) {
   1332  MOZ_ASSERT_IF(!aTransportProvider, aNegotiatedExtensions.IsEmpty());
   1333  nsCOMPtr<nsIPrincipal> principal;
   1334  nsCOMPtr<nsIPrincipal> partitionedPrincipal;
   1335 
   1336  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
   1337  if (NS_WARN_IF(!global)) {
   1338    aRv.Throw(NS_ERROR_FAILURE);
   1339    return nullptr;
   1340  }
   1341 
   1342  if (NS_IsMainThread()) {
   1343    nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal =
   1344        do_QueryInterface(aGlobal.GetAsSupports());
   1345    if (!scriptPrincipal) {
   1346      aRv.Throw(NS_ERROR_FAILURE);
   1347      return nullptr;
   1348    }
   1349 
   1350    principal = scriptPrincipal->GetPrincipal();
   1351    partitionedPrincipal = scriptPrincipal->PartitionedPrincipal();
   1352    if (!principal || !partitionedPrincipal) {
   1353      aRv.Throw(NS_ERROR_FAILURE);
   1354      return nullptr;
   1355    }
   1356  }
   1357 
   1358  nsTArray<nsString> protocolArray;
   1359 
   1360  for (uint32_t index = 0, len = aProtocols.Length(); index < len; ++index) {
   1361    const nsString& protocolElement = aProtocols[index];
   1362 
   1363    // Repeated protocols are not allowed
   1364    if (protocolArray.Contains(protocolElement)) {
   1365      aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
   1366      return nullptr;
   1367    }
   1368 
   1369    // Protocol string value must match constraints
   1370    if (!IsValidProtocolString(protocolElement)) {
   1371      aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
   1372      return nullptr;
   1373    }
   1374 
   1375    protocolArray.AppendElement(protocolElement);
   1376  }
   1377 
   1378  RefPtr<WebSocket> webSocket = new WebSocket(global);
   1379  RefPtr<WebSocketImpl> webSocketImpl = webSocket->mImpl;
   1380 
   1381  bool connectionFailed = true;
   1382 
   1383  global->UpdateWebSocketCount(1);
   1384 
   1385  if (NS_IsMainThread()) {
   1386    // We're keeping track of all main thread web sockets to be able to
   1387    // avoid throttling timeouts when we have active web sockets.
   1388 
   1389    bool isSecure = principal->SchemeIs("https");
   1390    aRv = webSocketImpl->IsSecure(&isSecure);
   1391    if (NS_WARN_IF(aRv.Failed())) {
   1392      return nullptr;
   1393    }
   1394 
   1395    aRv = webSocketImpl->Init(global, aGlobal.Context(), isSecure, principal,
   1396                              Nothing(), nullptr, !!aTransportProvider, aUrl,
   1397                              protocolArray, ""_ns, 0, 0);
   1398 
   1399    if (NS_WARN_IF(aRv.Failed())) {
   1400      return nullptr;
   1401    }
   1402 
   1403    nsCOMPtr<Document> doc = webSocket->GetDocumentIfCurrent();
   1404 
   1405    // the constructor should throw a SYNTAX_ERROR only if it fails to parse the
   1406    // url parameter, so don't throw if InitializeConnection fails, and call
   1407    // onerror/onclose asynchronously
   1408    connectionFailed = NS_FAILED(webSocketImpl->InitializeConnection(
   1409        principal, doc ? doc->CookieJarSettings() : nullptr));
   1410  } else {
   1411    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
   1412    MOZ_ASSERT(workerPrivate);
   1413 
   1414    uint32_t lineno;
   1415    JS::ColumnNumberOneOrigin column;
   1416    JS::AutoFilename file;
   1417    if (!JS::DescribeScriptedCaller(&file, aGlobal.Context(), &lineno,
   1418                                    &column)) {
   1419      NS_WARNING("Failed to get line number and filename in workers.");
   1420    }
   1421 
   1422    RefPtr<InitRunnable> runnable = new InitRunnable(
   1423        workerPrivate, webSocketImpl,
   1424        workerPrivate->GlobalScope()->GetClientInfo(), !!aTransportProvider,
   1425        aUrl, protocolArray, nsDependentCString(file.get()), lineno,
   1426        column.oneOriginValue());
   1427    runnable->Dispatch(workerPrivate, Canceling, aRv);
   1428    if (NS_WARN_IF(aRv.Failed())) {
   1429      return nullptr;
   1430    }
   1431 
   1432    aRv = runnable->ErrorCode();
   1433    if (NS_WARN_IF(aRv.Failed())) {
   1434      return nullptr;
   1435    }
   1436 
   1437    if (NS_WARN_IF(!webSocketImpl->RegisterWorkerRef(workerPrivate))) {
   1438      // The worker is shutting down.
   1439      aRv.Throw(NS_ERROR_FAILURE);
   1440      return nullptr;
   1441    }
   1442 
   1443    RefPtr<ConnectRunnable> connectRunnable =
   1444        new ConnectRunnable(workerPrivate, webSocketImpl);
   1445    connectRunnable->Dispatch(workerPrivate, Canceling, aRv);
   1446    if (NS_WARN_IF(aRv.Failed())) {
   1447      return nullptr;
   1448    }
   1449 
   1450    connectionFailed = connectRunnable->ConnectionFailed();
   1451  }
   1452 
   1453  // It can be that we have been already disconnected because the WebSocket is
   1454  // gone away while we where initializing the webSocket.
   1455  if (!webSocket->mImpl) {
   1456    aRv.Throw(NS_ERROR_FAILURE);
   1457    return nullptr;
   1458  }
   1459 
   1460  // We don't return an error if the connection just failed. Instead we dispatch
   1461  // an event.
   1462  if (connectionFailed) {
   1463    webSocketImpl->FailConnection(webSocketImpl,
   1464                                  nsIWebSocketChannel::CLOSE_ABNORMAL);
   1465  }
   1466 
   1467  // If we don't have a channel, the connection is failed and onerror() will be
   1468  // called asynchrounsly.
   1469  if (!webSocket->mImpl->mChannel) {
   1470    return webSocket.forget();
   1471  }
   1472 
   1473  class MOZ_STACK_CLASS ClearWebSocket {
   1474   public:
   1475    explicit ClearWebSocket(WebSocketImpl* aWebSocketImpl)
   1476        : mWebSocketImpl(aWebSocketImpl), mDone(false) {}
   1477 
   1478    void Done() { mDone = true; }
   1479 
   1480    ~ClearWebSocket() {
   1481      if (!mDone) {
   1482        mWebSocketImpl->mChannel = nullptr;
   1483        mWebSocketImpl->FailConnection(mWebSocketImpl,
   1484                                       nsIWebSocketChannel::CLOSE_ABNORMAL);
   1485      }
   1486    }
   1487 
   1488    RefPtr<WebSocketImpl> mWebSocketImpl;
   1489    bool mDone;
   1490  };
   1491 
   1492  ClearWebSocket cws(webSocket->mImpl);
   1493 
   1494  // This operation must be done on the correct thread. The rest must run on the
   1495  // main-thread.
   1496  aRv = webSocket->mImpl->mChannel->SetNotificationCallbacks(webSocket->mImpl);
   1497  if (NS_WARN_IF(aRv.Failed())) {
   1498    return nullptr;
   1499  }
   1500 
   1501  if (NS_IsMainThread()) {
   1502    MOZ_ASSERT(principal);
   1503    MOZ_ASSERT(partitionedPrincipal);
   1504 
   1505    nsCOMPtr<nsPIDOMWindowInner> ownerWindow = do_QueryInterface(global);
   1506 
   1507    UniquePtr<SerializedStackHolder> stack;
   1508    uint64_t windowID = 0;
   1509 
   1510    if (ownerWindow) {
   1511      BrowsingContext* browsingContext = ownerWindow->GetBrowsingContext();
   1512      if (browsingContext && browsingContext->WatchedByDevTools()) {
   1513        stack = GetCurrentStackForNetMonitor(aGlobal.Context());
   1514      }
   1515 
   1516      if (WindowContext* wc = ownerWindow->GetWindowContext()) {
   1517        windowID = wc->InnerWindowId();
   1518      }
   1519    }
   1520 
   1521    aRv = webSocket->mImpl->AsyncOpen(partitionedPrincipal, windowID,
   1522                                      aTransportProvider, aNegotiatedExtensions,
   1523                                      std::move(stack));
   1524  } else {
   1525    MOZ_ASSERT(!aTransportProvider && aNegotiatedExtensions.IsEmpty(),
   1526               "not yet implemented");
   1527 
   1528    UniquePtr<SerializedStackHolder> stack;
   1529    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
   1530    if (workerPrivate->IsWatchedByDevTools()) {
   1531      stack = GetCurrentStackForNetMonitor(aGlobal.Context());
   1532    }
   1533 
   1534    RefPtr<AsyncOpenRunnable> runnable =
   1535        new AsyncOpenRunnable(webSocket->mImpl, std::move(stack));
   1536    runnable->Dispatch(webSocket->mImpl->mWorkerRef->Private(), Canceling, aRv);
   1537    if (NS_WARN_IF(aRv.Failed())) {
   1538      return nullptr;
   1539    }
   1540 
   1541    aRv = runnable->ErrorCode();
   1542  }
   1543 
   1544  if (NS_WARN_IF(aRv.Failed())) {
   1545    return nullptr;
   1546  }
   1547 
   1548  // It can be that we have been already disconnected because the WebSocket is
   1549  // gone away while we where initializing the webSocket.
   1550  if (!webSocket->mImpl) {
   1551    aRv.Throw(NS_ERROR_FAILURE);
   1552    return nullptr;
   1553  }
   1554 
   1555  // Let's inform devtools about this new active WebSocket.
   1556  webSocket->mImpl->mService->WebSocketCreated(
   1557      webSocket->mImpl->mChannel->Serial(), webSocket->mImpl->mInnerWindowID,
   1558      webSocket->mURI, webSocket->mImpl->mRequestedProtocolList);
   1559  cws.Done();
   1560 
   1561  return webSocket.forget();
   1562 }
   1563 
   1564 NS_IMPL_CYCLE_COLLECTION_CLASS(WebSocket)
   1565 
   1566 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WebSocket,
   1567                                                  DOMEventTargetHelper)
   1568  if (tmp->mImpl) {
   1569    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImpl->mChannel)
   1570  }
   1571 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   1572 
   1573 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WebSocket, DOMEventTargetHelper)
   1574  if (tmp->mImpl) {
   1575    NS_IMPL_CYCLE_COLLECTION_UNLINK(mImpl->mChannel)
   1576    RefPtr<WebSocketImpl> pin(tmp->mImpl);
   1577    pin->Disconnect(pin);
   1578    MOZ_ASSERT(!tmp->mImpl);
   1579  }
   1580 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   1581 
   1582 bool WebSocket::IsCertainlyAliveForCC() const { return mKeepingAlive; }
   1583 
   1584 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebSocket)
   1585 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
   1586 
   1587 NS_IMPL_ADDREF_INHERITED(WebSocket, DOMEventTargetHelper)
   1588 NS_IMPL_RELEASE_INHERITED(WebSocket, DOMEventTargetHelper)
   1589 
   1590 void WebSocket::DisconnectFromOwner() {
   1591  // If we haven't called WebSocketImpl::Disconnect yet, update web
   1592  // socket count here.
   1593  if (mImpl && !mImpl->mDisconnectingOrDisconnected) {
   1594    GetOwnerGlobal()->UpdateWebSocketCount(-1);
   1595  }
   1596 
   1597  DOMEventTargetHelper::DisconnectFromOwner();
   1598 
   1599  if (mImpl) {
   1600    RefPtr<WebSocketImpl> pin(mImpl);
   1601    pin->CloseConnection(pin, nsIWebSocketChannel::CLOSE_GOING_AWAY);
   1602  }
   1603 
   1604  DontKeepAliveAnyMore();
   1605 }
   1606 
   1607 //-----------------------------------------------------------------------------
   1608 // WebSocketImpl:: initialization
   1609 //-----------------------------------------------------------------------------
   1610 
   1611 nsresult WebSocketImpl::Init(nsIGlobalObject* aWindowGlobal, JSContext* aCx,
   1612                             bool aIsSecure, nsIPrincipal* aPrincipal,
   1613                             const Maybe<ClientInfo>& aClientInfo,
   1614                             nsICSPEventListener* aCSPEventListener,
   1615                             bool aIsServerSide, const nsAString& aURL,
   1616                             nsTArray<nsString>& aProtocolArray,
   1617                             const nsACString& aScriptFile,
   1618                             uint32_t aScriptLine, uint32_t aScriptColumn) {
   1619  AssertIsOnMainThread();
   1620  MOZ_ASSERT(aPrincipal);
   1621 
   1622  mService = WebSocketEventService::GetOrCreate();
   1623 
   1624  // We need to keep the implementation alive in case the init disconnects it
   1625  // because of some error.
   1626  RefPtr<WebSocketImpl> kungfuDeathGrip = this;
   1627 
   1628  // Attempt to kill "ghost" websocket: but usually too early for check to fail
   1629  nsresult rv = mWebSocket->CheckCurrentGlobalCorrectness();
   1630  NS_ENSURE_SUCCESS(rv, rv);
   1631 
   1632  // Assign the sub protocol list and scan it for illegal values
   1633  for (uint32_t index = 0; index < aProtocolArray.Length(); ++index) {
   1634    if (!WebSocket::IsValidProtocolString(aProtocolArray[index])) {
   1635      return NS_ERROR_DOM_SYNTAX_ERR;
   1636    }
   1637 
   1638    if (!mRequestedProtocolList.IsEmpty()) {
   1639      mRequestedProtocolList.AppendLiteral(", ");
   1640    }
   1641 
   1642    AppendUTF16toUTF8(aProtocolArray[index], mRequestedProtocolList);
   1643  }
   1644 
   1645  // Shut down websocket if window is frozen or destroyed (only needed for
   1646  // "ghost" websockets--see bug 696085)
   1647  RefPtr<WebSocketImplProxy> proxy;
   1648  if (mIsMainThread) {
   1649    proxy = new WebSocketImplProxy(this);
   1650    proxy->BindToOwner(aWindowGlobal);
   1651  }
   1652 
   1653  if (!mIsMainThread) {
   1654    mScriptFile = aScriptFile;
   1655    mScriptLine = aScriptLine;
   1656    mScriptColumn = aScriptColumn;
   1657  } else {
   1658    MOZ_ASSERT(aCx);
   1659 
   1660    uint32_t lineno;
   1661    JS::ColumnNumberOneOrigin column;
   1662    JS::AutoFilename file;
   1663    if (JS::DescribeScriptedCaller(&file, aCx, &lineno, &column)) {
   1664      mScriptFile = file.get();
   1665      mScriptLine = lineno;
   1666      mScriptColumn = column.oneOriginValue();
   1667    }
   1668  }
   1669 
   1670  mIsServerSide = aIsServerSide;
   1671 
   1672  // If we don't have aCx, we are window-less, so we don't have a
   1673  // inner-windowID. This can happen in sharedWorkers and ServiceWorkers or in
   1674  // DedicateWorkers created by JSM.
   1675  if (aCx) {
   1676    if (nsPIDOMWindowInner* ownerWindow = mWebSocket->GetOwnerWindow()) {
   1677      mInnerWindowID = ownerWindow->WindowID();
   1678    }
   1679  }
   1680 
   1681  mPrivateBrowsing = aPrincipal->OriginAttributesRef().IsPrivateBrowsing();
   1682  mIsChromeContext = aPrincipal->IsSystemPrincipal();
   1683 
   1684  // parses the url
   1685  nsCOMPtr<nsIURI> baseURI = aPrincipal->GetURI();
   1686  rv = ParseURL(aURL, baseURI);
   1687  NS_ENSURE_SUCCESS(rv, rv);
   1688 
   1689  nsCOMPtr<Document> originDoc = mWebSocket->GetDocumentIfCurrent();
   1690  if (!originDoc) {
   1691    rv = mWebSocket->CheckCurrentGlobalCorrectness();
   1692    NS_ENSURE_SUCCESS(rv, rv);
   1693  }
   1694  mOriginDocument = originDoc;
   1695 
   1696  if (!mIsServerSide) {
   1697    nsCOMPtr<nsIURI> uri;
   1698    {
   1699      nsresult rv = NS_NewURI(getter_AddRefs(uri), mURI);
   1700 
   1701      // We crash here because we are sure that mURI is a valid URI, so either
   1702      // we are OOM'ing or something else bad is happening.
   1703      if (NS_WARN_IF(NS_FAILED(rv))) {
   1704        MOZ_CRASH();
   1705      }
   1706    }
   1707 
   1708    // The 'real' nsHttpChannel of the websocket gets opened in the parent.
   1709    // Since we don't serialize the CSP within child and parent and also not
   1710    // the context, we have to perform content policy checks here instead of
   1711    // AsyncOpen().
   1712    // Please note that websockets can't follow redirects, hence there is no
   1713    // need to perform a CSP check after redirects.
   1714    nsCOMPtr<nsILoadInfo> secCheckLoadInfo = MOZ_TRY(net::LoadInfo::Create(
   1715        aPrincipal,  // loading principal
   1716        aPrincipal,  // triggering principal
   1717        originDoc, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
   1718        nsIContentPolicy::TYPE_WEBSOCKET, aClientInfo));
   1719 
   1720    if (aCSPEventListener) {
   1721      secCheckLoadInfo->SetCspEventListener(aCSPEventListener);
   1722    }
   1723 
   1724    int16_t shouldLoad = nsIContentPolicy::ACCEPT;
   1725    rv = NS_CheckContentLoadPolicy(uri, secCheckLoadInfo, &shouldLoad,
   1726                                   nsContentUtils::GetContentPolicy());
   1727    NS_ENSURE_SUCCESS(rv, rv);
   1728 
   1729    if (NS_CP_REJECTED(shouldLoad)) {
   1730      // Disallowed by content policy
   1731      return NS_ERROR_CONTENT_BLOCKED;
   1732    }
   1733 
   1734    // If the HTTPS-Only mode is enabled, we need to upgrade the websocket
   1735    // connection from ws:// to wss:// and mark it as secure.
   1736    if (!mSecure && originDoc &&
   1737        !nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackURL(
   1738            originDoc->GetDocumentURI())) {
   1739      nsCOMPtr<nsIURI> uri;
   1740      nsresult rv = NS_NewURI(getter_AddRefs(uri), mURI);
   1741      NS_ENSURE_SUCCESS(rv, rv);
   1742 
   1743      // secCheckLoadInfo is only used for the triggering principal, so this
   1744      // is okay.
   1745      if (nsHTTPSOnlyUtils::ShouldUpgradeWebSocket(uri, secCheckLoadInfo)) {
   1746        mURI.ReplaceSubstring("ws://", "wss://");
   1747        if (NS_WARN_IF(mURI.Find("wss://") != 0)) {
   1748          return NS_OK;
   1749        }
   1750        mSecure = true;
   1751      }
   1752    }
   1753  }
   1754 
   1755  // Potentially the page uses the CSP directive 'upgrade-insecure-requests'.
   1756  // In such a case we have to upgrade ws: to wss: and also update mSecure
   1757  // to reflect that upgrade. Please note that we can not upgrade from ws:
   1758  // to wss: before performing content policy checks because CSP needs to
   1759  // send reports in case the scheme is about to be upgraded.
   1760  if (!mIsServerSide && !mSecure && originDoc &&
   1761      originDoc->GetUpgradeInsecureRequests(false) &&
   1762      !nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackURL(
   1763          originDoc->GetDocumentURI())) {
   1764    // let's use the old specification before the upgrade for logging
   1765    AutoTArray<nsString, 2> params;
   1766    CopyUTF8toUTF16(mURI, *params.AppendElement());
   1767 
   1768    // upgrade the request from ws:// to wss:// and mark as secure
   1769    mURI.ReplaceSubstring("ws://", "wss://");
   1770    if (NS_WARN_IF(mURI.Find("wss://") != 0)) {
   1771      return NS_OK;
   1772    }
   1773    mSecure = true;
   1774 
   1775    params.AppendElement(u"wss"_ns);
   1776    CSP_LogLocalizedStr("upgradeInsecureRequest", params,
   1777                        ""_ns,   // aSourceFile
   1778                        u""_ns,  // aScriptSample
   1779                        0,       // aLineNumber
   1780                        1,       // aColumnNumber
   1781                        nsIScriptError::warningFlag,
   1782                        "upgradeInsecureRequest"_ns, mInnerWindowID,
   1783                        mPrivateBrowsing);
   1784  }
   1785 
   1786  // Don't allow https:// to open ws://
   1787  // Check that we aren't a server side websocket or set to be upgraded to wss
   1788  // or allowing ws from https or a local websocket
   1789  if (!mIsServerSide && !mSecure && aIsSecure &&
   1790      !Preferences::GetBool("network.websocket.allowInsecureFromHTTPS",
   1791                            false) &&
   1792      !nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackHost(
   1793          mAsciiHost)) {
   1794    nsCOMPtr<nsIURI> uri;
   1795    nsresult rv = NS_NewURI(getter_AddRefs(uri), mURI);
   1796    NS_ENSURE_SUCCESS(rv, rv);
   1797    if (!nsMixedContentBlocker::IsPotentiallyTrustworthyOnion(uri)) {
   1798      return NS_ERROR_DOM_SECURITY_ERR;
   1799    }
   1800 
   1801    // Obtain the precursor's URI for the loading principal if it exists
   1802    // otherwise use the loading principal's URI
   1803    nsCOMPtr<nsIPrincipal> precursorPrincipal =
   1804        aPrincipal->GetPrecursorPrincipal();
   1805    nsCOMPtr<nsIURI> precursorOrLoadingURI = precursorPrincipal
   1806                                                 ? precursorPrincipal->GetURI()
   1807                                                 : aPrincipal->GetURI();
   1808 
   1809    // Check if the parent was loaded securely if we have one
   1810    if (precursorOrLoadingURI) {
   1811      nsCOMPtr<nsIURI> precursorOrLoadingInnermostURI =
   1812          NS_GetInnermostURI(precursorOrLoadingURI);
   1813      // If the parent was loaded securely then disallow loading ws
   1814      if (precursorOrLoadingInnermostURI &&
   1815          precursorOrLoadingInnermostURI->SchemeIs("https")) {
   1816        return NS_ERROR_DOM_SECURITY_ERR;
   1817      }
   1818    }
   1819  }
   1820 
   1821  if (mIsMainThread) {
   1822    mImplProxy = std::move(proxy);
   1823  }
   1824  return NS_OK;
   1825 }
   1826 
   1827 nsresult WebSocketImpl::AsyncOpen(
   1828    nsIPrincipal* aPrincipal, uint64_t aInnerWindowID,
   1829    nsITransportProvider* aTransportProvider,
   1830    const nsACString& aNegotiatedExtensions,
   1831    UniquePtr<SerializedStackHolder> aOriginStack) {
   1832  MOZ_ASSERT(NS_IsMainThread(), "Not running on main thread");
   1833  MOZ_ASSERT_IF(!aTransportProvider, aNegotiatedExtensions.IsEmpty());
   1834 
   1835  nsCString webExposedOriginSerialization;
   1836  nsresult rv = aPrincipal->GetWebExposedOriginSerialization(
   1837      webExposedOriginSerialization);
   1838  if (NS_FAILED(rv)) {
   1839    webExposedOriginSerialization.AssignLiteral("null");
   1840  }
   1841 
   1842  if (aTransportProvider) {
   1843    rv = mChannel->SetServerParameters(aTransportProvider,
   1844                                       aNegotiatedExtensions);
   1845    NS_ENSURE_SUCCESS(rv, rv);
   1846  }
   1847 
   1848  ToLowerCase(webExposedOriginSerialization);
   1849 
   1850  nsCOMPtr<nsIURI> uri;
   1851  if (!aTransportProvider) {
   1852    rv = NS_NewURI(getter_AddRefs(uri), mURI);
   1853    MOZ_ASSERT(NS_SUCCEEDED(rv));
   1854  }
   1855 
   1856  rv = mChannel->AsyncOpenNative(uri, webExposedOriginSerialization,
   1857                                 aPrincipal->OriginAttributesRef(),
   1858                                 aInnerWindowID, this, nullptr);
   1859  if (NS_WARN_IF(NS_FAILED(rv))) {
   1860    return NS_ERROR_CONTENT_BLOCKED;
   1861  }
   1862 
   1863  NotifyNetworkMonitorAlternateStack(mChannel, std::move(aOriginStack));
   1864 
   1865  mInnerWindowID = aInnerWindowID;
   1866 
   1867  return NS_OK;
   1868 }
   1869 
   1870 //-----------------------------------------------------------------------------
   1871 // WebSocketImpl methods:
   1872 //-----------------------------------------------------------------------------
   1873 
   1874 class nsAutoCloseWS final {
   1875 public:
   1876  explicit nsAutoCloseWS(WebSocketImpl* aWebSocketImpl)
   1877      : mWebSocketImpl(aWebSocketImpl) {}
   1878 
   1879  ~nsAutoCloseWS() {
   1880    if (!mWebSocketImpl->mChannel) {
   1881      mWebSocketImpl->CloseConnection(
   1882          mWebSocketImpl, nsIWebSocketChannel::CLOSE_INTERNAL_ERROR);
   1883    }
   1884  }
   1885 
   1886 private:
   1887  RefPtr<WebSocketImpl> mWebSocketImpl;
   1888 };
   1889 
   1890 nsresult WebSocketImpl::InitializeConnection(
   1891    nsIPrincipal* aPrincipal, nsICookieJarSettings* aCookieJarSettings) {
   1892  AssertIsOnMainThread();
   1893  MOZ_ASSERT(!mChannel, "mChannel should be null");
   1894 
   1895  nsCOMPtr<nsIWebSocketChannel> wsChannel;
   1896  nsAutoCloseWS autoClose(this);
   1897  nsresult rv;
   1898 
   1899  if (mSecure) {
   1900    wsChannel =
   1901        do_CreateInstance("@mozilla.org/network/protocol;1?name=wss", &rv);
   1902  } else {
   1903    wsChannel =
   1904        do_CreateInstance("@mozilla.org/network/protocol;1?name=ws", &rv);
   1905  }
   1906  NS_ENSURE_SUCCESS(rv, rv);
   1907 
   1908  // add ourselves to the document's load group and
   1909  // provide the http stack the loadgroup info too
   1910  nsCOMPtr<nsILoadGroup> loadGroup;
   1911  rv = GetLoadGroup(getter_AddRefs(loadGroup));
   1912  if (loadGroup) {
   1913    rv = wsChannel->SetLoadGroup(loadGroup);
   1914    NS_ENSURE_SUCCESS(rv, rv);
   1915    rv = loadGroup->AddRequest(this, nullptr);
   1916    NS_ENSURE_SUCCESS(rv, rv);
   1917 
   1918    mWeakLoadGroup = do_GetWeakReference(loadGroup);
   1919  }
   1920 
   1921  // manually adding loadinfo to the channel since it
   1922  // was not set during channel creation.
   1923  nsCOMPtr<Document> doc(mOriginDocument);
   1924 
   1925  // mOriginDocument has to be release on main-thread because WeakReferences
   1926  // are not thread-safe.
   1927  mOriginDocument = nullptr;
   1928 
   1929  // The TriggeringPrincipal for websockets must always be a script.
   1930  // Let's make sure that the doc's principal (if a doc exists)
   1931  // and aPrincipal are same origin.
   1932  MOZ_ASSERT(!doc || doc->NodePrincipal()->Equals(aPrincipal));
   1933 
   1934  rv = wsChannel->InitLoadInfoNative(
   1935      doc, doc ? doc->NodePrincipal() : aPrincipal, aPrincipal,
   1936      aCookieJarSettings,
   1937      nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
   1938      nsIContentPolicy::TYPE_WEBSOCKET, 0);
   1939  MOZ_ASSERT(NS_SUCCEEDED(rv));
   1940 
   1941  if (!mRequestedProtocolList.IsEmpty()) {
   1942    rv = wsChannel->SetProtocol(mRequestedProtocolList);
   1943    NS_ENSURE_SUCCESS(rv, rv);
   1944  }
   1945 
   1946  nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(wsChannel);
   1947  NS_ENSURE_TRUE(rr, NS_ERROR_FAILURE);
   1948 
   1949  rv = rr->RetargetDeliveryTo(this);
   1950  NS_ENSURE_SUCCESS(rv, rv);
   1951 
   1952  mChannel = wsChannel;
   1953 
   1954  if (mIsMainThread) {
   1955    MOZ_ASSERT(mImplProxy);
   1956    mService->AssociateWebSocketImplWithSerialID(mImplProxy,
   1957                                                 mChannel->Serial());
   1958  }
   1959 
   1960  return NS_OK;
   1961 }
   1962 
   1963 void WebSocketImpl::DispatchConnectionCloseEvents(
   1964    const RefPtr<WebSocketImpl>& aProofOfRef) {
   1965  AssertIsOnTargetThread();
   1966 
   1967  if (mDisconnectingOrDisconnected) {
   1968    return;
   1969  }
   1970 
   1971  mWebSocket->SetReadyState(WebSocket::CLOSED);
   1972 
   1973  // Let's keep the object alive because the webSocket can be CCed in the
   1974  // onerror or in the onclose callback
   1975  RefPtr<WebSocket> webSocket = mWebSocket;
   1976 
   1977  // Call 'onerror' if needed
   1978  if (mFailed) {
   1979    nsresult rv = webSocket->CreateAndDispatchSimpleEvent(ERROR_EVENT_STRING);
   1980    if (NS_FAILED(rv)) {
   1981      NS_WARNING("Failed to dispatch the error event");
   1982    }
   1983  }
   1984 
   1985  nsresult rv = webSocket->CreateAndDispatchCloseEvent(
   1986      mCloseEventWasClean, mCloseEventCode, mCloseEventReason);
   1987  if (NS_FAILED(rv)) {
   1988    NS_WARNING("Failed to dispatch the close event");
   1989  }
   1990 
   1991  webSocket->UpdateMustKeepAlive();
   1992  Disconnect(aProofOfRef);
   1993 }
   1994 
   1995 nsresult WebSocket::CreateAndDispatchSimpleEvent(const nsAString& aName) {
   1996  MOZ_ASSERT(mImpl);
   1997  AssertIsOnTargetThread();
   1998 
   1999  nsresult rv = CheckCurrentGlobalCorrectness();
   2000  if (NS_FAILED(rv)) {
   2001    return NS_OK;
   2002  }
   2003 
   2004  RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
   2005 
   2006  // it doesn't bubble, and it isn't cancelable
   2007  event->InitEvent(aName, false, false);
   2008  event->SetTrusted(true);
   2009 
   2010  ErrorResult err;
   2011  DispatchEvent(*event, err);
   2012  return err.StealNSResult();
   2013 }
   2014 
   2015 nsresult WebSocket::CreateAndDispatchMessageEvent(const nsACString& aData,
   2016                                                  bool aIsBinary) {
   2017  MOZ_ASSERT(mImpl);
   2018  AssertIsOnTargetThread();
   2019 
   2020  AutoJSAPI jsapi;
   2021  if (NS_WARN_IF(!jsapi.Init(GetOwnerGlobal()))) {
   2022    return NS_ERROR_FAILURE;
   2023  }
   2024 
   2025  JSContext* cx = jsapi.cx();
   2026 
   2027  nsresult rv = CheckCurrentGlobalCorrectness();
   2028  if (NS_FAILED(rv)) {
   2029    return NS_OK;
   2030  }
   2031 
   2032  uint16_t messageType = nsIWebSocketEventListener::TYPE_STRING;
   2033 
   2034  // Create appropriate JS object for message
   2035  JS::Rooted<JS::Value> jsData(cx);
   2036  if (aIsBinary) {
   2037    if (mBinaryType == dom::BinaryType::Blob) {
   2038      messageType = nsIWebSocketEventListener::TYPE_BLOB;
   2039 
   2040      RefPtr<Blob> blob =
   2041          Blob::CreateStringBlob(GetOwnerGlobal(), aData, u""_ns);
   2042      if (NS_WARN_IF(!blob)) {
   2043        return NS_ERROR_FAILURE;
   2044      }
   2045 
   2046      if (!ToJSValue(cx, blob, &jsData)) {
   2047        return NS_ERROR_FAILURE;
   2048      }
   2049 
   2050    } else if (mBinaryType == dom::BinaryType::Arraybuffer) {
   2051      messageType = nsIWebSocketEventListener::TYPE_ARRAYBUFFER;
   2052 
   2053      ErrorResult rv;
   2054      JS::Rooted<JSObject*> arrayBuf(cx, ArrayBuffer::Create(cx, aData, rv));
   2055      RETURN_NSRESULT_ON_FAILURE(rv);
   2056      jsData.setObject(*arrayBuf);
   2057    } else {
   2058      MOZ_CRASH("Unknown binary type!");
   2059      return NS_ERROR_UNEXPECTED;
   2060    }
   2061  } else {
   2062    // JS string
   2063    nsAutoString utf16Data;
   2064    if (!AppendUTF8toUTF16(aData, utf16Data, mozilla::fallible)) {
   2065      return NS_ERROR_OUT_OF_MEMORY;
   2066    }
   2067    JSString* jsString;
   2068    jsString = JS_NewUCStringCopyN(cx, utf16Data.get(), utf16Data.Length());
   2069    NS_ENSURE_TRUE(jsString, NS_ERROR_FAILURE);
   2070 
   2071    jsData.setString(jsString);
   2072  }
   2073 
   2074  mImpl->mService->WebSocketMessageAvailable(
   2075      mImpl->mChannel->Serial(), mImpl->mInnerWindowID, aData, messageType);
   2076 
   2077  // create an event that uses the MessageEvent interface,
   2078  // which does not bubble, is not cancelable, and has no default action
   2079 
   2080  RefPtr<MessageEvent> event = new MessageEvent(this, nullptr, nullptr);
   2081 
   2082  event->InitMessageEvent(nullptr, MESSAGE_EVENT_STRING, CanBubble::eNo,
   2083                          Cancelable::eNo, jsData, mImpl->mUTF16Origin, u""_ns,
   2084                          nullptr, Sequence<OwningNonNull<MessagePort>>());
   2085  event->SetTrusted(true);
   2086 
   2087  ErrorResult err;
   2088  DispatchEvent(*event, err);
   2089  return err.StealNSResult();
   2090 }
   2091 
   2092 nsresult WebSocket::CreateAndDispatchCloseEvent(bool aWasClean, uint16_t aCode,
   2093                                                const nsAString& aReason) {
   2094  AssertIsOnTargetThread();
   2095 
   2096  // This method is called by a runnable and it can happen that, in the
   2097  // meantime, GC unlinked this object, so mImpl could be null.
   2098  if (mImpl && mImpl->mChannel) {
   2099    mImpl->mService->WebSocketClosed(mImpl->mChannel->Serial(),
   2100                                     mImpl->mInnerWindowID, aWasClean, aCode,
   2101                                     aReason);
   2102  }
   2103 
   2104  nsresult rv = CheckCurrentGlobalCorrectness();
   2105  if (NS_FAILED(rv)) {
   2106    return NS_OK;
   2107  }
   2108 
   2109  CloseEventInit init;
   2110  init.mBubbles = false;
   2111  init.mCancelable = false;
   2112  init.mWasClean = aWasClean;
   2113  init.mCode = aCode;
   2114  init.mReason = aReason;
   2115 
   2116  RefPtr<CloseEvent> event =
   2117      CloseEvent::Constructor(this, CLOSE_EVENT_STRING, init);
   2118  event->SetTrusted(true);
   2119 
   2120  ErrorResult err;
   2121  DispatchEvent(*event, err);
   2122  return err.StealNSResult();
   2123 }
   2124 
   2125 nsresult WebSocketImpl::ParseURL(const nsAString& aURL, nsIURI* aBaseURI) {
   2126  AssertIsOnMainThread();
   2127  NS_ENSURE_TRUE(!aURL.IsEmpty(), NS_ERROR_DOM_SYNTAX_ERR);
   2128 
   2129  if (mIsServerSide) {
   2130    mWebSocket->mURI = aURL;
   2131    CopyUTF16toUTF8(mWebSocket->mURI, mURI);
   2132 
   2133    return NS_OK;
   2134  }
   2135 
   2136  nsCOMPtr<nsIURI> uri;
   2137  nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL, nullptr, aBaseURI);
   2138  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
   2139 
   2140  nsCOMPtr<nsIURL> parsedURL = do_QueryInterface(uri, &rv);
   2141  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
   2142 
   2143  nsAutoCString scheme;
   2144  rv = parsedURL->GetScheme(scheme);
   2145  NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && !scheme.IsEmpty(),
   2146                 NS_ERROR_DOM_SYNTAX_ERR);
   2147 
   2148  // If |urlRecord|'s [=url/scheme=] is "`http`", then set |urlRecord|'s
   2149  // [=url/scheme=] to "`ws`". Otherwise, if |urlRecord|'s [=url/scheme=] is
   2150  // "`https`", set |urlRecord|'s [=url/scheme=] to "`wss`".
   2151  // https://websockets.spec.whatwg.org/#dom-websocket-websocket
   2152 
   2153  if (scheme == "http" || scheme == "https") {
   2154    scheme = scheme == "https" ? "wss"_ns : "ws"_ns;
   2155 
   2156    NS_MutateURI mutator(parsedURL);
   2157    mutator.SetScheme(scheme);
   2158    rv = mutator.Finalize(parsedURL);
   2159    NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
   2160  }
   2161 
   2162  bool hasRef;
   2163  rv = parsedURL->GetHasRef(&hasRef);
   2164  NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && !hasRef, NS_ERROR_DOM_SYNTAX_ERR);
   2165 
   2166  nsAutoCString host;
   2167  rv = parsedURL->GetAsciiHost(host);
   2168  NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && !host.IsEmpty(), NS_ERROR_DOM_SYNTAX_ERR);
   2169 
   2170  int32_t port;
   2171  rv = parsedURL->GetPort(&port);
   2172  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
   2173 
   2174  nsAutoCString filePath;
   2175  rv = parsedURL->GetFilePath(filePath);
   2176  if (filePath.IsEmpty()) {
   2177    filePath.Assign('/');
   2178  }
   2179  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
   2180 
   2181  nsAutoCString query;
   2182  rv = parsedURL->GetQuery(query);
   2183  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
   2184 
   2185  if (scheme.LowerCaseEqualsLiteral("ws")) {
   2186    mSecure = false;
   2187    mPort = (port == -1) ? DEFAULT_WS_SCHEME_PORT : port;
   2188  } else if (scheme.LowerCaseEqualsLiteral("wss")) {
   2189    mSecure = true;
   2190    mPort = (port == -1) ? DEFAULT_WSS_SCHEME_PORT : port;
   2191  } else {
   2192    return NS_ERROR_DOM_SYNTAX_ERR;
   2193  }
   2194 
   2195  rv =
   2196      nsContentUtils::GetWebExposedOriginSerialization(parsedURL, mUTF16Origin);
   2197  NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
   2198 
   2199  mAsciiHost = host;
   2200  ToLowerCase(mAsciiHost);
   2201 
   2202  mResource = filePath;
   2203  if (!query.IsEmpty()) {
   2204    mResource.Append('?');
   2205    mResource.Append(query);
   2206  }
   2207  uint32_t length = mResource.Length();
   2208  uint32_t i;
   2209  for (i = 0; i < length; ++i) {
   2210    if (mResource[i] < static_cast<char16_t>(0x0021) ||
   2211        mResource[i] > static_cast<char16_t>(0x007E)) {
   2212      return NS_ERROR_DOM_SYNTAX_ERR;
   2213    }
   2214  }
   2215 
   2216  rv = parsedURL->GetSpec(mURI);
   2217  MOZ_ASSERT(NS_SUCCEEDED(rv));
   2218 
   2219  CopyUTF8toUTF16(mURI, mWebSocket->mURI);
   2220  return NS_OK;
   2221 }
   2222 
   2223 //-----------------------------------------------------------------------------
   2224 // Methods that keep alive the WebSocket object when:
   2225 //   1. the object has registered event listeners that can be triggered
   2226 //      ("strong event listeners");
   2227 //   2. there are outgoing not sent messages.
   2228 //-----------------------------------------------------------------------------
   2229 
   2230 void WebSocket::UpdateMustKeepAlive() {
   2231  // Here we could not have mImpl.
   2232  MOZ_ASSERT(NS_IsMainThread() == mIsMainThread);
   2233 
   2234  if (!mCheckMustKeepAlive || !mImpl) {
   2235    return;
   2236  }
   2237 
   2238  bool shouldKeepAlive = false;
   2239  uint16_t readyState = ReadyState();
   2240 
   2241  if (mListenerManager) {
   2242    switch (readyState) {
   2243      case CONNECTING: {
   2244        if (mListenerManager->HasListenersFor(OPEN_EVENT_STRING) ||
   2245            mListenerManager->HasListenersFor(MESSAGE_EVENT_STRING) ||
   2246            mListenerManager->HasListenersFor(ERROR_EVENT_STRING) ||
   2247            mListenerManager->HasListenersFor(CLOSE_EVENT_STRING)) {
   2248          shouldKeepAlive = true;
   2249        }
   2250      } break;
   2251 
   2252      case OPEN:
   2253      case CLOSING: {
   2254        if (mListenerManager->HasListenersFor(MESSAGE_EVENT_STRING) ||
   2255            mListenerManager->HasListenersFor(ERROR_EVENT_STRING) ||
   2256            mListenerManager->HasListenersFor(CLOSE_EVENT_STRING) ||
   2257            mOutgoingBufferedAmount.value() != 0) {
   2258          shouldKeepAlive = true;
   2259        }
   2260      } break;
   2261 
   2262      case CLOSED: {
   2263        shouldKeepAlive = false;
   2264      }
   2265    }
   2266  }
   2267 
   2268  if (mKeepingAlive && !shouldKeepAlive) {
   2269    mKeepingAlive = false;
   2270    mImpl->ReleaseObject();
   2271    // Note that this could be made 'alive' again if another listener is
   2272    // added.
   2273  } else if (!mKeepingAlive && shouldKeepAlive) {
   2274    mKeepingAlive = true;
   2275    mImpl->AddRefObject();
   2276  }
   2277 }
   2278 
   2279 void WebSocket::DontKeepAliveAnyMore() {
   2280  // Here we could not have mImpl.
   2281  MOZ_ASSERT(NS_IsMainThread() == mIsMainThread);
   2282 
   2283  if (mKeepingAlive) {
   2284    MOZ_ASSERT(mImpl);
   2285 
   2286    mKeepingAlive = false;
   2287    mImpl->ReleaseObject();
   2288  }
   2289 
   2290  mCheckMustKeepAlive = false;
   2291 }
   2292 
   2293 void WebSocketImpl::AddRefObject() {
   2294  MOZ_RELEASE_ASSERT(NS_IsMainThread() == mIsMainThread);
   2295  AddRef();
   2296 }
   2297 
   2298 void WebSocketImpl::ReleaseObject() {
   2299  MOZ_RELEASE_ASSERT(NS_IsMainThread() == mIsMainThread);
   2300  Release();
   2301 }
   2302 
   2303 bool WebSocketImpl::RegisterWorkerRef(WorkerPrivate* aWorkerPrivate) {
   2304  MOZ_ASSERT(aWorkerPrivate);
   2305 
   2306  RefPtr<WebSocketImpl> self(this);
   2307 
   2308  // In workers we have to keep the worker alive using a strong reference in
   2309  // order to dispatch messages correctly.
   2310  RefPtr<StrongWorkerRef> workerRef =
   2311      StrongWorkerRef::Create(aWorkerPrivate, "WebSocketImpl", [self]() {
   2312        {
   2313          MutexAutoLock lock(self->mMutex);
   2314          self->mWorkerShuttingDown = true;
   2315        }
   2316 
   2317        self->CloseConnection(self, nsIWebSocketChannel::CLOSE_GOING_AWAY,
   2318                              ""_ns);
   2319      });
   2320  if (NS_WARN_IF(!workerRef)) {
   2321    return false;
   2322  }
   2323 
   2324  mWorkerRef = new ThreadSafeWorkerRef(workerRef);
   2325  MOZ_ASSERT(mWorkerRef);
   2326 
   2327  return true;
   2328 }
   2329 
   2330 void WebSocketImpl::UnregisterWorkerRef() {
   2331  MOZ_ASSERT(mDisconnectingOrDisconnected);
   2332  MOZ_ASSERT(mWorkerRef);
   2333  mWorkerRef->Private()->AssertIsOnWorkerThread();
   2334 
   2335  {
   2336    MutexAutoLock lock(mMutex);
   2337    mWorkerShuttingDown = true;
   2338  }
   2339 
   2340  // The DTOR of this StrongWorkerRef will release the worker for us.
   2341  mWorkerRef = nullptr;
   2342 }
   2343 
   2344 nsresult WebSocketImpl::UpdateURI() {
   2345  AssertIsOnTargetThread();
   2346 
   2347  // Check for Redirections
   2348  RefPtr<BaseWebSocketChannel> channel;
   2349  channel = static_cast<BaseWebSocketChannel*>(mChannel.get());
   2350  MOZ_ASSERT(channel);
   2351 
   2352  channel->GetEffectiveURL(mWebSocket->mEffectiveURL);
   2353  mSecure = channel->IsEncrypted();
   2354 
   2355  return NS_OK;
   2356 }
   2357 
   2358 void WebSocket::EventListenerAdded(nsAtom* aType) {
   2359  AssertIsOnTargetThread();
   2360  UpdateMustKeepAlive();
   2361 }
   2362 
   2363 void WebSocket::EventListenerRemoved(nsAtom* aType) {
   2364  AssertIsOnTargetThread();
   2365  UpdateMustKeepAlive();
   2366 }
   2367 
   2368 //-----------------------------------------------------------------------------
   2369 // WebSocket - methods
   2370 //-----------------------------------------------------------------------------
   2371 
   2372 // webIDL: readonly attribute unsigned short readyState;
   2373 uint16_t WebSocket::ReadyState() {
   2374  MutexAutoLock lock(mMutex);
   2375  return mReadyState;
   2376 }
   2377 
   2378 void WebSocket::SetReadyState(uint16_t aReadyState) {
   2379  MutexAutoLock lock(mMutex);
   2380  mReadyState = aReadyState;
   2381 }
   2382 
   2383 // webIDL: readonly attribute unsigned long long bufferedAmount;
   2384 uint64_t WebSocket::BufferedAmount() const {
   2385  AssertIsOnTargetThread();
   2386  MOZ_RELEASE_ASSERT(mOutgoingBufferedAmount.isValid());
   2387  return mOutgoingBufferedAmount.value();
   2388 }
   2389 
   2390 // webIDL: attribute BinaryType binaryType;
   2391 dom::BinaryType WebSocket::BinaryType() const {
   2392  AssertIsOnTargetThread();
   2393  return mBinaryType;
   2394 }
   2395 
   2396 // webIDL: attribute BinaryType binaryType;
   2397 void WebSocket::SetBinaryType(dom::BinaryType aData) {
   2398  AssertIsOnTargetThread();
   2399  mBinaryType = aData;
   2400 }
   2401 
   2402 // webIDL: readonly attribute DOMString url
   2403 void WebSocket::GetUrl(nsAString& aURL) {
   2404  AssertIsOnTargetThread();
   2405 
   2406  if (mEffectiveURL.IsEmpty()) {
   2407    aURL = mURI;
   2408  } else {
   2409    aURL = mEffectiveURL;
   2410  }
   2411 }
   2412 
   2413 // webIDL: readonly attribute DOMString extensions;
   2414 void WebSocket::GetExtensions(nsAString& aExtensions) {
   2415  AssertIsOnTargetThread();
   2416  CopyUTF8toUTF16(mEstablishedExtensions, aExtensions);
   2417 }
   2418 
   2419 // webIDL: readonly attribute DOMString protocol;
   2420 void WebSocket::GetProtocol(nsAString& aProtocol) {
   2421  AssertIsOnTargetThread();
   2422  CopyUTF8toUTF16(mEstablishedProtocol, aProtocol);
   2423 }
   2424 
   2425 // webIDL: void send(DOMString data);
   2426 void WebSocket::Send(const nsAString& aData, ErrorResult& aRv) {
   2427  AssertIsOnTargetThread();
   2428 
   2429  nsAutoCString msgString;
   2430  if (!AppendUTF16toUTF8(aData, msgString, mozilla::fallible_t())) {
   2431    aRv.Throw(NS_ERROR_FILE_TOO_BIG);
   2432    return;
   2433  }
   2434  Send(nullptr, msgString, msgString.Length(), false, aRv);
   2435 }
   2436 
   2437 void WebSocket::Send(Blob& aData, ErrorResult& aRv) {
   2438  AssertIsOnTargetThread();
   2439 
   2440  nsCOMPtr<nsIInputStream> msgStream;
   2441  aData.CreateInputStream(getter_AddRefs(msgStream), aRv);
   2442  if (NS_WARN_IF(aRv.Failed())) {
   2443    return;
   2444  }
   2445 
   2446  uint64_t msgLength = aData.GetSize(aRv);
   2447  if (NS_WARN_IF(aRv.Failed())) {
   2448    return;
   2449  }
   2450 
   2451  if (msgLength > UINT32_MAX) {
   2452    aRv.Throw(NS_ERROR_FILE_TOO_BIG);
   2453    return;
   2454  }
   2455 
   2456  Send(msgStream, ""_ns, msgLength, true, aRv);
   2457 }
   2458 
   2459 void WebSocket::Send(const ArrayBuffer& aData, ErrorResult& aRv) {
   2460  AssertIsOnTargetThread();
   2461 
   2462  static_assert(
   2463      sizeof(std::remove_reference_t<decltype(aData)>::element_type) == 1,
   2464      "byte-sized data required");
   2465 
   2466  nsCString msgString;
   2467  if (!aData.AppendDataTo(msgString)) {
   2468    aRv.Throw(NS_ERROR_FILE_TOO_BIG);
   2469    return;
   2470  }
   2471  Send(nullptr, msgString, msgString.Length(), true, aRv);
   2472 }
   2473 
   2474 void WebSocket::Send(const ArrayBufferView& aData, ErrorResult& aRv) {
   2475  AssertIsOnTargetThread();
   2476 
   2477  static_assert(
   2478      sizeof(std::remove_reference_t<decltype(aData)>::element_type) == 1,
   2479      "byte-sized data required");
   2480 
   2481  nsCString msgString;
   2482  if (!aData.AppendDataTo(msgString)) {
   2483    aRv.Throw(NS_ERROR_FILE_TOO_BIG);
   2484    return;
   2485  }
   2486  Send(nullptr, msgString, msgString.Length(), true, aRv);
   2487 }
   2488 
   2489 void WebSocket::Send(nsIInputStream* aMsgStream, const nsACString& aMsgString,
   2490                     uint32_t aMsgLength, bool aIsBinary, ErrorResult& aRv) {
   2491  AssertIsOnTargetThread();
   2492 
   2493  int64_t readyState = ReadyState();
   2494  if (readyState == CONNECTING) {
   2495    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
   2496    return;
   2497  }
   2498 
   2499  CheckedUint64 outgoingBufferedAmount = mOutgoingBufferedAmount;
   2500  outgoingBufferedAmount += aMsgLength;
   2501  if (!outgoingBufferedAmount.isValid()) {
   2502    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
   2503    return;
   2504  }
   2505 
   2506  // Always increment outgoing buffer len, even if closed
   2507  mOutgoingBufferedAmount = outgoingBufferedAmount;
   2508  MOZ_RELEASE_ASSERT(mOutgoingBufferedAmount.isValid());
   2509 
   2510  if (readyState == CLOSING || readyState == CLOSED) {
   2511    return;
   2512  }
   2513 
   2514  // We must have mImpl when connected.
   2515  MOZ_ASSERT(mImpl);
   2516  MOZ_ASSERT(readyState == OPEN, "Unknown state in WebSocket::Send");
   2517 
   2518  nsresult rv;
   2519  if (aMsgStream) {
   2520    rv = mImpl->mChannel->SendBinaryStream(aMsgStream, aMsgLength);
   2521  } else {
   2522    if (aIsBinary) {
   2523      rv = mImpl->mChannel->SendBinaryMsg(aMsgString);
   2524    } else {
   2525      rv = mImpl->mChannel->SendMsg(aMsgString);
   2526    }
   2527  }
   2528 
   2529  if (NS_FAILED(rv)) {
   2530    aRv.Throw(rv);
   2531    return;
   2532  }
   2533 
   2534  UpdateMustKeepAlive();
   2535 }
   2536 
   2537 // webIDL: void close(optional unsigned short code, optional DOMString reason):
   2538 void WebSocket::Close(const Optional<uint16_t>& aCode,
   2539                      const Optional<nsAString>& aReason, ErrorResult& aRv) {
   2540  MOZ_RELEASE_ASSERT(NS_IsMainThread() == mIsMainThread);
   2541 
   2542  // the reason code is optional, but if provided it must be in a specific range
   2543  uint16_t closeCode = 0;
   2544  if (aCode.WasPassed()) {
   2545    if (aCode.Value() != 1000 &&
   2546        (aCode.Value() < 3000 || aCode.Value() > 4999)) {
   2547      aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
   2548      return;
   2549    }
   2550    closeCode = aCode.Value();
   2551  }
   2552 
   2553  nsCString closeReason;
   2554  if (aReason.WasPassed()) {
   2555    CopyUTF16toUTF8(aReason.Value(), closeReason);
   2556 
   2557    // The API requires the UTF-8 string to be 123 or less bytes
   2558    if (closeReason.Length() > 123) {
   2559      aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
   2560      return;
   2561    }
   2562  }
   2563 
   2564  int64_t readyState = ReadyState();
   2565  if (readyState == CLOSING || readyState == CLOSED) {
   2566    return;
   2567  }
   2568 
   2569  // If we don't have mImpl, we are in a shutting down worker where we are still
   2570  // in CONNECTING state, but already disconnected internally.
   2571  if (!mImpl) {
   2572    MOZ_ASSERT(readyState == CONNECTING);
   2573    SetReadyState(CLOSING);
   2574    return;
   2575  }
   2576 
   2577  // These could cause the mImpl to be released (and so this to be
   2578  // released); make sure it stays valid through the call
   2579  RefPtr<WebSocketImpl> pin(mImpl);
   2580 
   2581  if (readyState == CONNECTING) {
   2582    pin->FailConnection(pin, closeCode, closeReason);
   2583    return;
   2584  }
   2585 
   2586  MOZ_ASSERT(readyState == OPEN);
   2587  pin->CloseConnection(pin, closeCode, closeReason);
   2588 }
   2589 
   2590 //-----------------------------------------------------------------------------
   2591 // WebSocketImpl::nsIRequest
   2592 //-----------------------------------------------------------------------------
   2593 
   2594 NS_IMETHODIMP
   2595 WebSocketImpl::GetName(nsACString& aName) {
   2596  AssertIsOnMainThread();
   2597 
   2598  CopyUTF16toUTF8(mWebSocket->mURI, aName);
   2599  return NS_OK;
   2600 }
   2601 
   2602 NS_IMETHODIMP
   2603 WebSocketImpl::IsPending(bool* aValue) {
   2604  AssertIsOnTargetThread();
   2605 
   2606  int64_t readyState = mWebSocket->ReadyState();
   2607  *aValue = (readyState != WebSocket::CLOSED);
   2608  return NS_OK;
   2609 }
   2610 
   2611 NS_IMETHODIMP
   2612 WebSocketImpl::GetStatus(nsresult* aStatus) {
   2613  AssertIsOnTargetThread();
   2614 
   2615  *aStatus = NS_OK;
   2616  return NS_OK;
   2617 }
   2618 
   2619 namespace {
   2620 
   2621 class CancelRunnable final : public MainThreadWorkerRunnable {
   2622 public:
   2623  CancelRunnable(ThreadSafeWorkerRef* aWorkerRef, WebSocketImpl* aImpl)
   2624      : MainThreadWorkerRunnable("CancelRunnable"), mImpl(aImpl) {}
   2625 
   2626  bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
   2627    aWorkerPrivate->AssertIsOnWorkerThread();
   2628    return !NS_FAILED(mImpl->CancelInternal());
   2629  }
   2630 
   2631 private:
   2632  RefPtr<WebSocketImpl> mImpl;
   2633 };
   2634 
   2635 }  // namespace
   2636 
   2637 NS_IMETHODIMP WebSocketImpl::SetCanceledReason(const nsACString& aReason) {
   2638  return SetCanceledReasonImpl(aReason);
   2639 }
   2640 
   2641 NS_IMETHODIMP WebSocketImpl::GetCanceledReason(nsACString& aReason) {
   2642  return GetCanceledReasonImpl(aReason);
   2643 }
   2644 
   2645 NS_IMETHODIMP WebSocketImpl::CancelWithReason(nsresult aStatus,
   2646                                              const nsACString& aReason) {
   2647  return CancelWithReasonImpl(aStatus, aReason);
   2648 }
   2649 
   2650 // Window closed, stop/reload button pressed, user navigated away from page,
   2651 // etc.
   2652 NS_IMETHODIMP
   2653 WebSocketImpl::Cancel(nsresult aStatus) {
   2654  AssertIsOnMainThread();
   2655 
   2656  if (!mIsMainThread) {
   2657    MOZ_ASSERT(mWorkerRef);
   2658    RefPtr<CancelRunnable> runnable = new CancelRunnable(mWorkerRef, this);
   2659    if (!runnable->Dispatch(mWorkerRef->Private())) {
   2660      return NS_ERROR_FAILURE;
   2661    }
   2662 
   2663    return NS_OK;
   2664  }
   2665 
   2666  return CancelInternal();
   2667 }
   2668 
   2669 nsresult WebSocketImpl::CancelInternal() {
   2670  AssertIsOnTargetThread();
   2671 
   2672  // If CancelInternal is called by a runnable, we may already be disconnected
   2673  // by the time it runs.
   2674  if (mDisconnectingOrDisconnected) {
   2675    return NS_OK;
   2676  }
   2677 
   2678  int64_t readyState = mWebSocket->ReadyState();
   2679  if (readyState == WebSocket::CLOSING || readyState == WebSocket::CLOSED) {
   2680    return NS_OK;
   2681  }
   2682 
   2683  RefPtr<WebSocketImpl> self(this);
   2684  return CloseConnection(self, nsIWebSocketChannel::CLOSE_GOING_AWAY);
   2685 }
   2686 
   2687 NS_IMETHODIMP
   2688 WebSocketImpl::Suspend() {
   2689  AssertIsOnMainThread();
   2690  return NS_ERROR_NOT_IMPLEMENTED;
   2691 }
   2692 
   2693 NS_IMETHODIMP
   2694 WebSocketImpl::Resume() {
   2695  AssertIsOnMainThread();
   2696  return NS_ERROR_NOT_IMPLEMENTED;
   2697 }
   2698 
   2699 NS_IMETHODIMP
   2700 WebSocketImpl::GetLoadGroup(nsILoadGroup** aLoadGroup) {
   2701  AssertIsOnMainThread();
   2702 
   2703  *aLoadGroup = nullptr;
   2704 
   2705  if (mIsMainThread) {
   2706    nsCOMPtr<Document> doc = mWebSocket->GetDocumentIfCurrent();
   2707    if (doc) {
   2708      *aLoadGroup = doc->GetDocumentLoadGroup().take();
   2709    }
   2710 
   2711    return NS_OK;
   2712  }
   2713 
   2714  MOZ_ASSERT(mWorkerRef);
   2715 
   2716  nsPIDOMWindowInner* window = mWorkerRef->Private()->GetAncestorWindow();
   2717  if (!window) {
   2718    return NS_OK;
   2719  }
   2720 
   2721  Document* doc = window->GetExtantDoc();
   2722  if (doc) {
   2723    *aLoadGroup = doc->GetDocumentLoadGroup().take();
   2724  }
   2725 
   2726  return NS_OK;
   2727 }
   2728 
   2729 NS_IMETHODIMP
   2730 WebSocketImpl::SetLoadGroup(nsILoadGroup* aLoadGroup) {
   2731  AssertIsOnMainThread();
   2732  return NS_ERROR_UNEXPECTED;
   2733 }
   2734 
   2735 NS_IMETHODIMP
   2736 WebSocketImpl::GetLoadFlags(nsLoadFlags* aLoadFlags) {
   2737  AssertIsOnMainThread();
   2738 
   2739  *aLoadFlags = nsIRequest::LOAD_BACKGROUND;
   2740  return NS_OK;
   2741 }
   2742 
   2743 NS_IMETHODIMP
   2744 WebSocketImpl::SetLoadFlags(nsLoadFlags aLoadFlags) {
   2745  AssertIsOnMainThread();
   2746 
   2747  // we won't change the load flags at all.
   2748  return NS_OK;
   2749 }
   2750 
   2751 NS_IMETHODIMP
   2752 WebSocketImpl::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
   2753  return GetTRRModeImpl(aTRRMode);
   2754 }
   2755 
   2756 NS_IMETHODIMP
   2757 WebSocketImpl::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
   2758  return SetTRRModeImpl(aTRRMode);
   2759 }
   2760 
   2761 namespace {
   2762 
   2763 class WorkerRunnableDispatcher final : public WorkerThreadRunnable {
   2764  RefPtr<WebSocketImpl> mWebSocketImpl;
   2765 
   2766 public:
   2767  WorkerRunnableDispatcher(WebSocketImpl* aImpl,
   2768                           ThreadSafeWorkerRef* aWorkerRef,
   2769                           already_AddRefed<nsIRunnable> aEvent)
   2770      : WorkerThreadRunnable("WorkerRunnableDispatcher"),
   2771        mWebSocketImpl(aImpl),
   2772        mEvent(std::move(aEvent)) {}
   2773 
   2774  bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
   2775    aWorkerPrivate->AssertIsOnWorkerThread();
   2776 
   2777    // No messages when disconnected.
   2778    if (mWebSocketImpl->mDisconnectingOrDisconnected) {
   2779      NS_WARNING("Dispatching a WebSocket event after the disconnection!");
   2780      return true;
   2781    }
   2782 
   2783    return !NS_FAILED(mEvent->Run());
   2784  }
   2785 
   2786  void PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
   2787               bool aRunResult) override {}
   2788 
   2789  bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
   2790    // We don't call WorkerRunnable::PreDispatch because it would assert the
   2791    // wrong thing about which thread we're on.  We're on whichever thread the
   2792    // channel implementation is running on (probably the main thread or socket
   2793    // transport thread).
   2794    return true;
   2795  }
   2796 
   2797  void PostDispatch(WorkerPrivate* aWorkerPrivate,
   2798                    bool aDispatchResult) override {
   2799    // We don't call WorkerRunnable::PreDispatch because it would assert the
   2800    // wrong thing about which thread we're on.  We're on whichever thread the
   2801    // channel implementation is running on (probably the main thread or socket
   2802    // transport thread).
   2803  }
   2804 
   2805 private:
   2806  nsCOMPtr<nsIRunnable> mEvent;
   2807 };
   2808 
   2809 }  // namespace
   2810 
   2811 NS_IMETHODIMP
   2812 WebSocketImpl::DispatchFromScript(nsIRunnable* aEvent, DispatchFlags aFlags) {
   2813  return Dispatch(do_AddRef(aEvent), aFlags);
   2814 }
   2815 
   2816 NS_IMETHODIMP
   2817 WebSocketImpl::Dispatch(already_AddRefed<nsIRunnable> aEvent,
   2818                        DispatchFlags aFlags) {
   2819  // FIXME: This dispatch implementation is inconsistent about whether or not
   2820  // failure causes a leak when the `NS_DISPATCH_FALLIBLE` flag is not set.
   2821  nsCOMPtr<nsIRunnable> event_ref(aEvent);
   2822  if (mIsMainThread) {
   2823    nsISerialEventTarget* target = GetMainThreadSerialEventTarget();
   2824    NS_ENSURE_TRUE(target, NS_ERROR_FAILURE);
   2825    return target->Dispatch(event_ref.forget(), aFlags);
   2826  }
   2827 
   2828  MutexAutoLock lock(mMutex);
   2829  if (mWorkerShuttingDown) {
   2830    return NS_OK;
   2831  }
   2832 
   2833  MOZ_DIAGNOSTIC_ASSERT(mWorkerRef);
   2834 
   2835  // If the target is a worker, we have to use a custom WorkerRunnableDispatcher
   2836  // runnable.
   2837  RefPtr<WorkerRunnableDispatcher> event =
   2838      new WorkerRunnableDispatcher(this, mWorkerRef, event_ref.forget());
   2839 
   2840  if (!event->Dispatch(mWorkerRef->Private())) {
   2841    return NS_ERROR_FAILURE;
   2842  }
   2843 
   2844  return NS_OK;
   2845 }
   2846 
   2847 NS_IMETHODIMP
   2848 WebSocketImpl::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t) {
   2849  return NS_ERROR_NOT_IMPLEMENTED;
   2850 }
   2851 
   2852 NS_IMETHODIMP
   2853 WebSocketImpl::RegisterShutdownTask(nsITargetShutdownTask*) {
   2854  return NS_ERROR_NOT_IMPLEMENTED;
   2855 }
   2856 
   2857 NS_IMETHODIMP
   2858 WebSocketImpl::UnregisterShutdownTask(nsITargetShutdownTask*) {
   2859  return NS_ERROR_NOT_IMPLEMENTED;
   2860 }
   2861 
   2862 NS_IMETHODIMP
   2863 WebSocketImpl::IsOnCurrentThread(bool* aResult) {
   2864  *aResult = IsTargetThread();
   2865  return NS_OK;
   2866 }
   2867 
   2868 NS_IMETHODIMP_(bool)
   2869 WebSocketImpl::IsOnCurrentThreadInfallible() { return IsTargetThread(); }
   2870 
   2871 bool WebSocketImpl::IsTargetThread() const {
   2872  // FIXME: This should also check if we're on the worker thread. Code using
   2873  // `IsOnCurrentThread` could easily misbehave here!
   2874  return NS_IsMainThread() == mIsMainThread;
   2875 }
   2876 
   2877 void WebSocket::AssertIsOnTargetThread() const {
   2878  MOZ_ASSERT(NS_IsMainThread() == mIsMainThread);
   2879 }
   2880 
   2881 nsresult WebSocketImpl::IsSecure(bool* aValue) {
   2882  MOZ_ASSERT(NS_IsMainThread());
   2883  MOZ_ASSERT(mIsMainThread);
   2884 
   2885  // Check the principal's uri to determine if we were loaded from https.
   2886  nsCOMPtr<nsIGlobalObject> globalObject(GetEntryGlobal());
   2887  nsCOMPtr<nsIPrincipal> principal;
   2888 
   2889  if (globalObject) {
   2890    principal = globalObject->PrincipalOrNull();
   2891  }
   2892 
   2893  nsCOMPtr<nsPIDOMWindowInner> innerWindow = do_QueryInterface(globalObject);
   2894  if (!innerWindow) {
   2895    // If we are in a XPConnect sandbox or in a JS component,
   2896    // innerWindow will be null. There is nothing on top of this to be
   2897    // considered.
   2898    if (NS_WARN_IF(!principal)) {
   2899      return NS_OK;
   2900    }
   2901    *aValue = principal->SchemeIs("https");
   2902    return NS_OK;
   2903  }
   2904 
   2905  RefPtr<WindowContext> windowContext = innerWindow->GetWindowContext();
   2906  if (NS_WARN_IF(!windowContext)) {
   2907    return NS_ERROR_DOM_SECURITY_ERR;
   2908  }
   2909 
   2910  while (true) {
   2911    if (windowContext->GetIsSecure()) {
   2912      *aValue = true;
   2913      return NS_OK;
   2914    }
   2915 
   2916    if (windowContext->IsTop()) {
   2917      break;
   2918    } else {
   2919      // If we're not a top window get the parent window context instead.
   2920      windowContext = windowContext->GetParentWindowContext();
   2921    }
   2922 
   2923    if (NS_WARN_IF(!windowContext)) {
   2924      return NS_ERROR_DOM_SECURITY_ERR;
   2925    }
   2926  }
   2927 
   2928  *aValue = windowContext->GetIsSecure();
   2929  return NS_OK;
   2930 }
   2931 
   2932 }  // namespace mozilla::dom