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

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

Generated by: LCOV version 1.13