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/. */
8 #include "mozilla/dom/WebSocketBinding.h"
9 #include "mozilla/net/WebSocketChannel.h"
11 #include "js/ColumnNumber.h" // JS::ColumnNumberZeroOrigin
13 #include "jsfriendapi.h"
14 #include "mozilla/Atomics.h"
15 #include "mozilla/BasePrincipal.h"
16 #include "mozilla/DOMEventTargetHelper.h"
17 #include "mozilla/dom/File.h"
18 #include "mozilla/dom/MessageEvent.h"
19 #include "mozilla/dom/MessageEventBinding.h"
20 #include "mozilla/dom/nsCSPContext.h"
21 #include "mozilla/dom/nsCSPUtils.h"
22 #include "mozilla/dom/nsHTTPSOnlyUtils.h"
23 #include "mozilla/dom/nsMixedContentBlocker.h"
24 #include "mozilla/dom/ScriptSettings.h"
25 #include "mozilla/dom/SerializedStackHolder.h"
26 #include "mozilla/dom/UnionTypes.h"
27 #include "mozilla/dom/WindowContext.h"
28 #include "mozilla/dom/WorkerPrivate.h"
29 #include "mozilla/dom/WorkerRef.h"
30 #include "mozilla/dom/WorkerRunnable.h"
31 #include "mozilla/dom/WorkerScope.h"
32 #include "mozilla/StaticPrefs_dom.h"
33 #include "mozilla/LoadInfo.h"
34 #include "nsIScriptGlobalObject.h"
35 #include "mozilla/dom/Document.h"
37 #include "nsContentUtils.h"
39 #include "nsICookieJarSettings.h"
40 #include "nsIScriptObjectPrincipal.h"
42 #include "nsThreadUtils.h"
43 #include "nsIPromptFactory.h"
44 #include "nsIWindowWatcher.h"
45 #include "nsIPrompt.h"
46 #include "nsIStringBundle.h"
47 #include "nsIConsoleService.h"
48 #include "mozilla/dom/CloseEvent.h"
49 #include "mozilla/net/WebSocketEventService.h"
50 #include "nsJSUtils.h"
51 #include "nsIScriptError.h"
52 #include "nsNetUtil.h"
53 #include "nsIAuthPrompt.h"
54 #include "nsIAuthPrompt2.h"
55 #include "nsILoadGroup.h"
56 #include "mozilla/Preferences.h"
57 #include "xpcpublic.h"
58 #include "nsContentPolicyUtils.h"
59 #include "nsWrapperCacheInlines.h"
60 #include "nsIObserverService.h"
61 #include "nsIEventTarget.h"
62 #include "nsIInterfaceRequestor.h"
63 #include "nsIObserver.h"
64 #include "nsIRequest.h"
65 #include "nsIThreadRetargetableRequest.h"
66 #include "nsIWebSocketChannel.h"
67 #include "nsIWebSocketListener.h"
68 #include "nsProxyRelease.h"
69 #include "nsWeakReference.h"
70 #include "nsIWebSocketImpl.h"
72 #define OPEN_EVENT_STRING u"open"_ns
73 #define MESSAGE_EVENT_STRING u"message"_ns
74 #define ERROR_EVENT_STRING u"error"_ns
75 #define CLOSE_EVENT_STRING u"close"_ns
77 using namespace mozilla::net
;
79 namespace mozilla::dom
{
83 // This class is responsible for proxying nsIObserver and nsIWebSocketImpl
84 // interfaces to WebSocketImpl. WebSocketImplProxy should be only accessed on
85 // main thread, so we can let it support weak reference.
86 class WebSocketImplProxy final
: public nsIObserver
,
87 public nsSupportsWeakReference
,
88 public nsIWebSocketImpl
{
92 NS_DECL_NSIWEBSOCKETIMPL
94 explicit WebSocketImplProxy(WebSocketImpl
* aOwner
) : mOwner(aOwner
) {
95 MOZ_ASSERT(NS_IsMainThread());
99 MOZ_ASSERT(NS_IsMainThread());
105 ~WebSocketImplProxy() = default;
107 RefPtr
<WebSocketImpl
> mOwner
;
110 class WebSocketImpl final
: public nsIInterfaceRequestor
,
111 public nsIWebSocketListener
,
114 public nsISerialEventTarget
,
115 public nsIWebSocketImpl
{
117 NS_DECL_NSIINTERFACEREQUESTOR
118 NS_DECL_NSIWEBSOCKETLISTENER
121 NS_DECL_THREADSAFE_ISUPPORTS
122 NS_DECL_NSIEVENTTARGET_FULL
123 NS_DECL_NSIWEBSOCKETIMPL
125 explicit WebSocketImpl(WebSocket
* aWebSocket
)
126 : mWebSocket(aWebSocket
),
127 mIsServerSide(false),
129 mOnCloseScheduled(false),
131 mDisconnectingOrDisconnected(false),
132 mCloseEventWasClean(false),
133 mCloseEventCode(nsIWebSocketChannel::CLOSE_ABNORMAL
),
138 mPrivateBrowsing(false),
139 mIsChromeContext(false),
141 mMutex("WebSocketImpl::mMutex"),
142 mWorkerShuttingDown(false) {
143 if (!NS_IsMainThread()) {
144 mIsMainThread
= false;
148 void AssertIsOnTargetThread() const { MOZ_ASSERT(IsTargetThread()); }
150 bool IsTargetThread() const;
152 nsresult
Init(JSContext
* aCx
, bool aIsSecure
, nsIPrincipal
* aPrincipal
,
153 const Maybe
<ClientInfo
>& aClientInfo
,
154 nsICSPEventListener
* aCSPEventListener
, bool aIsServerSide
,
155 const nsAString
& aURL
, nsTArray
<nsString
>& aProtocolArray
,
156 const nsACString
& aScriptFile
, uint32_t aScriptLine
,
157 uint32_t aScriptColumn
);
159 nsresult
AsyncOpen(nsIPrincipal
* aPrincipal
, uint64_t aInnerWindowID
,
160 nsITransportProvider
* aTransportProvider
,
161 const nsACString
& aNegotiatedExtensions
,
162 UniquePtr
<SerializedStackHolder
> aOriginStack
);
164 nsresult
ParseURL(const nsAString
& aURL
);
165 nsresult
InitializeConnection(nsIPrincipal
* aPrincipal
,
166 nsICookieJarSettings
* aCookieJarSettings
);
168 // These methods when called can release the WebSocket object
169 void FailConnection(uint16_t reasonCode
,
170 const nsACString
& aReasonString
= ""_ns
);
171 nsresult
CloseConnection(uint16_t reasonCode
,
172 const nsACString
& aReasonString
= ""_ns
);
174 void DisconnectInternal();
176 nsresult
ConsoleError();
177 void PrintErrorOnConsole(const char* aBundleURI
, const char* aError
,
178 nsTArray
<nsString
>&& aFormatStrings
);
180 nsresult
DoOnMessageAvailable(const nsACString
& aMsg
, bool isBinary
) const;
182 // ConnectionCloseEvents: 'error' event if needed, then 'close' event.
183 nsresult
ScheduleConnectionCloseEvents(nsISupports
* aContext
,
184 nsresult aStatusCode
);
185 // 2nd half of ScheduleConnectionCloseEvents, run in its own event.
186 void DispatchConnectionCloseEvents();
188 nsresult
UpdateURI();
191 void ReleaseObject();
193 bool RegisterWorkerRef(WorkerPrivate
* aWorkerPrivate
);
194 void UnregisterWorkerRef();
196 nsresult
CancelInternal();
198 nsresult
IsSecure(bool* aValue
);
200 RefPtr
<WebSocket
> mWebSocket
;
202 nsCOMPtr
<nsIWebSocketChannel
> mChannel
;
204 bool mIsServerSide
; // True if we're implementing the server side of a
205 // websocket connection
207 bool mSecure
; // if true it is using SSL and the wss scheme,
208 // otherwise it is using the ws scheme with no SSL
210 bool mOnCloseScheduled
;
212 Atomic
<bool> mDisconnectingOrDisconnected
;
214 // Set attributes of DOM 'onclose' message
215 bool mCloseEventWasClean
;
216 nsString mCloseEventReason
;
217 uint16_t mCloseEventCode
;
219 nsCString mAsciiHost
; // hostname
221 nsCString mResource
; // [filepath[?query]]
222 nsString mUTF16Origin
;
225 nsCString mRequestedProtocolList
;
227 nsWeakPtr mOriginDocument
;
229 // Web Socket owner information:
230 // - the script file name, UTF8 encoded.
231 // - source code line number and column number where the Web Socket object
233 // - the ID of the Web Socket owner window. Note that this may not
234 // be the same as the inner window where the script lives.
235 // e.g within iframes
236 // These attributes are used for error reporting.
237 nsCString mScriptFile
;
238 uint32_t mScriptLine
;
239 uint32_t mScriptColumn
;
240 uint64_t mInnerWindowID
;
241 bool mPrivateBrowsing
;
242 bool mIsChromeContext
;
244 RefPtr
<ThreadSafeWorkerRef
> mWorkerRef
;
246 nsWeakPtr mWeakLoadGroup
;
250 // This mutex protects mWorkerShuttingDown.
251 mozilla::Mutex mMutex
;
252 bool mWorkerShuttingDown
MOZ_GUARDED_BY(mMutex
);
254 RefPtr
<WebSocketEventService
> mService
;
255 nsCOMPtr
<nsIPrincipal
> mLoadingPrincipal
;
257 // For dispatching runnables to main thread.
258 nsCOMPtr
<nsISerialEventTarget
> mMainThreadEventTarget
;
260 RefPtr
<WebSocketImplProxy
> mImplProxy
;
264 MOZ_RELEASE_ASSERT(NS_IsMainThread() == mIsMainThread
||
265 mDisconnectingOrDisconnected
);
267 // If we threw during Init we never called disconnect
268 if (!mDisconnectingOrDisconnected
) {
274 NS_IMPL_ISUPPORTS(WebSocketImplProxy
, nsIObserver
, nsISupportsWeakReference
,
278 WebSocketImplProxy::Observe(nsISupports
* aSubject
, const char* aTopic
,
279 const char16_t
* aData
) {
284 return mOwner
->Observe(aSubject
, aTopic
, aData
);
288 WebSocketImplProxy::SendMessage(const nsAString
& aMessage
) {
293 return mOwner
->SendMessage(aMessage
);
296 NS_IMPL_ISUPPORTS(WebSocketImpl
, nsIInterfaceRequestor
, nsIWebSocketListener
,
297 nsIObserver
, nsIRequest
, nsIEventTarget
, nsISerialEventTarget
,
300 class CallDispatchConnectionCloseEvents final
: public DiscardableRunnable
{
302 explicit CallDispatchConnectionCloseEvents(WebSocketImpl
* aWebSocketImpl
)
303 : DiscardableRunnable("dom::CallDispatchConnectionCloseEvents"),
304 mWebSocketImpl(aWebSocketImpl
) {
305 aWebSocketImpl
->AssertIsOnTargetThread();
308 NS_IMETHOD
Run() override
{
309 mWebSocketImpl
->AssertIsOnTargetThread();
310 mWebSocketImpl
->DispatchConnectionCloseEvents();
315 RefPtr
<WebSocketImpl
> mWebSocketImpl
;
318 //-----------------------------------------------------------------------------
320 //-----------------------------------------------------------------------------
324 class PrintErrorOnConsoleRunnable final
: public WorkerMainThreadRunnable
{
326 PrintErrorOnConsoleRunnable(WebSocketImpl
* aImpl
, const char* aBundleURI
,
328 nsTArray
<nsString
>&& aFormatStrings
)
329 : WorkerMainThreadRunnable(aImpl
->mWorkerRef
->Private(),
330 "WebSocket :: print error on console"_ns
),
332 mBundleURI(aBundleURI
),
334 mFormatStrings(std::move(aFormatStrings
)) {}
336 bool MainThreadRun() override
{
337 mImpl
->PrintErrorOnConsole(mBundleURI
, mError
, std::move(mFormatStrings
));
342 // Raw pointer because this runnable is sync.
343 WebSocketImpl
* mImpl
;
345 const char* mBundleURI
;
347 nsTArray
<nsString
> mFormatStrings
;
352 void WebSocketImpl::PrintErrorOnConsole(const char* aBundleURI
,
354 nsTArray
<nsString
>&& aFormatStrings
) {
355 // This method must run on the main thread.
357 if (!NS_IsMainThread()) {
358 MOZ_ASSERT(mWorkerRef
);
360 RefPtr
<PrintErrorOnConsoleRunnable
> runnable
=
361 new PrintErrorOnConsoleRunnable(this, aBundleURI
, aError
,
362 std::move(aFormatStrings
));
364 runnable
->Dispatch(Killing
, rv
);
365 // XXXbz this seems totally broken. We should be propagating this out, but
366 // none of our callers really propagate anything usefully. Come to think of
367 // it, why is this a syncrunnable anyway? Can't this be a fire-and-forget
369 rv
.SuppressException();
374 nsCOMPtr
<nsIStringBundleService
> bundleService
=
375 do_GetService(NS_STRINGBUNDLE_CONTRACTID
, &rv
);
376 NS_ENSURE_SUCCESS_VOID(rv
);
378 nsCOMPtr
<nsIStringBundle
> strBundle
;
379 rv
= bundleService
->CreateBundle(aBundleURI
, getter_AddRefs(strBundle
));
380 NS_ENSURE_SUCCESS_VOID(rv
);
382 nsCOMPtr
<nsIConsoleService
> console(
383 do_GetService(NS_CONSOLESERVICE_CONTRACTID
, &rv
));
384 NS_ENSURE_SUCCESS_VOID(rv
);
386 nsCOMPtr
<nsIScriptError
> errorObject(
387 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID
, &rv
));
388 NS_ENSURE_SUCCESS_VOID(rv
);
390 // Localize the error message
391 nsAutoString message
;
392 if (!aFormatStrings
.IsEmpty()) {
393 rv
= strBundle
->FormatStringFromName(aError
, aFormatStrings
, message
);
395 rv
= strBundle
->GetStringFromName(aError
, message
);
397 NS_ENSURE_SUCCESS_VOID(rv
);
399 if (mInnerWindowID
) {
400 rv
= errorObject
->InitWithWindowID(
401 message
, NS_ConvertUTF8toUTF16(mScriptFile
), u
""_ns
, mScriptLine
,
402 mScriptColumn
, nsIScriptError::errorFlag
, "Web Socket"_ns
,
406 errorObject
->Init(message
, NS_ConvertUTF8toUTF16(mScriptFile
), u
""_ns
,
407 mScriptLine
, mScriptColumn
, nsIScriptError::errorFlag
,
408 "Web Socket"_ns
, mPrivateBrowsing
, mIsChromeContext
);
411 NS_ENSURE_SUCCESS_VOID(rv
);
413 // print the error message directly to the JS console
414 rv
= console
->LogMessage(errorObject
);
415 NS_ENSURE_SUCCESS_VOID(rv
);
420 class CancelWebSocketRunnable final
: public Runnable
{
422 CancelWebSocketRunnable(nsIWebSocketChannel
* aChannel
, uint16_t aReasonCode
,
423 const nsACString
& aReasonString
)
424 : Runnable("dom::CancelWebSocketRunnable"),
426 mReasonCode(aReasonCode
),
427 mReasonString(aReasonString
) {}
429 NS_IMETHOD
Run() override
{
430 nsresult rv
= mChannel
->Close(mReasonCode
, mReasonString
);
432 NS_WARNING("Failed to dispatch the close message");
438 nsCOMPtr
<nsIWebSocketChannel
> mChannel
;
439 uint16_t mReasonCode
;
440 nsCString mReasonString
;
443 class MOZ_STACK_CLASS MaybeDisconnect
{
445 explicit MaybeDisconnect(WebSocketImpl
* aImpl
) : mImpl(aImpl
) {}
448 bool toDisconnect
= false;
451 MutexAutoLock
lock(mImpl
->mMutex
);
452 toDisconnect
= mImpl
->mWorkerShuttingDown
;
461 WebSocketImpl
* mImpl
;
464 class CloseConnectionRunnable final
: public Runnable
{
466 CloseConnectionRunnable(WebSocketImpl
* aImpl
, uint16_t aReasonCode
,
467 const nsACString
& aReasonString
)
468 : Runnable("dom::CloseConnectionRunnable"),
470 mReasonCode(aReasonCode
),
471 mReasonString(aReasonString
) {}
473 NS_IMETHOD
Run() override
{
474 return mImpl
->CloseConnection(mReasonCode
, mReasonString
);
478 RefPtr
<WebSocketImpl
> mImpl
;
479 uint16_t mReasonCode
;
480 const nsCString mReasonString
;
485 nsresult
WebSocketImpl::CloseConnection(uint16_t aReasonCode
,
486 const nsACString
& aReasonString
) {
487 if (!IsTargetThread()) {
488 nsCOMPtr
<nsIRunnable
> runnable
=
489 new CloseConnectionRunnable(this, aReasonCode
, aReasonString
);
490 return Dispatch(runnable
.forget(), NS_DISPATCH_NORMAL
);
493 AssertIsOnTargetThread();
495 if (mDisconnectingOrDisconnected
) {
499 // If this method is called because the worker is going away, we will not
500 // receive the OnStop() method and we have to disconnect the WebSocket and
501 // release the ThreadSafeWorkerRef.
502 MaybeDisconnect
md(this);
504 uint16_t readyState
= mWebSocket
->ReadyState();
505 if (readyState
== WebSocket::CLOSING
|| readyState
== WebSocket::CLOSED
) {
509 // The common case...
511 mWebSocket
->SetReadyState(WebSocket::CLOSING
);
513 // The channel has to be closed on the main-thread.
515 if (NS_IsMainThread()) {
516 return mChannel
->Close(aReasonCode
, aReasonString
);
519 RefPtr
<CancelWebSocketRunnable
> runnable
=
520 new CancelWebSocketRunnable(mChannel
, aReasonCode
, aReasonString
);
521 return NS_DispatchToMainThread(runnable
);
524 // No channel, but not disconnected: canceled or failed early
525 MOZ_ASSERT(readyState
== WebSocket::CONNECTING
,
526 "Should only get here for early websocket cancel/error");
528 // Server won't be sending us a close code, so use what's passed in here.
529 mCloseEventCode
= aReasonCode
;
530 CopyUTF8toUTF16(aReasonString
, mCloseEventReason
);
532 mWebSocket
->SetReadyState(WebSocket::CLOSING
);
534 ScheduleConnectionCloseEvents(
535 nullptr, (aReasonCode
== nsIWebSocketChannel::CLOSE_NORMAL
||
536 aReasonCode
== nsIWebSocketChannel::CLOSE_GOING_AWAY
)
543 nsresult
WebSocketImpl::ConsoleError() {
544 AssertIsOnTargetThread();
547 MutexAutoLock
lock(mMutex
);
548 if (mWorkerShuttingDown
) {
549 // Too late to report anything, bail out.
554 nsTArray
<nsString
> formatStrings
;
555 CopyUTF8toUTF16(mURI
, *formatStrings
.AppendElement());
557 if (mWebSocket
->ReadyState() < WebSocket::OPEN
) {
558 PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
559 "connectionFailure", std::move(formatStrings
));
561 PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
562 "netInterrupt", std::move(formatStrings
));
564 /// todo some specific errors - like for message too large
568 void WebSocketImpl::FailConnection(uint16_t aReasonCode
,
569 const nsACString
& aReasonString
) {
570 AssertIsOnTargetThread();
572 if (mDisconnectingOrDisconnected
) {
578 CloseConnection(aReasonCode
, aReasonString
);
580 if (NS_IsMainThread() && mImplProxy
) {
581 mImplProxy
->Disconnect();
582 mImplProxy
= nullptr;
588 class DisconnectInternalRunnable final
: public WorkerMainThreadRunnable
{
590 explicit DisconnectInternalRunnable(WebSocketImpl
* aImpl
)
591 : WorkerMainThreadRunnable(GetCurrentThreadWorkerPrivate(),
592 "WebSocket :: disconnect"_ns
),
595 bool MainThreadRun() override
{
596 mImpl
->DisconnectInternal();
601 // A raw pointer because this runnable is sync.
602 WebSocketImpl
* mImpl
;
607 void WebSocketImpl::Disconnect() {
608 MOZ_RELEASE_ASSERT(NS_IsMainThread() == mIsMainThread
);
610 if (mDisconnectingOrDisconnected
) {
614 // DontKeepAliveAnyMore() and DisconnectInternal() can release the object. So
615 // hold a reference to this until the end of the method.
616 RefPtr
<WebSocketImpl
> kungfuDeathGrip
= this;
618 // Disconnect can be called from some control event (such as a callback from
619 // StrongWorkerRef). This will be schedulated before any other sync/async
620 // runnable. In order to prevent some double Disconnect() calls, we use this
622 mDisconnectingOrDisconnected
= true;
624 // DisconnectInternal touches observers and nsILoadGroup and it must run on
627 if (NS_IsMainThread()) {
628 DisconnectInternal();
630 // If we haven't called WebSocket::DisconnectFromOwner yet, update
631 // web socket count here.
632 if (mWebSocket
->GetOwner()) {
633 mWebSocket
->GetOwner()->UpdateWebSocketCount(-1);
636 RefPtr
<DisconnectInternalRunnable
> runnable
=
637 new DisconnectInternalRunnable(this);
639 runnable
->Dispatch(Killing
, rv
);
640 // XXXbz this seems totally broken. We should be propagating this out, but
641 // where to, exactly?
642 rv
.SuppressException();
645 NS_ReleaseOnMainThread("WebSocketImpl::mChannel", mChannel
.forget());
646 NS_ReleaseOnMainThread("WebSocketImpl::mService", mService
.forget());
648 mWebSocket
->DontKeepAliveAnyMore();
649 mWebSocket
->mImpl
= nullptr;
652 UnregisterWorkerRef();
655 // We want to release the WebSocket in the correct thread.
656 mWebSocket
= nullptr;
659 void WebSocketImpl::DisconnectInternal() {
660 AssertIsOnMainThread();
662 nsCOMPtr
<nsILoadGroup
> loadGroup
= do_QueryReferent(mWeakLoadGroup
);
664 loadGroup
->RemoveRequest(this, nullptr, NS_OK
);
665 // mWeakLoadGroup has to be release on main-thread because WeakReferences
666 // are not thread-safe.
667 mWeakLoadGroup
= nullptr;
671 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
673 os
->RemoveObserver(mImplProxy
, DOM_WINDOW_DESTROYED_TOPIC
);
674 os
->RemoveObserver(mImplProxy
, DOM_WINDOW_FROZEN_TOPIC
);
679 mImplProxy
->Disconnect();
680 mImplProxy
= nullptr;
684 //-----------------------------------------------------------------------------
685 // WebSocketImpl::nsIWebSocketImpl
686 //-----------------------------------------------------------------------------
689 WebSocketImpl::SendMessage(const nsAString
& aMessage
) {
690 nsString
message(aMessage
);
691 nsCOMPtr
<nsIRunnable
> runnable
= NS_NewRunnableFunction(
692 "WebSocketImpl::SendMessage",
693 [self
= RefPtr
<WebSocketImpl
>(this), message
= std::move(message
)]() {
694 ErrorResult IgnoredErrorResult
;
695 self
->mWebSocket
->Send(message
, IgnoredErrorResult
);
697 return Dispatch(runnable
.forget(), NS_DISPATCH_NORMAL
);
700 //-----------------------------------------------------------------------------
701 // WebSocketImpl::nsIWebSocketListener methods:
702 //-----------------------------------------------------------------------------
704 nsresult
WebSocketImpl::DoOnMessageAvailable(const nsACString
& aMsg
,
705 bool isBinary
) const {
706 AssertIsOnTargetThread();
708 if (mDisconnectingOrDisconnected
) {
712 int16_t readyState
= mWebSocket
->ReadyState();
713 if (readyState
== WebSocket::CLOSED
) {
714 NS_ERROR("Received message after CLOSED");
715 return NS_ERROR_UNEXPECTED
;
718 if (readyState
== WebSocket::OPEN
) {
719 // Dispatch New Message
720 nsresult rv
= mWebSocket
->CreateAndDispatchMessageEvent(aMsg
, isBinary
);
722 NS_WARNING("Failed to dispatch the message event");
728 // CLOSING should be the only other state where it's possible to get msgs
729 // from channel: Spec says to drop them.
730 MOZ_ASSERT(readyState
== WebSocket::CLOSING
,
731 "Received message while CONNECTING or CLOSED");
736 WebSocketImpl::OnMessageAvailable(nsISupports
* aContext
,
737 const nsACString
& aMsg
) {
738 AssertIsOnTargetThread();
740 if (mDisconnectingOrDisconnected
) {
744 return DoOnMessageAvailable(aMsg
, false);
748 WebSocketImpl::OnBinaryMessageAvailable(nsISupports
* aContext
,
749 const nsACString
& aMsg
) {
750 AssertIsOnTargetThread();
752 if (mDisconnectingOrDisconnected
) {
756 return DoOnMessageAvailable(aMsg
, true);
760 WebSocketImpl::OnStart(nsISupports
* aContext
) {
761 AssertIsOnTargetThread();
763 if (mDisconnectingOrDisconnected
) {
767 int16_t readyState
= mWebSocket
->ReadyState();
769 // This is the only function that sets OPEN, and should be called only once
770 MOZ_ASSERT(readyState
!= WebSocket::OPEN
,
771 "readyState already OPEN! OnStart called twice?");
773 // Nothing to do if we've already closed/closing
774 if (readyState
!= WebSocket::CONNECTING
) {
778 // Attempt to kill "ghost" websocket: but usually too early for check to fail
779 nsresult rv
= mWebSocket
->CheckCurrentGlobalCorrectness();
781 CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY
);
785 if (!mRequestedProtocolList
.IsEmpty()) {
786 rv
= mChannel
->GetProtocol(mWebSocket
->mEstablishedProtocol
);
787 MOZ_ASSERT(NS_SUCCEEDED(rv
));
790 rv
= mChannel
->GetExtensions(mWebSocket
->mEstablishedExtensions
);
791 MOZ_ASSERT(NS_SUCCEEDED(rv
));
794 mWebSocket
->SetReadyState(WebSocket::OPEN
);
796 mService
->WebSocketOpened(
797 mChannel
->Serial(), mInnerWindowID
, mWebSocket
->mEffectiveURL
,
798 mWebSocket
->mEstablishedProtocol
, mWebSocket
->mEstablishedExtensions
,
799 mChannel
->HttpChannelId());
801 // Let's keep the object alive because the webSocket can be CCed in the
803 RefPtr
<WebSocket
> webSocket
= mWebSocket
;
806 rv
= webSocket
->CreateAndDispatchSimpleEvent(OPEN_EVENT_STRING
);
808 NS_WARNING("Failed to dispatch the open event");
811 webSocket
->UpdateMustKeepAlive();
816 WebSocketImpl::OnStop(nsISupports
* aContext
, nsresult aStatusCode
) {
817 AssertIsOnTargetThread();
819 if (mDisconnectingOrDisconnected
) {
823 // We can be CONNECTING here if connection failed.
824 // We can be OPEN if we have encountered a fatal protocol error
825 // We can be CLOSING if close() was called and/or server initiated close.
826 MOZ_ASSERT(mWebSocket
->ReadyState() != WebSocket::CLOSED
,
827 "Shouldn't already be CLOSED when OnStop called");
829 return ScheduleConnectionCloseEvents(aContext
, aStatusCode
);
832 nsresult
WebSocketImpl::ScheduleConnectionCloseEvents(nsISupports
* aContext
,
833 nsresult aStatusCode
) {
834 AssertIsOnTargetThread();
836 // no-op if some other code has already initiated close event
837 if (!mOnCloseScheduled
) {
838 mCloseEventWasClean
= NS_SUCCEEDED(aStatusCode
);
840 if (aStatusCode
== NS_BASE_STREAM_CLOSED
) {
841 // don't generate an error event just because of an unclean close
845 if (aStatusCode
== NS_ERROR_NET_INADEQUATE_SECURITY
) {
846 // TLS negotiation failed so we need to set status code to 1015.
847 mCloseEventCode
= 1015;
850 if (NS_FAILED(aStatusCode
)) {
855 mOnCloseScheduled
= true;
857 NS_DispatchToCurrentThread(new CallDispatchConnectionCloseEvents(this));
864 WebSocketImpl::OnAcknowledge(nsISupports
* aContext
, uint32_t aSize
) {
865 AssertIsOnTargetThread();
867 if (mDisconnectingOrDisconnected
) {
871 MOZ_RELEASE_ASSERT(mWebSocket
->mOutgoingBufferedAmount
.isValid());
872 if (aSize
> mWebSocket
->mOutgoingBufferedAmount
.value()) {
873 return NS_ERROR_UNEXPECTED
;
876 CheckedUint64 outgoingBufferedAmount
= mWebSocket
->mOutgoingBufferedAmount
;
877 outgoingBufferedAmount
-= aSize
;
878 if (!outgoingBufferedAmount
.isValid()) {
879 return NS_ERROR_UNEXPECTED
;
882 mWebSocket
->mOutgoingBufferedAmount
= outgoingBufferedAmount
;
883 MOZ_RELEASE_ASSERT(mWebSocket
->mOutgoingBufferedAmount
.isValid());
889 WebSocketImpl::OnServerClose(nsISupports
* aContext
, uint16_t aCode
,
890 const nsACString
& aReason
) {
891 AssertIsOnTargetThread();
893 if (mDisconnectingOrDisconnected
) {
897 int16_t readyState
= mWebSocket
->ReadyState();
899 MOZ_ASSERT(readyState
!= WebSocket::CONNECTING
,
900 "Received server close before connected?");
901 MOZ_ASSERT(readyState
!= WebSocket::CLOSED
,
902 "Received server close after already closed!");
904 // store code/string for onclose DOM event
905 mCloseEventCode
= aCode
;
906 CopyUTF8toUTF16(aReason
, mCloseEventReason
);
908 if (readyState
== WebSocket::OPEN
) {
909 // Server initiating close.
910 // RFC 6455, 5.5.1: "When sending a Close frame in response, the endpoint
911 // typically echos the status code it received".
912 // But never send certain codes, per section 7.4.1
913 if (aCode
== 1005 || aCode
== 1006 || aCode
== 1015) {
914 CloseConnection(0, ""_ns
);
916 CloseConnection(aCode
, aReason
);
919 // We initiated close, and server has replied: OnStop does rest of the work.
920 MOZ_ASSERT(readyState
== WebSocket::CLOSING
, "unknown state");
927 WebSocketImpl::OnError() {
928 if (!IsTargetThread()) {
930 NS_NewRunnableFunction("dom::FailConnectionRunnable",
931 [self
= RefPtr
{this}]() {
932 self
->FailConnection(
933 nsIWebSocketChannel::CLOSE_ABNORMAL
);
938 AssertIsOnTargetThread();
939 FailConnection(nsIWebSocketChannel::CLOSE_ABNORMAL
);
943 //-----------------------------------------------------------------------------
944 // WebSocketImpl::nsIInterfaceRequestor
945 //-----------------------------------------------------------------------------
948 WebSocketImpl::GetInterface(const nsIID
& aIID
, void** aResult
) {
949 AssertIsOnMainThread();
951 if (!mWebSocket
|| mWebSocket
->ReadyState() == WebSocket::CLOSED
) {
952 return NS_ERROR_FAILURE
;
955 if (aIID
.Equals(NS_GET_IID(nsIAuthPrompt
)) ||
956 aIID
.Equals(NS_GET_IID(nsIAuthPrompt2
))) {
957 nsCOMPtr
<nsPIDOMWindowInner
> win
= mWebSocket
->GetWindowIfCurrent();
959 return NS_ERROR_NOT_AVAILABLE
;
963 nsCOMPtr
<nsIPromptFactory
> wwatch
=
964 do_GetService(NS_WINDOWWATCHER_CONTRACTID
, &rv
);
965 NS_ENSURE_SUCCESS(rv
, rv
);
967 nsCOMPtr
<nsPIDOMWindowOuter
> outerWindow
= win
->GetOuterWindow();
968 return wwatch
->GetPrompt(outerWindow
, aIID
, aResult
);
971 return QueryInterface(aIID
, aResult
);
974 ////////////////////////////////////////////////////////////////////////////////
976 ////////////////////////////////////////////////////////////////////////////////
978 WebSocket::WebSocket(nsIGlobalObject
* aGlobal
)
979 : DOMEventTargetHelper(aGlobal
),
981 mKeepingAlive(false),
982 mCheckMustKeepAlive(true),
983 mOutgoingBufferedAmount(0),
984 mBinaryType(dom::BinaryType::Blob
),
985 mMutex("WebSocket::mMutex"),
986 mReadyState(CONNECTING
) {
989 mImpl
= new WebSocketImpl(this);
990 mIsMainThread
= mImpl
->mIsMainThread
;
993 WebSocket::~WebSocket() = default;
995 mozilla::Maybe
<EventCallbackDebuggerNotificationType
>
996 WebSocket::GetDebuggerNotificationType() const {
997 return mozilla::Some(EventCallbackDebuggerNotificationType::Websocket
);
1000 JSObject
* WebSocket::WrapObject(JSContext
* cx
,
1001 JS::Handle
<JSObject
*> aGivenProto
) {
1002 return WebSocket_Binding::Wrap(cx
, this, aGivenProto
);
1005 //---------------------------------------------------------------------------
1007 //---------------------------------------------------------------------------
1010 already_AddRefed
<WebSocket
> WebSocket::Constructor(
1011 const GlobalObject
& aGlobal
, const nsAString
& aUrl
,
1012 const StringOrStringSequence
& aProtocols
, ErrorResult
& aRv
) {
1013 if (aProtocols
.IsStringSequence()) {
1014 return WebSocket::ConstructorCommon(
1015 aGlobal
, aUrl
, aProtocols
.GetAsStringSequence(), nullptr, ""_ns
, aRv
);
1018 Sequence
<nsString
> protocols
;
1019 if (!protocols
.AppendElement(aProtocols
.GetAsString(), fallible
)) {
1020 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
1024 return WebSocket::ConstructorCommon(aGlobal
, aUrl
, protocols
, nullptr, ""_ns
,
1028 already_AddRefed
<WebSocket
> WebSocket::CreateServerWebSocket(
1029 const GlobalObject
& aGlobal
, const nsAString
& aUrl
,
1030 const Sequence
<nsString
>& aProtocols
,
1031 nsITransportProvider
* aTransportProvider
,
1032 const nsAString
& aNegotiatedExtensions
, ErrorResult
& aRv
) {
1033 return WebSocket::ConstructorCommon(
1034 aGlobal
, aUrl
, aProtocols
, aTransportProvider
,
1035 NS_ConvertUTF16toUTF8(aNegotiatedExtensions
), aRv
);
1040 // This class is used to clear any exception.
1041 class MOZ_STACK_CLASS ClearException
{
1043 explicit ClearException(JSContext
* aCx
) : mCx(aCx
) {}
1045 ~ClearException() { JS_ClearPendingException(mCx
); }
1051 class WebSocketMainThreadRunnable
: public WorkerMainThreadRunnable
{
1053 WebSocketMainThreadRunnable(WorkerPrivate
* aWorkerPrivate
,
1054 const nsACString
& aTelemetryKey
)
1055 : WorkerMainThreadRunnable(aWorkerPrivate
, aTelemetryKey
) {
1056 MOZ_ASSERT(aWorkerPrivate
);
1057 aWorkerPrivate
->AssertIsOnWorkerThread();
1060 bool MainThreadRun() override
{
1061 AssertIsOnMainThread();
1063 // Walk up to our containing page
1064 WorkerPrivate
* wp
= mWorkerPrivate
;
1065 while (wp
->GetParent()) {
1066 wp
= wp
->GetParent();
1069 nsPIDOMWindowInner
* window
= wp
->GetWindow();
1071 return InitWithWindow(window
);
1074 return InitWindowless(wp
);
1078 virtual bool InitWithWindow(nsPIDOMWindowInner
* aWindow
) = 0;
1080 virtual bool InitWindowless(WorkerPrivate
* aTopLevelWorkerPrivate
) = 0;
1083 class InitRunnable final
: public WebSocketMainThreadRunnable
{
1085 InitRunnable(WorkerPrivate
* aWorkerPrivate
, WebSocketImpl
* aImpl
,
1086 const Maybe
<mozilla::dom::ClientInfo
>& aClientInfo
,
1087 bool aIsServerSide
, const nsAString
& aURL
,
1088 nsTArray
<nsString
>& aProtocolArray
,
1089 const nsACString
& aScriptFile
, uint32_t aScriptLine
,
1090 uint32_t aScriptColumn
)
1091 : WebSocketMainThreadRunnable(aWorkerPrivate
, "WebSocket :: init"_ns
),
1093 mClientInfo(aClientInfo
),
1094 mIsServerSide(aIsServerSide
),
1096 mProtocolArray(aProtocolArray
),
1097 mScriptFile(aScriptFile
),
1098 mScriptLine(aScriptLine
),
1099 mScriptColumn(aScriptColumn
),
1101 MOZ_ASSERT(mWorkerPrivate
);
1102 mWorkerPrivate
->AssertIsOnWorkerThread();
1105 nsresult
ErrorCode() const { return mErrorCode
; }
1108 virtual bool InitWithWindow(nsPIDOMWindowInner
* aWindow
) override
{
1110 if (NS_WARN_IF(!jsapi
.Init(aWindow
))) {
1111 mErrorCode
= NS_ERROR_FAILURE
;
1115 ClearException
ce(jsapi
.cx());
1117 Document
* doc
= aWindow
->GetExtantDoc();
1119 mErrorCode
= NS_ERROR_FAILURE
;
1123 mErrorCode
= mImpl
->Init(
1124 jsapi
.cx(), mWorkerPrivate
->GetPrincipal()->SchemeIs("https"),
1125 doc
->NodePrincipal(), mClientInfo
, mWorkerPrivate
->CSPEventListener(),
1126 mIsServerSide
, mURL
, mProtocolArray
, mScriptFile
, mScriptLine
,
1131 virtual bool InitWindowless(WorkerPrivate
* aTopLevelWorkerPrivate
) override
{
1132 MOZ_ASSERT(NS_IsMainThread());
1133 MOZ_ASSERT(aTopLevelWorkerPrivate
&& !aTopLevelWorkerPrivate
->GetWindow());
1136 mImpl
->Init(nullptr, mWorkerPrivate
->GetPrincipal()->SchemeIs("https"),
1137 aTopLevelWorkerPrivate
->GetPrincipal(), mClientInfo
,
1138 mWorkerPrivate
->CSPEventListener(), mIsServerSide
, mURL
,
1139 mProtocolArray
, mScriptFile
, mScriptLine
, mScriptColumn
);
1143 // Raw pointer. This worker runnable runs synchronously.
1144 WebSocketImpl
* mImpl
;
1146 Maybe
<ClientInfo
> mClientInfo
;
1148 const nsAString
& mURL
;
1149 nsTArray
<nsString
>& mProtocolArray
;
1150 nsCString mScriptFile
;
1151 uint32_t mScriptLine
;
1152 uint32_t mScriptColumn
;
1153 nsresult mErrorCode
;
1156 class ConnectRunnable final
: public WebSocketMainThreadRunnable
{
1158 ConnectRunnable(WorkerPrivate
* aWorkerPrivate
, WebSocketImpl
* aImpl
)
1159 : WebSocketMainThreadRunnable(aWorkerPrivate
, "WebSocket :: init"_ns
),
1161 mConnectionFailed(true) {
1162 MOZ_ASSERT(mWorkerPrivate
);
1163 mWorkerPrivate
->AssertIsOnWorkerThread();
1166 bool ConnectionFailed() const { return mConnectionFailed
; }
1169 virtual bool InitWithWindow(nsPIDOMWindowInner
* aWindow
) override
{
1170 Document
* doc
= aWindow
->GetExtantDoc();
1175 mConnectionFailed
= NS_FAILED(mImpl
->InitializeConnection(
1176 doc
->NodePrincipal(), mWorkerPrivate
->CookieJarSettings()));
1180 virtual bool InitWindowless(WorkerPrivate
* aTopLevelWorkerPrivate
) override
{
1181 MOZ_ASSERT(NS_IsMainThread());
1182 MOZ_ASSERT(aTopLevelWorkerPrivate
&& !aTopLevelWorkerPrivate
->GetWindow());
1184 mConnectionFailed
= NS_FAILED(
1185 mImpl
->InitializeConnection(aTopLevelWorkerPrivate
->GetPrincipal(),
1186 mWorkerPrivate
->CookieJarSettings()));
1190 // Raw pointer. This worker runnable runs synchronously.
1191 WebSocketImpl
* mImpl
;
1193 bool mConnectionFailed
;
1196 class AsyncOpenRunnable final
: public WebSocketMainThreadRunnable
{
1198 explicit AsyncOpenRunnable(WebSocketImpl
* aImpl
,
1199 UniquePtr
<SerializedStackHolder
> aOriginStack
)
1200 : WebSocketMainThreadRunnable(aImpl
->mWorkerRef
->Private(),
1201 "WebSocket :: AsyncOpen"_ns
),
1203 mOriginStack(std::move(aOriginStack
)),
1205 MOZ_ASSERT(mWorkerPrivate
);
1206 mWorkerPrivate
->AssertIsOnWorkerThread();
1209 nsresult
ErrorCode() const { return mErrorCode
; }
1212 virtual bool InitWithWindow(nsPIDOMWindowInner
* aWindow
) override
{
1213 AssertIsOnMainThread();
1214 MOZ_ASSERT(aWindow
);
1216 Document
* doc
= aWindow
->GetExtantDoc();
1218 mErrorCode
= NS_ERROR_FAILURE
;
1222 nsCOMPtr
<nsIPrincipal
> principal
= doc
->PartitionedPrincipal();
1224 mErrorCode
= NS_ERROR_FAILURE
;
1228 uint64_t windowID
= 0;
1229 if (WindowContext
* wc
= aWindow
->GetWindowContext()) {
1230 windowID
= wc
->InnerWindowId();
1233 mErrorCode
= mImpl
->AsyncOpen(principal
, windowID
, nullptr, ""_ns
,
1234 std::move(mOriginStack
));
1238 virtual bool InitWindowless(WorkerPrivate
* aTopLevelWorkerPrivate
) override
{
1239 MOZ_ASSERT(NS_IsMainThread());
1240 MOZ_ASSERT(aTopLevelWorkerPrivate
&& !aTopLevelWorkerPrivate
->GetWindow());
1243 mImpl
->AsyncOpen(aTopLevelWorkerPrivate
->GetPartitionedPrincipal(), 0,
1244 nullptr, ""_ns
, nullptr);
1249 // Raw pointer. This worker runs synchronously.
1250 WebSocketImpl
* mImpl
;
1252 UniquePtr
<SerializedStackHolder
> mOriginStack
;
1254 nsresult mErrorCode
;
1259 // Check a protocol entry contains only valid characters
1260 bool WebSocket::IsValidProtocolString(const nsString
& aValue
) {
1261 // RFC 6455 (4.1): "not including separator characters as defined in RFC 2616"
1262 const char16_t illegalCharacters
[] = {0x28, 0x29, 0x3C, 0x3E, 0x40, 0x2C,
1263 0x3B, 0x3A, 0x5C, 0x22, 0x2F, 0x5B,
1264 0x5D, 0x3F, 0x3D, 0x7B, 0x7D};
1266 // Cannot be empty string
1267 if (aValue
.IsEmpty()) {
1271 const auto* start
= aValue
.BeginReading();
1272 const auto* end
= aValue
.EndReading();
1274 auto charFilter
= [&](char16_t c
) {
1275 // RFC 6455 (4.1 P18): "in the range U+0021 to U+007E"
1276 if (c
< 0x21 || c
> 0x7E) {
1280 return std::find(std::begin(illegalCharacters
), std::end(illegalCharacters
),
1281 c
) != std::end(illegalCharacters
);
1284 return std::find_if(start
, end
, charFilter
) == end
;
1287 already_AddRefed
<WebSocket
> WebSocket::ConstructorCommon(
1288 const GlobalObject
& aGlobal
, const nsAString
& aUrl
,
1289 const Sequence
<nsString
>& aProtocols
,
1290 nsITransportProvider
* aTransportProvider
,
1291 const nsACString
& aNegotiatedExtensions
, ErrorResult
& aRv
) {
1292 MOZ_ASSERT_IF(!aTransportProvider
, aNegotiatedExtensions
.IsEmpty());
1293 nsCOMPtr
<nsIPrincipal
> principal
;
1294 nsCOMPtr
<nsIPrincipal
> partitionedPrincipal
;
1296 nsCOMPtr
<nsIGlobalObject
> global
= do_QueryInterface(aGlobal
.GetAsSupports());
1297 if (NS_WARN_IF(!global
)) {
1298 aRv
.Throw(NS_ERROR_FAILURE
);
1302 if (NS_IsMainThread()) {
1303 nsCOMPtr
<nsIScriptObjectPrincipal
> scriptPrincipal
=
1304 do_QueryInterface(aGlobal
.GetAsSupports());
1305 if (!scriptPrincipal
) {
1306 aRv
.Throw(NS_ERROR_FAILURE
);
1310 principal
= scriptPrincipal
->GetPrincipal();
1311 partitionedPrincipal
= scriptPrincipal
->PartitionedPrincipal();
1312 if (!principal
|| !partitionedPrincipal
) {
1313 aRv
.Throw(NS_ERROR_FAILURE
);
1318 nsTArray
<nsString
> protocolArray
;
1320 for (uint32_t index
= 0, len
= aProtocols
.Length(); index
< len
; ++index
) {
1321 const nsString
& protocolElement
= aProtocols
[index
];
1323 // Repeated protocols are not allowed
1324 if (protocolArray
.Contains(protocolElement
)) {
1325 aRv
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
1329 // Protocol string value must match constraints
1330 if (!IsValidProtocolString(protocolElement
)) {
1331 aRv
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
1335 protocolArray
.AppendElement(protocolElement
);
1338 RefPtr
<WebSocket
> webSocket
= new WebSocket(global
);
1339 RefPtr
<WebSocketImpl
> webSocketImpl
= webSocket
->mImpl
;
1341 bool connectionFailed
= true;
1343 if (NS_IsMainThread()) {
1344 // We're keeping track of all main thread web sockets to be able to
1345 // avoid throttling timeouts when we have active web sockets.
1346 if (webSocket
->GetOwner()) {
1347 webSocket
->GetOwner()->UpdateWebSocketCount(1);
1350 bool isSecure
= principal
->SchemeIs("https");
1351 aRv
= webSocketImpl
->IsSecure(&isSecure
);
1352 if (NS_WARN_IF(aRv
.Failed())) {
1356 aRv
= webSocketImpl
->Init(aGlobal
.Context(), isSecure
, principal
, Nothing(),
1357 nullptr, !!aTransportProvider
, aUrl
,
1358 protocolArray
, ""_ns
, 0, 0);
1360 if (NS_WARN_IF(aRv
.Failed())) {
1364 nsCOMPtr
<Document
> doc
= webSocket
->GetDocumentIfCurrent();
1366 // the constructor should throw a SYNTAX_ERROR only if it fails to parse the
1367 // url parameter, so don't throw if InitializeConnection fails, and call
1368 // onerror/onclose asynchronously
1369 connectionFailed
= NS_FAILED(webSocketImpl
->InitializeConnection(
1370 principal
, doc
? doc
->CookieJarSettings() : nullptr));
1372 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
1373 MOZ_ASSERT(workerPrivate
);
1376 JS::ColumnNumberZeroOrigin column
;
1377 JS::AutoFilename file
;
1378 if (!JS::DescribeScriptedCaller(aGlobal
.Context(), &file
, &lineno
,
1380 NS_WARNING("Failed to get line number and filename in workers.");
1383 RefPtr
<InitRunnable
> runnable
= new InitRunnable(
1384 workerPrivate
, webSocketImpl
,
1385 workerPrivate
->GlobalScope()->GetClientInfo(), !!aTransportProvider
,
1386 aUrl
, protocolArray
, nsDependentCString(file
.get()), lineno
,
1387 column
.zeroOriginValue());
1388 runnable
->Dispatch(Canceling
, aRv
);
1389 if (NS_WARN_IF(aRv
.Failed())) {
1393 aRv
= runnable
->ErrorCode();
1394 if (NS_WARN_IF(aRv
.Failed())) {
1398 if (NS_WARN_IF(!webSocketImpl
->RegisterWorkerRef(workerPrivate
))) {
1399 // The worker is shutting down.
1400 aRv
.Throw(NS_ERROR_FAILURE
);
1404 RefPtr
<ConnectRunnable
> connectRunnable
=
1405 new ConnectRunnable(workerPrivate
, webSocketImpl
);
1406 connectRunnable
->Dispatch(Canceling
, aRv
);
1407 if (NS_WARN_IF(aRv
.Failed())) {
1411 connectionFailed
= connectRunnable
->ConnectionFailed();
1414 // It can be that we have been already disconnected because the WebSocket is
1415 // gone away while we where initializing the webSocket.
1416 if (!webSocket
->mImpl
) {
1417 aRv
.Throw(NS_ERROR_FAILURE
);
1421 // We don't return an error if the connection just failed. Instead we dispatch
1423 if (connectionFailed
) {
1424 webSocket
->mImpl
->FailConnection(nsIWebSocketChannel::CLOSE_ABNORMAL
);
1427 // If we don't have a channel, the connection is failed and onerror() will be
1428 // called asynchrounsly.
1429 if (!webSocket
->mImpl
->mChannel
) {
1430 return webSocket
.forget();
1433 class MOZ_STACK_CLASS ClearWebSocket
{
1435 explicit ClearWebSocket(WebSocketImpl
* aWebSocketImpl
)
1436 : mWebSocketImpl(aWebSocketImpl
), mDone(false) {}
1438 void Done() { mDone
= true; }
1442 mWebSocketImpl
->mChannel
= nullptr;
1443 mWebSocketImpl
->FailConnection(nsIWebSocketChannel::CLOSE_ABNORMAL
);
1447 WebSocketImpl
* mWebSocketImpl
;
1451 ClearWebSocket
cws(webSocket
->mImpl
);
1453 // This operation must be done on the correct thread. The rest must run on the
1455 aRv
= webSocket
->mImpl
->mChannel
->SetNotificationCallbacks(webSocket
->mImpl
);
1456 if (NS_WARN_IF(aRv
.Failed())) {
1460 if (NS_IsMainThread()) {
1461 MOZ_ASSERT(principal
);
1462 MOZ_ASSERT(partitionedPrincipal
);
1464 nsCOMPtr
<nsPIDOMWindowInner
> ownerWindow
= do_QueryInterface(global
);
1466 UniquePtr
<SerializedStackHolder
> stack
;
1467 uint64_t windowID
= 0;
1470 BrowsingContext
* browsingContext
= ownerWindow
->GetBrowsingContext();
1471 if (browsingContext
&& browsingContext
->WatchedByDevTools()) {
1472 stack
= GetCurrentStackForNetMonitor(aGlobal
.Context());
1475 if (WindowContext
* wc
= ownerWindow
->GetWindowContext()) {
1476 windowID
= wc
->InnerWindowId();
1480 aRv
= webSocket
->mImpl
->AsyncOpen(partitionedPrincipal
, windowID
,
1481 aTransportProvider
, aNegotiatedExtensions
,
1484 MOZ_ASSERT(!aTransportProvider
&& aNegotiatedExtensions
.IsEmpty(),
1485 "not yet implemented");
1487 UniquePtr
<SerializedStackHolder
> stack
;
1488 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
1489 if (workerPrivate
->IsWatchedByDevTools()) {
1490 stack
= GetCurrentStackForNetMonitor(aGlobal
.Context());
1493 RefPtr
<AsyncOpenRunnable
> runnable
=
1494 new AsyncOpenRunnable(webSocket
->mImpl
, std::move(stack
));
1495 runnable
->Dispatch(Canceling
, aRv
);
1496 if (NS_WARN_IF(aRv
.Failed())) {
1500 aRv
= runnable
->ErrorCode();
1503 if (NS_WARN_IF(aRv
.Failed())) {
1507 // It can be that we have been already disconnected because the WebSocket is
1508 // gone away while we where initializing the webSocket.
1509 if (!webSocket
->mImpl
) {
1510 aRv
.Throw(NS_ERROR_FAILURE
);
1514 // Let's inform devtools about this new active WebSocket.
1515 webSocket
->mImpl
->mService
->WebSocketCreated(
1516 webSocket
->mImpl
->mChannel
->Serial(), webSocket
->mImpl
->mInnerWindowID
,
1517 webSocket
->mURI
, webSocket
->mImpl
->mRequestedProtocolList
);
1520 return webSocket
.forget();
1523 NS_IMPL_CYCLE_COLLECTION_CLASS(WebSocket
)
1525 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WebSocket
,
1526 DOMEventTargetHelper
)
1528 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImpl
->mChannel
)
1530 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1532 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WebSocket
, DOMEventTargetHelper
)
1534 NS_IMPL_CYCLE_COLLECTION_UNLINK(mImpl
->mChannel
)
1535 tmp
->mImpl
->Disconnect();
1536 MOZ_ASSERT(!tmp
->mImpl
);
1538 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1540 bool WebSocket::IsCertainlyAliveForCC() const { return mKeepingAlive
; }
1542 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebSocket
)
1543 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
1545 NS_IMPL_ADDREF_INHERITED(WebSocket
, DOMEventTargetHelper
)
1546 NS_IMPL_RELEASE_INHERITED(WebSocket
, DOMEventTargetHelper
)
1548 void WebSocket::DisconnectFromOwner() {
1549 // If we haven't called WebSocketImpl::Disconnect yet, update web
1550 // socket count here.
1551 if (NS_IsMainThread() && mImpl
&& !mImpl
->mDisconnectingOrDisconnected
&&
1553 GetOwner()->UpdateWebSocketCount(-1);
1556 DOMEventTargetHelper::DisconnectFromOwner();
1559 mImpl
->CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY
);
1562 DontKeepAliveAnyMore();
1565 //-----------------------------------------------------------------------------
1566 // WebSocketImpl:: initialization
1567 //-----------------------------------------------------------------------------
1569 nsresult
WebSocketImpl::Init(JSContext
* aCx
, bool aIsSecure
,
1570 nsIPrincipal
* aPrincipal
,
1571 const Maybe
<ClientInfo
>& aClientInfo
,
1572 nsICSPEventListener
* aCSPEventListener
,
1573 bool aIsServerSide
, const nsAString
& aURL
,
1574 nsTArray
<nsString
>& aProtocolArray
,
1575 const nsACString
& aScriptFile
,
1576 uint32_t aScriptLine
, uint32_t aScriptColumn
) {
1577 AssertIsOnMainThread();
1578 MOZ_ASSERT(aPrincipal
);
1580 mService
= WebSocketEventService::GetOrCreate();
1582 // We need to keep the implementation alive in case the init disconnects it
1583 // because of some error.
1584 RefPtr
<WebSocketImpl
> kungfuDeathGrip
= this;
1586 // Attempt to kill "ghost" websocket: but usually too early for check to fail
1587 nsresult rv
= mWebSocket
->CheckCurrentGlobalCorrectness();
1588 NS_ENSURE_SUCCESS(rv
, rv
);
1590 // Shut down websocket if window is frozen or destroyed (only needed for
1591 // "ghost" websockets--see bug 696085)
1592 RefPtr
<WebSocketImplProxy
> proxy
;
1593 if (mIsMainThread
) {
1594 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
1595 if (NS_WARN_IF(!os
)) {
1596 return NS_ERROR_FAILURE
;
1599 proxy
= new WebSocketImplProxy(this);
1600 rv
= os
->AddObserver(proxy
, DOM_WINDOW_DESTROYED_TOPIC
, true);
1601 NS_ENSURE_SUCCESS(rv
, rv
);
1603 rv
= os
->AddObserver(proxy
, DOM_WINDOW_FROZEN_TOPIC
, true);
1604 NS_ENSURE_SUCCESS(rv
, rv
);
1607 if (!mIsMainThread
) {
1608 mScriptFile
= aScriptFile
;
1609 mScriptLine
= aScriptLine
;
1610 mScriptColumn
= aScriptColumn
;
1615 JS::ColumnNumberZeroOrigin column
;
1616 JS::AutoFilename file
;
1617 if (JS::DescribeScriptedCaller(aCx
, &file
, &lineno
, &column
)) {
1618 mScriptFile
= file
.get();
1619 mScriptLine
= lineno
;
1620 mScriptColumn
= column
.zeroOriginValue();
1624 mIsServerSide
= aIsServerSide
;
1626 // If we don't have aCx, we are window-less, so we don't have a
1627 // inner-windowID. This can happen in sharedWorkers and ServiceWorkers or in
1628 // DedicateWorkers created by JSM.
1630 if (nsPIDOMWindowInner
* ownerWindow
= mWebSocket
->GetOwner()) {
1631 mInnerWindowID
= ownerWindow
->WindowID();
1635 mPrivateBrowsing
= !!aPrincipal
->OriginAttributesRef().mPrivateBrowsingId
;
1636 mIsChromeContext
= aPrincipal
->IsSystemPrincipal();
1639 rv
= ParseURL(aURL
);
1640 NS_ENSURE_SUCCESS(rv
, rv
);
1642 nsCOMPtr
<Document
> originDoc
= mWebSocket
->GetDocumentIfCurrent();
1644 rv
= mWebSocket
->CheckCurrentGlobalCorrectness();
1645 NS_ENSURE_SUCCESS(rv
, rv
);
1647 mOriginDocument
= do_GetWeakReference(originDoc
);
1649 if (!mIsServerSide
) {
1650 nsCOMPtr
<nsIURI
> uri
;
1652 nsresult rv
= NS_NewURI(getter_AddRefs(uri
), mURI
);
1654 // We crash here because we are sure that mURI is a valid URI, so either
1655 // we are OOM'ing or something else bad is happening.
1656 if (NS_WARN_IF(NS_FAILED(rv
))) {
1661 // The 'real' nsHttpChannel of the websocket gets opened in the parent.
1662 // Since we don't serialize the CSP within child and parent and also not
1663 // the context, we have to perform content policy checks here instead of
1665 // Please note that websockets can't follow redirects, hence there is no
1666 // need to perform a CSP check after redirects.
1667 nsCOMPtr
<nsILoadInfo
> secCheckLoadInfo
= new net::LoadInfo(
1668 aPrincipal
, // loading principal
1669 aPrincipal
, // triggering principal
1670 originDoc
, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK
,
1671 nsIContentPolicy::TYPE_WEBSOCKET
, aClientInfo
);
1673 if (aCSPEventListener
) {
1674 secCheckLoadInfo
->SetCspEventListener(aCSPEventListener
);
1677 int16_t shouldLoad
= nsIContentPolicy::ACCEPT
;
1678 rv
= NS_CheckContentLoadPolicy(uri
, secCheckLoadInfo
, ""_ns
, &shouldLoad
,
1679 nsContentUtils::GetContentPolicy());
1680 NS_ENSURE_SUCCESS(rv
, rv
);
1682 if (NS_CP_REJECTED(shouldLoad
)) {
1683 // Disallowed by content policy
1684 return NS_ERROR_CONTENT_BLOCKED
;
1687 // If the HTTPS-Only mode is enabled, we need to upgrade the websocket
1688 // connection from ws:// to wss:// and mark it as secure.
1689 if (!mSecure
&& originDoc
&&
1690 !nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackURL(
1691 originDoc
->GetDocumentURI())) {
1692 nsCOMPtr
<nsIURI
> uri
;
1693 nsresult rv
= NS_NewURI(getter_AddRefs(uri
), mURI
);
1694 NS_ENSURE_SUCCESS(rv
, rv
);
1696 // secCheckLoadInfo is only used for the triggering principal, so this
1698 if (nsHTTPSOnlyUtils::ShouldUpgradeWebSocket(uri
, secCheckLoadInfo
)) {
1699 mURI
.ReplaceSubstring("ws://", "wss://");
1700 if (NS_WARN_IF(mURI
.Find("wss://") != 0)) {
1708 // Potentially the page uses the CSP directive 'upgrade-insecure-requests'.
1709 // In such a case we have to upgrade ws: to wss: and also update mSecure
1710 // to reflect that upgrade. Please note that we can not upgrade from ws:
1711 // to wss: before performing content policy checks because CSP needs to
1712 // send reports in case the scheme is about to be upgraded.
1713 if (!mIsServerSide
&& !mSecure
&& originDoc
&&
1714 originDoc
->GetUpgradeInsecureRequests(false) &&
1715 !nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackURL(
1716 originDoc
->GetDocumentURI())) {
1717 // let's use the old specification before the upgrade for logging
1718 AutoTArray
<nsString
, 2> params
;
1719 CopyUTF8toUTF16(mURI
, *params
.AppendElement());
1721 // upgrade the request from ws:// to wss:// and mark as secure
1722 mURI
.ReplaceSubstring("ws://", "wss://");
1723 if (NS_WARN_IF(mURI
.Find("wss://") != 0)) {
1728 params
.AppendElement(u
"wss"_ns
);
1729 CSP_LogLocalizedStr("upgradeInsecureRequest", params
,
1730 u
""_ns
, // aSourceFile
1731 u
""_ns
, // aScriptSample
1734 nsIScriptError::warningFlag
,
1735 "upgradeInsecureRequest"_ns
, mInnerWindowID
,
1739 // Don't allow https:// to open ws://
1740 if (!mIsServerSide
&& !mSecure
&&
1741 !Preferences::GetBool("network.websocket.allowInsecureFromHTTPS",
1743 !nsMixedContentBlocker::IsPotentiallyTrustworthyLoopbackHost(
1746 return NS_ERROR_DOM_SECURITY_ERR
;
1750 // Assign the sub protocol list and scan it for illegal values
1751 for (uint32_t index
= 0; index
< aProtocolArray
.Length(); ++index
) {
1752 if (!WebSocket::IsValidProtocolString(aProtocolArray
[index
])) {
1753 return NS_ERROR_DOM_SYNTAX_ERR
;
1756 if (!mRequestedProtocolList
.IsEmpty()) {
1757 mRequestedProtocolList
.AppendLiteral(", ");
1760 AppendUTF16toUTF8(aProtocolArray
[index
], mRequestedProtocolList
);
1763 if (mIsMainThread
) {
1764 mImplProxy
= std::move(proxy
);
1769 nsresult
WebSocketImpl::AsyncOpen(
1770 nsIPrincipal
* aPrincipal
, uint64_t aInnerWindowID
,
1771 nsITransportProvider
* aTransportProvider
,
1772 const nsACString
& aNegotiatedExtensions
,
1773 UniquePtr
<SerializedStackHolder
> aOriginStack
) {
1774 MOZ_ASSERT(NS_IsMainThread(), "Not running on main thread");
1775 MOZ_ASSERT_IF(!aTransportProvider
, aNegotiatedExtensions
.IsEmpty());
1777 nsCString webExposedOriginSerialization
;
1778 nsresult rv
= aPrincipal
->GetWebExposedOriginSerialization(
1779 webExposedOriginSerialization
);
1780 if (NS_FAILED(rv
)) {
1781 webExposedOriginSerialization
.AssignLiteral("null");
1784 if (aTransportProvider
) {
1785 rv
= mChannel
->SetServerParameters(aTransportProvider
,
1786 aNegotiatedExtensions
);
1787 NS_ENSURE_SUCCESS(rv
, rv
);
1790 ToLowerCase(webExposedOriginSerialization
);
1792 nsCOMPtr
<nsIURI
> uri
;
1793 if (!aTransportProvider
) {
1794 rv
= NS_NewURI(getter_AddRefs(uri
), mURI
);
1795 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1798 rv
= mChannel
->AsyncOpenNative(uri
, webExposedOriginSerialization
,
1799 aPrincipal
->OriginAttributesRef(),
1800 aInnerWindowID
, this, nullptr);
1801 if (NS_WARN_IF(NS_FAILED(rv
))) {
1802 return NS_ERROR_CONTENT_BLOCKED
;
1805 NotifyNetworkMonitorAlternateStack(mChannel
, std::move(aOriginStack
));
1807 mInnerWindowID
= aInnerWindowID
;
1812 //-----------------------------------------------------------------------------
1813 // WebSocketImpl methods:
1814 //-----------------------------------------------------------------------------
1816 class nsAutoCloseWS final
{
1818 explicit nsAutoCloseWS(WebSocketImpl
* aWebSocketImpl
)
1819 : mWebSocketImpl(aWebSocketImpl
) {}
1822 if (!mWebSocketImpl
->mChannel
) {
1823 mWebSocketImpl
->CloseConnection(
1824 nsIWebSocketChannel::CLOSE_INTERNAL_ERROR
);
1829 RefPtr
<WebSocketImpl
> mWebSocketImpl
;
1832 nsresult
WebSocketImpl::InitializeConnection(
1833 nsIPrincipal
* aPrincipal
, nsICookieJarSettings
* aCookieJarSettings
) {
1834 AssertIsOnMainThread();
1835 MOZ_ASSERT(!mChannel
, "mChannel should be null");
1837 nsCOMPtr
<nsIWebSocketChannel
> wsChannel
;
1838 nsAutoCloseWS
autoClose(this);
1843 do_CreateInstance("@mozilla.org/network/protocol;1?name=wss", &rv
);
1846 do_CreateInstance("@mozilla.org/network/protocol;1?name=ws", &rv
);
1848 NS_ENSURE_SUCCESS(rv
, rv
);
1850 // add ourselves to the document's load group and
1851 // provide the http stack the loadgroup info too
1852 nsCOMPtr
<nsILoadGroup
> loadGroup
;
1853 rv
= GetLoadGroup(getter_AddRefs(loadGroup
));
1855 rv
= wsChannel
->SetLoadGroup(loadGroup
);
1856 NS_ENSURE_SUCCESS(rv
, rv
);
1857 rv
= loadGroup
->AddRequest(this, nullptr);
1858 NS_ENSURE_SUCCESS(rv
, rv
);
1860 mWeakLoadGroup
= do_GetWeakReference(loadGroup
);
1863 // manually adding loadinfo to the channel since it
1864 // was not set during channel creation.
1865 nsCOMPtr
<Document
> doc
= do_QueryReferent(mOriginDocument
);
1867 // mOriginDocument has to be release on main-thread because WeakReferences
1868 // are not thread-safe.
1869 mOriginDocument
= nullptr;
1871 // The TriggeringPrincipal for websockets must always be a script.
1872 // Let's make sure that the doc's principal (if a doc exists)
1873 // and aPrincipal are same origin.
1874 MOZ_ASSERT(!doc
|| doc
->NodePrincipal()->Equals(aPrincipal
));
1876 rv
= wsChannel
->InitLoadInfoNative(
1877 doc
, doc
? doc
->NodePrincipal() : aPrincipal
, aPrincipal
,
1879 nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL
,
1880 nsIContentPolicy::TYPE_WEBSOCKET
, 0);
1881 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1883 if (!mRequestedProtocolList
.IsEmpty()) {
1884 rv
= wsChannel
->SetProtocol(mRequestedProtocolList
);
1885 NS_ENSURE_SUCCESS(rv
, rv
);
1888 nsCOMPtr
<nsIThreadRetargetableRequest
> rr
= do_QueryInterface(wsChannel
);
1889 NS_ENSURE_TRUE(rr
, NS_ERROR_FAILURE
);
1891 rv
= rr
->RetargetDeliveryTo(this);
1892 NS_ENSURE_SUCCESS(rv
, rv
);
1894 mChannel
= wsChannel
;
1896 if (mIsMainThread
) {
1897 MOZ_ASSERT(mImplProxy
);
1898 mService
->AssociateWebSocketImplWithSerialID(mImplProxy
,
1899 mChannel
->Serial());
1902 if (mIsMainThread
&& doc
) {
1903 mMainThreadEventTarget
= doc
->EventTargetFor(TaskCategory::Other
);
1909 void WebSocketImpl::DispatchConnectionCloseEvents() {
1910 AssertIsOnTargetThread();
1912 if (mDisconnectingOrDisconnected
) {
1916 mWebSocket
->SetReadyState(WebSocket::CLOSED
);
1918 // Let's keep the object alive because the webSocket can be CCed in the
1919 // onerror or in the onclose callback.
1920 RefPtr
<WebSocket
> webSocket
= mWebSocket
;
1922 // Call 'onerror' if needed
1924 nsresult rv
= webSocket
->CreateAndDispatchSimpleEvent(ERROR_EVENT_STRING
);
1925 if (NS_FAILED(rv
)) {
1926 NS_WARNING("Failed to dispatch the error event");
1930 nsresult rv
= webSocket
->CreateAndDispatchCloseEvent(
1931 mCloseEventWasClean
, mCloseEventCode
, mCloseEventReason
);
1932 if (NS_FAILED(rv
)) {
1933 NS_WARNING("Failed to dispatch the close event");
1936 webSocket
->UpdateMustKeepAlive();
1940 nsresult
WebSocket::CreateAndDispatchSimpleEvent(const nsAString
& aName
) {
1942 AssertIsOnTargetThread();
1944 nsresult rv
= CheckCurrentGlobalCorrectness();
1945 if (NS_FAILED(rv
)) {
1949 RefPtr
<Event
> event
= NS_NewDOMEvent(this, nullptr, nullptr);
1951 // it doesn't bubble, and it isn't cancelable
1952 event
->InitEvent(aName
, false, false);
1953 event
->SetTrusted(true);
1956 DispatchEvent(*event
, err
);
1957 return err
.StealNSResult();
1960 nsresult
WebSocket::CreateAndDispatchMessageEvent(const nsACString
& aData
,
1963 AssertIsOnTargetThread();
1966 if (NS_WARN_IF(!jsapi
.Init(GetOwnerGlobal()))) {
1967 return NS_ERROR_FAILURE
;
1970 JSContext
* cx
= jsapi
.cx();
1972 nsresult rv
= CheckCurrentGlobalCorrectness();
1973 if (NS_FAILED(rv
)) {
1977 uint16_t messageType
= nsIWebSocketEventListener::TYPE_STRING
;
1979 // Create appropriate JS object for message
1980 JS::Rooted
<JS::Value
> jsData(cx
);
1982 if (mBinaryType
== dom::BinaryType::Blob
) {
1983 messageType
= nsIWebSocketEventListener::TYPE_BLOB
;
1986 Blob::CreateStringBlob(GetOwnerGlobal(), aData
, u
""_ns
);
1987 if (NS_WARN_IF(!blob
)) {
1988 return NS_ERROR_FAILURE
;
1991 if (!ToJSValue(cx
, blob
, &jsData
)) {
1992 return NS_ERROR_FAILURE
;
1995 } else if (mBinaryType
== dom::BinaryType::Arraybuffer
) {
1996 messageType
= nsIWebSocketEventListener::TYPE_ARRAYBUFFER
;
1998 JS::Rooted
<JSObject
*> arrayBuf(cx
);
2000 nsContentUtils::CreateArrayBuffer(cx
, aData
, arrayBuf
.address());
2001 NS_ENSURE_SUCCESS(rv
, rv
);
2002 jsData
.setObject(*arrayBuf
);
2004 MOZ_CRASH("Unknown binary type!");
2005 return NS_ERROR_UNEXPECTED
;
2009 nsAutoString utf16Data
;
2010 if (!AppendUTF8toUTF16(aData
, utf16Data
, mozilla::fallible
)) {
2011 return NS_ERROR_OUT_OF_MEMORY
;
2014 jsString
= JS_NewUCStringCopyN(cx
, utf16Data
.get(), utf16Data
.Length());
2015 NS_ENSURE_TRUE(jsString
, NS_ERROR_FAILURE
);
2017 jsData
.setString(jsString
);
2020 mImpl
->mService
->WebSocketMessageAvailable(
2021 mImpl
->mChannel
->Serial(), mImpl
->mInnerWindowID
, aData
, messageType
);
2023 // create an event that uses the MessageEvent interface,
2024 // which does not bubble, is not cancelable, and has no default action
2026 RefPtr
<MessageEvent
> event
= new MessageEvent(this, nullptr, nullptr);
2028 event
->InitMessageEvent(nullptr, MESSAGE_EVENT_STRING
, CanBubble::eNo
,
2029 Cancelable::eNo
, jsData
, mImpl
->mUTF16Origin
, u
""_ns
,
2030 nullptr, Sequence
<OwningNonNull
<MessagePort
>>());
2031 event
->SetTrusted(true);
2034 DispatchEvent(*event
, err
);
2035 return err
.StealNSResult();
2038 nsresult
WebSocket::CreateAndDispatchCloseEvent(bool aWasClean
, uint16_t aCode
,
2039 const nsAString
& aReason
) {
2040 AssertIsOnTargetThread();
2042 // This method is called by a runnable and it can happen that, in the
2043 // meantime, GC unlinked this object, so mImpl could be null.
2044 if (mImpl
&& mImpl
->mChannel
) {
2045 mImpl
->mService
->WebSocketClosed(mImpl
->mChannel
->Serial(),
2046 mImpl
->mInnerWindowID
, aWasClean
, aCode
,
2050 nsresult rv
= CheckCurrentGlobalCorrectness();
2051 if (NS_FAILED(rv
)) {
2055 CloseEventInit init
;
2056 init
.mBubbles
= false;
2057 init
.mCancelable
= false;
2058 init
.mWasClean
= aWasClean
;
2060 init
.mReason
= aReason
;
2062 RefPtr
<CloseEvent
> event
=
2063 CloseEvent::Constructor(this, CLOSE_EVENT_STRING
, init
);
2064 event
->SetTrusted(true);
2067 DispatchEvent(*event
, err
);
2068 return err
.StealNSResult();
2071 nsresult
WebSocketImpl::ParseURL(const nsAString
& aURL
) {
2072 AssertIsOnMainThread();
2073 NS_ENSURE_TRUE(!aURL
.IsEmpty(), NS_ERROR_DOM_SYNTAX_ERR
);
2075 if (mIsServerSide
) {
2076 mWebSocket
->mURI
= aURL
;
2077 CopyUTF16toUTF8(mWebSocket
->mURI
, mURI
);
2082 nsCOMPtr
<nsIURI
> uri
;
2083 nsresult rv
= NS_NewURI(getter_AddRefs(uri
), aURL
);
2084 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SYNTAX_ERR
);
2086 nsCOMPtr
<nsIURL
> parsedURL
= do_QueryInterface(uri
, &rv
);
2087 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SYNTAX_ERR
);
2090 rv
= parsedURL
->GetHasRef(&hasRef
);
2091 NS_ENSURE_TRUE(NS_SUCCEEDED(rv
) && !hasRef
, NS_ERROR_DOM_SYNTAX_ERR
);
2093 nsAutoCString scheme
;
2094 rv
= parsedURL
->GetScheme(scheme
);
2095 NS_ENSURE_TRUE(NS_SUCCEEDED(rv
) && !scheme
.IsEmpty(),
2096 NS_ERROR_DOM_SYNTAX_ERR
);
2099 rv
= parsedURL
->GetAsciiHost(host
);
2100 NS_ENSURE_TRUE(NS_SUCCEEDED(rv
) && !host
.IsEmpty(), NS_ERROR_DOM_SYNTAX_ERR
);
2103 rv
= parsedURL
->GetPort(&port
);
2104 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SYNTAX_ERR
);
2106 nsAutoCString filePath
;
2107 rv
= parsedURL
->GetFilePath(filePath
);
2108 if (filePath
.IsEmpty()) {
2109 filePath
.Assign('/');
2111 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SYNTAX_ERR
);
2113 nsAutoCString query
;
2114 rv
= parsedURL
->GetQuery(query
);
2115 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SYNTAX_ERR
);
2117 if (scheme
.LowerCaseEqualsLiteral("ws")) {
2119 mPort
= (port
== -1) ? DEFAULT_WS_SCHEME_PORT
: port
;
2120 } else if (scheme
.LowerCaseEqualsLiteral("wss")) {
2122 mPort
= (port
== -1) ? DEFAULT_WSS_SCHEME_PORT
: port
;
2124 return NS_ERROR_DOM_SYNTAX_ERR
;
2128 nsContentUtils::GetWebExposedOriginSerialization(parsedURL
, mUTF16Origin
);
2129 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SYNTAX_ERR
);
2132 ToLowerCase(mAsciiHost
);
2134 mResource
= filePath
;
2135 if (!query
.IsEmpty()) {
2136 mResource
.Append('?');
2137 mResource
.Append(query
);
2139 uint32_t length
= mResource
.Length();
2141 for (i
= 0; i
< length
; ++i
) {
2142 if (mResource
[i
] < static_cast<char16_t
>(0x0021) ||
2143 mResource
[i
] > static_cast<char16_t
>(0x007E)) {
2144 return NS_ERROR_DOM_SYNTAX_ERR
;
2148 rv
= parsedURL
->GetSpec(mURI
);
2149 MOZ_ASSERT(NS_SUCCEEDED(rv
));
2151 CopyUTF8toUTF16(mURI
, mWebSocket
->mURI
);
2155 //-----------------------------------------------------------------------------
2156 // Methods that keep alive the WebSocket object when:
2157 // 1. the object has registered event listeners that can be triggered
2158 // ("strong event listeners");
2159 // 2. there are outgoing not sent messages.
2160 //-----------------------------------------------------------------------------
2162 void WebSocket::UpdateMustKeepAlive() {
2163 // Here we could not have mImpl.
2164 MOZ_ASSERT(NS_IsMainThread() == mIsMainThread
);
2166 if (!mCheckMustKeepAlive
|| !mImpl
) {
2170 bool shouldKeepAlive
= false;
2171 uint16_t readyState
= ReadyState();
2173 if (mListenerManager
) {
2174 switch (readyState
) {
2176 if (mListenerManager
->HasListenersFor(OPEN_EVENT_STRING
) ||
2177 mListenerManager
->HasListenersFor(MESSAGE_EVENT_STRING
) ||
2178 mListenerManager
->HasListenersFor(ERROR_EVENT_STRING
) ||
2179 mListenerManager
->HasListenersFor(CLOSE_EVENT_STRING
)) {
2180 shouldKeepAlive
= true;
2186 if (mListenerManager
->HasListenersFor(MESSAGE_EVENT_STRING
) ||
2187 mListenerManager
->HasListenersFor(ERROR_EVENT_STRING
) ||
2188 mListenerManager
->HasListenersFor(CLOSE_EVENT_STRING
) ||
2189 mOutgoingBufferedAmount
.value() != 0) {
2190 shouldKeepAlive
= true;
2195 shouldKeepAlive
= false;
2200 if (mKeepingAlive
&& !shouldKeepAlive
) {
2201 mKeepingAlive
= false;
2202 mImpl
->ReleaseObject();
2203 } else if (!mKeepingAlive
&& shouldKeepAlive
) {
2204 mKeepingAlive
= true;
2205 mImpl
->AddRefObject();
2209 void WebSocket::DontKeepAliveAnyMore() {
2210 // Here we could not have mImpl.
2211 MOZ_ASSERT(NS_IsMainThread() == mIsMainThread
);
2213 if (mKeepingAlive
) {
2216 mKeepingAlive
= false;
2217 mImpl
->ReleaseObject();
2220 mCheckMustKeepAlive
= false;
2223 void WebSocketImpl::AddRefObject() {
2224 MOZ_RELEASE_ASSERT(NS_IsMainThread() == mIsMainThread
);
2228 void WebSocketImpl::ReleaseObject() {
2229 MOZ_RELEASE_ASSERT(NS_IsMainThread() == mIsMainThread
);
2233 bool WebSocketImpl::RegisterWorkerRef(WorkerPrivate
* aWorkerPrivate
) {
2234 MOZ_ASSERT(aWorkerPrivate
);
2236 RefPtr
<WebSocketImpl
> self
= this;
2238 // In workers we have to keep the worker alive using a strong reference in
2239 // order to dispatch messages correctly.
2240 RefPtr
<StrongWorkerRef
> workerRef
=
2241 StrongWorkerRef::Create(aWorkerPrivate
, "WebSocketImpl", [self
]() {
2243 MutexAutoLock
lock(self
->mMutex
);
2244 self
->mWorkerShuttingDown
= true;
2247 self
->CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY
, ""_ns
);
2249 if (NS_WARN_IF(!workerRef
)) {
2253 mWorkerRef
= new ThreadSafeWorkerRef(workerRef
);
2254 MOZ_ASSERT(mWorkerRef
);
2259 void WebSocketImpl::UnregisterWorkerRef() {
2260 MOZ_ASSERT(mDisconnectingOrDisconnected
);
2261 MOZ_ASSERT(mWorkerRef
);
2262 mWorkerRef
->Private()->AssertIsOnWorkerThread();
2265 MutexAutoLock
lock(mMutex
);
2266 mWorkerShuttingDown
= true;
2269 // The DTOR of this StrongWorkerRef will release the worker for us.
2270 mWorkerRef
= nullptr;
2273 nsresult
WebSocketImpl::UpdateURI() {
2274 AssertIsOnTargetThread();
2276 // Check for Redirections
2277 RefPtr
<BaseWebSocketChannel
> channel
;
2278 channel
= static_cast<BaseWebSocketChannel
*>(mChannel
.get());
2279 MOZ_ASSERT(channel
);
2281 channel
->GetEffectiveURL(mWebSocket
->mEffectiveURL
);
2282 mSecure
= channel
->IsEncrypted();
2287 void WebSocket::EventListenerAdded(nsAtom
* aType
) {
2288 AssertIsOnTargetThread();
2289 UpdateMustKeepAlive();
2292 void WebSocket::EventListenerRemoved(nsAtom
* aType
) {
2293 AssertIsOnTargetThread();
2294 UpdateMustKeepAlive();
2297 //-----------------------------------------------------------------------------
2298 // WebSocket - methods
2299 //-----------------------------------------------------------------------------
2301 // webIDL: readonly attribute unsigned short readyState;
2302 uint16_t WebSocket::ReadyState() {
2303 MutexAutoLock
lock(mMutex
);
2307 void WebSocket::SetReadyState(uint16_t aReadyState
) {
2308 MutexAutoLock
lock(mMutex
);
2309 mReadyState
= aReadyState
;
2312 // webIDL: readonly attribute unsigned long long bufferedAmount;
2313 uint64_t WebSocket::BufferedAmount() const {
2314 AssertIsOnTargetThread();
2315 MOZ_RELEASE_ASSERT(mOutgoingBufferedAmount
.isValid());
2316 return mOutgoingBufferedAmount
.value();
2319 // webIDL: attribute BinaryType binaryType;
2320 dom::BinaryType
WebSocket::BinaryType() const {
2321 AssertIsOnTargetThread();
2325 // webIDL: attribute BinaryType binaryType;
2326 void WebSocket::SetBinaryType(dom::BinaryType aData
) {
2327 AssertIsOnTargetThread();
2328 mBinaryType
= aData
;
2331 // webIDL: readonly attribute DOMString url
2332 void WebSocket::GetUrl(nsAString
& aURL
) {
2333 AssertIsOnTargetThread();
2335 if (mEffectiveURL
.IsEmpty()) {
2338 aURL
= mEffectiveURL
;
2342 // webIDL: readonly attribute DOMString extensions;
2343 void WebSocket::GetExtensions(nsAString
& aExtensions
) {
2344 AssertIsOnTargetThread();
2345 CopyUTF8toUTF16(mEstablishedExtensions
, aExtensions
);
2348 // webIDL: readonly attribute DOMString protocol;
2349 void WebSocket::GetProtocol(nsAString
& aProtocol
) {
2350 AssertIsOnTargetThread();
2351 CopyUTF8toUTF16(mEstablishedProtocol
, aProtocol
);
2354 // webIDL: void send(DOMString data);
2355 void WebSocket::Send(const nsAString
& aData
, ErrorResult
& aRv
) {
2356 AssertIsOnTargetThread();
2358 nsAutoCString msgString
;
2359 if (!AppendUTF16toUTF8(aData
, msgString
, mozilla::fallible_t())) {
2360 aRv
.Throw(NS_ERROR_FILE_TOO_BIG
);
2363 Send(nullptr, msgString
, msgString
.Length(), false, aRv
);
2366 void WebSocket::Send(Blob
& aData
, ErrorResult
& aRv
) {
2367 AssertIsOnTargetThread();
2369 nsCOMPtr
<nsIInputStream
> msgStream
;
2370 aData
.CreateInputStream(getter_AddRefs(msgStream
), aRv
);
2371 if (NS_WARN_IF(aRv
.Failed())) {
2375 uint64_t msgLength
= aData
.GetSize(aRv
);
2376 if (NS_WARN_IF(aRv
.Failed())) {
2380 if (msgLength
> UINT32_MAX
) {
2381 aRv
.Throw(NS_ERROR_FILE_TOO_BIG
);
2385 Send(msgStream
, ""_ns
, msgLength
, true, aRv
);
2388 void WebSocket::Send(const ArrayBuffer
& aData
, ErrorResult
& aRv
) {
2389 AssertIsOnTargetThread();
2391 aData
.ComputeState();
2393 static_assert(sizeof(*aData
.Data()) == 1, "byte-sized data required");
2395 uint32_t len
= aData
.Length();
2396 char* data
= reinterpret_cast<char*>(aData
.Data());
2398 nsDependentCSubstring msgString
;
2399 if (!msgString
.Assign(data
, len
, mozilla::fallible_t())) {
2400 aRv
.Throw(NS_ERROR_FILE_TOO_BIG
);
2403 Send(nullptr, msgString
, len
, true, aRv
);
2406 void WebSocket::Send(const ArrayBufferView
& aData
, ErrorResult
& aRv
) {
2407 AssertIsOnTargetThread();
2409 aData
.ComputeState();
2411 static_assert(sizeof(*aData
.Data()) == 1, "byte-sized data required");
2413 uint32_t len
= aData
.Length();
2414 char* data
= reinterpret_cast<char*>(aData
.Data());
2416 nsDependentCSubstring msgString
;
2417 if (!msgString
.Assign(data
, len
, mozilla::fallible_t())) {
2418 aRv
.Throw(NS_ERROR_FILE_TOO_BIG
);
2421 Send(nullptr, msgString
, len
, true, aRv
);
2424 void WebSocket::Send(nsIInputStream
* aMsgStream
, const nsACString
& aMsgString
,
2425 uint32_t aMsgLength
, bool aIsBinary
, ErrorResult
& aRv
) {
2426 AssertIsOnTargetThread();
2428 int64_t readyState
= ReadyState();
2429 if (readyState
== CONNECTING
) {
2430 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
2434 CheckedUint64 outgoingBufferedAmount
= mOutgoingBufferedAmount
;
2435 outgoingBufferedAmount
+= aMsgLength
;
2436 if (!outgoingBufferedAmount
.isValid()) {
2437 aRv
.Throw(NS_ERROR_OUT_OF_MEMORY
);
2441 // Always increment outgoing buffer len, even if closed
2442 mOutgoingBufferedAmount
= outgoingBufferedAmount
;
2443 MOZ_RELEASE_ASSERT(mOutgoingBufferedAmount
.isValid());
2445 if (readyState
== CLOSING
|| readyState
== CLOSED
) {
2449 // We must have mImpl when connected.
2451 MOZ_ASSERT(readyState
== OPEN
, "Unknown state in WebSocket::Send");
2455 rv
= mImpl
->mChannel
->SendBinaryStream(aMsgStream
, aMsgLength
);
2458 rv
= mImpl
->mChannel
->SendBinaryMsg(aMsgString
);
2460 rv
= mImpl
->mChannel
->SendMsg(aMsgString
);
2464 if (NS_FAILED(rv
)) {
2469 UpdateMustKeepAlive();
2472 // webIDL: void close(optional unsigned short code, optional DOMString reason):
2473 void WebSocket::Close(const Optional
<uint16_t>& aCode
,
2474 const Optional
<nsAString
>& aReason
, ErrorResult
& aRv
) {
2475 MOZ_RELEASE_ASSERT(NS_IsMainThread() == mIsMainThread
);
2477 // the reason code is optional, but if provided it must be in a specific range
2478 uint16_t closeCode
= 0;
2479 if (aCode
.WasPassed()) {
2480 if (aCode
.Value() != 1000 &&
2481 (aCode
.Value() < 3000 || aCode
.Value() > 4999)) {
2482 aRv
.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR
);
2485 closeCode
= aCode
.Value();
2488 nsCString closeReason
;
2489 if (aReason
.WasPassed()) {
2490 CopyUTF16toUTF8(aReason
.Value(), closeReason
);
2492 // The API requires the UTF-8 string to be 123 or less bytes
2493 if (closeReason
.Length() > 123) {
2494 aRv
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
2499 int64_t readyState
= ReadyState();
2500 if (readyState
== CLOSING
|| readyState
== CLOSED
) {
2504 // If we don't have mImpl, we are in a shutting down worker where we are still
2505 // in CONNECTING state, but already disconnected internally.
2507 MOZ_ASSERT(readyState
== CONNECTING
);
2508 SetReadyState(CLOSING
);
2512 if (readyState
== CONNECTING
) {
2513 mImpl
->FailConnection(closeCode
, closeReason
);
2517 MOZ_ASSERT(readyState
== OPEN
);
2518 mImpl
->CloseConnection(closeCode
, closeReason
);
2521 //-----------------------------------------------------------------------------
2522 // WebSocketImpl::nsIObserver
2523 //-----------------------------------------------------------------------------
2526 WebSocketImpl::Observe(nsISupports
* aSubject
, const char* aTopic
,
2527 const char16_t
* aData
) {
2528 AssertIsOnMainThread();
2530 int64_t readyState
= mWebSocket
->ReadyState();
2531 if ((readyState
== WebSocket::CLOSING
) || (readyState
== WebSocket::CLOSED
)) {
2535 nsCOMPtr
<nsPIDOMWindowInner
> window
= do_QueryInterface(aSubject
);
2536 if (!mWebSocket
->GetOwner() || window
!= mWebSocket
->GetOwner()) {
2540 if ((strcmp(aTopic
, DOM_WINDOW_FROZEN_TOPIC
) == 0) ||
2541 (strcmp(aTopic
, DOM_WINDOW_DESTROYED_TOPIC
) == 0)) {
2542 CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY
);
2548 //-----------------------------------------------------------------------------
2549 // WebSocketImpl::nsIRequest
2550 //-----------------------------------------------------------------------------
2553 WebSocketImpl::GetName(nsACString
& aName
) {
2554 AssertIsOnMainThread();
2556 CopyUTF16toUTF8(mWebSocket
->mURI
, aName
);
2561 WebSocketImpl::IsPending(bool* aValue
) {
2562 AssertIsOnTargetThread();
2564 int64_t readyState
= mWebSocket
->ReadyState();
2565 *aValue
= (readyState
!= WebSocket::CLOSED
);
2570 WebSocketImpl::GetStatus(nsresult
* aStatus
) {
2571 AssertIsOnTargetThread();
2579 class CancelRunnable final
: public MainThreadWorkerRunnable
{
2581 CancelRunnable(ThreadSafeWorkerRef
* aWorkerRef
, WebSocketImpl
* aImpl
)
2582 : MainThreadWorkerRunnable(aWorkerRef
->Private()), mImpl(aImpl
) {}
2584 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
2585 aWorkerPrivate
->AssertIsOnWorkerThread();
2586 return !NS_FAILED(mImpl
->CancelInternal());
2590 RefPtr
<WebSocketImpl
> mImpl
;
2595 NS_IMETHODIMP
WebSocketImpl::SetCanceledReason(const nsACString
& aReason
) {
2596 return SetCanceledReasonImpl(aReason
);
2599 NS_IMETHODIMP
WebSocketImpl::GetCanceledReason(nsACString
& aReason
) {
2600 return GetCanceledReasonImpl(aReason
);
2603 NS_IMETHODIMP
WebSocketImpl::CancelWithReason(nsresult aStatus
,
2604 const nsACString
& aReason
) {
2605 return CancelWithReasonImpl(aStatus
, aReason
);
2608 // Window closed, stop/reload button pressed, user navigated away from page,
2611 WebSocketImpl::Cancel(nsresult aStatus
) {
2612 AssertIsOnMainThread();
2614 if (!mIsMainThread
) {
2615 MOZ_ASSERT(mWorkerRef
);
2616 RefPtr
<CancelRunnable
> runnable
= new CancelRunnable(mWorkerRef
, this);
2617 if (!runnable
->Dispatch()) {
2618 return NS_ERROR_FAILURE
;
2624 return CancelInternal();
2627 nsresult
WebSocketImpl::CancelInternal() {
2628 AssertIsOnTargetThread();
2630 // If CancelInternal is called by a runnable, we may already be disconnected
2631 // by the time it runs.
2632 if (mDisconnectingOrDisconnected
) {
2636 int64_t readyState
= mWebSocket
->ReadyState();
2637 if (readyState
== WebSocket::CLOSING
|| readyState
== WebSocket::CLOSED
) {
2641 return CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY
);
2645 WebSocketImpl::Suspend() {
2646 AssertIsOnMainThread();
2647 return NS_ERROR_NOT_IMPLEMENTED
;
2651 WebSocketImpl::Resume() {
2652 AssertIsOnMainThread();
2653 return NS_ERROR_NOT_IMPLEMENTED
;
2657 WebSocketImpl::GetLoadGroup(nsILoadGroup
** aLoadGroup
) {
2658 AssertIsOnMainThread();
2660 *aLoadGroup
= nullptr;
2662 if (mIsMainThread
) {
2663 nsCOMPtr
<Document
> doc
= mWebSocket
->GetDocumentIfCurrent();
2665 *aLoadGroup
= doc
->GetDocumentLoadGroup().take();
2671 MOZ_ASSERT(mWorkerRef
);
2673 // Walk up to our containing page
2674 WorkerPrivate
* wp
= mWorkerRef
->Private();
2675 while (wp
->GetParent()) {
2676 wp
= wp
->GetParent();
2679 nsPIDOMWindowInner
* window
= wp
->GetWindow();
2684 Document
* doc
= window
->GetExtantDoc();
2686 *aLoadGroup
= doc
->GetDocumentLoadGroup().take();
2693 WebSocketImpl::SetLoadGroup(nsILoadGroup
* aLoadGroup
) {
2694 AssertIsOnMainThread();
2695 return NS_ERROR_UNEXPECTED
;
2699 WebSocketImpl::GetLoadFlags(nsLoadFlags
* aLoadFlags
) {
2700 AssertIsOnMainThread();
2702 *aLoadFlags
= nsIRequest::LOAD_BACKGROUND
;
2707 WebSocketImpl::SetLoadFlags(nsLoadFlags aLoadFlags
) {
2708 AssertIsOnMainThread();
2710 // we won't change the load flags at all.
2715 WebSocketImpl::GetTRRMode(nsIRequest::TRRMode
* aTRRMode
) {
2716 return GetTRRModeImpl(aTRRMode
);
2720 WebSocketImpl::SetTRRMode(nsIRequest::TRRMode aTRRMode
) {
2721 return SetTRRModeImpl(aTRRMode
);
2726 class WorkerRunnableDispatcher final
: public WorkerRunnable
{
2727 RefPtr
<WebSocketImpl
> mWebSocketImpl
;
2730 WorkerRunnableDispatcher(WebSocketImpl
* aImpl
,
2731 ThreadSafeWorkerRef
* aWorkerRef
,
2732 already_AddRefed
<nsIRunnable
> aEvent
)
2733 : WorkerRunnable(aWorkerRef
->Private(), WorkerThreadUnchangedBusyCount
),
2734 mWebSocketImpl(aImpl
),
2735 mEvent(std::move(aEvent
)) {}
2737 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
) override
{
2738 aWorkerPrivate
->AssertIsOnWorkerThread();
2740 // No messages when disconnected.
2741 if (mWebSocketImpl
->mDisconnectingOrDisconnected
) {
2742 NS_WARNING("Dispatching a WebSocket event after the disconnection!");
2746 return !NS_FAILED(mEvent
->Run());
2749 void PostRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
,
2750 bool aRunResult
) override
{}
2752 bool PreDispatch(WorkerPrivate
* aWorkerPrivate
) override
{
2753 // We don't call WorkerRunnable::PreDispatch because it would assert the
2754 // wrong thing about which thread we're on. We're on whichever thread the
2755 // channel implementation is running on (probably the main thread or socket
2756 // transport thread).
2760 void PostDispatch(WorkerPrivate
* aWorkerPrivate
,
2761 bool aDispatchResult
) override
{
2762 // We don't call WorkerRunnable::PreDispatch because it would assert the
2763 // wrong thing about which thread we're on. We're on whichever thread the
2764 // channel implementation is running on (probably the main thread or socket
2765 // transport thread).
2769 nsCOMPtr
<nsIRunnable
> mEvent
;
2775 WebSocketImpl::DispatchFromScript(nsIRunnable
* aEvent
, uint32_t aFlags
) {
2776 nsCOMPtr
<nsIRunnable
> event(aEvent
);
2777 return Dispatch(event
.forget(), aFlags
);
2781 WebSocketImpl::Dispatch(already_AddRefed
<nsIRunnable
> aEvent
, uint32_t aFlags
) {
2782 nsCOMPtr
<nsIRunnable
> event_ref(aEvent
);
2783 // If the target is the main-thread, we should try to dispatch the runnable
2784 // to a labeled event target.
2785 if (mIsMainThread
) {
2786 return mMainThreadEventTarget
2787 ? mMainThreadEventTarget
->Dispatch(event_ref
.forget())
2788 : GetMainThreadSerialEventTarget()->Dispatch(event_ref
.forget());
2791 MutexAutoLock
lock(mMutex
);
2792 if (mWorkerShuttingDown
) {
2796 MOZ_DIAGNOSTIC_ASSERT(mWorkerRef
);
2798 // If the target is a worker, we have to use a custom WorkerRunnableDispatcher
2800 RefPtr
<WorkerRunnableDispatcher
> event
=
2801 new WorkerRunnableDispatcher(this, mWorkerRef
, event_ref
.forget());
2803 if (!event
->Dispatch()) {
2804 return NS_ERROR_FAILURE
;
2811 WebSocketImpl::DelayedDispatch(already_AddRefed
<nsIRunnable
>, uint32_t) {
2812 return NS_ERROR_NOT_IMPLEMENTED
;
2816 WebSocketImpl::RegisterShutdownTask(nsITargetShutdownTask
*) {
2817 return NS_ERROR_NOT_IMPLEMENTED
;
2821 WebSocketImpl::UnregisterShutdownTask(nsITargetShutdownTask
*) {
2822 return NS_ERROR_NOT_IMPLEMENTED
;
2826 WebSocketImpl::IsOnCurrentThread(bool* aResult
) {
2827 *aResult
= IsTargetThread();
2831 NS_IMETHODIMP_(bool)
2832 WebSocketImpl::IsOnCurrentThreadInfallible() { return IsTargetThread(); }
2834 bool WebSocketImpl::IsTargetThread() const {
2835 // FIXME: This should also check if we're on the worker thread. Code using
2836 // `IsOnCurrentThread` could easily misbehave here!
2837 return NS_IsMainThread() == mIsMainThread
;
2840 void WebSocket::AssertIsOnTargetThread() const {
2841 MOZ_ASSERT(NS_IsMainThread() == mIsMainThread
);
2844 nsresult
WebSocketImpl::IsSecure(bool* aValue
) {
2845 MOZ_ASSERT(NS_IsMainThread());
2846 MOZ_ASSERT(mIsMainThread
);
2848 // Check the principal's uri to determine if we were loaded from https.
2849 nsCOMPtr
<nsIGlobalObject
> globalObject(GetEntryGlobal());
2850 nsCOMPtr
<nsIPrincipal
> principal
;
2853 principal
= globalObject
->PrincipalOrNull();
2856 nsCOMPtr
<nsPIDOMWindowInner
> innerWindow
= do_QueryInterface(globalObject
);
2858 // If we are in a XPConnect sandbox or in a JS component,
2859 // innerWindow will be null. There is nothing on top of this to be
2861 if (NS_WARN_IF(!principal
)) {
2864 *aValue
= principal
->SchemeIs("https");
2868 RefPtr
<WindowContext
> windowContext
= innerWindow
->GetWindowContext();
2869 if (NS_WARN_IF(!windowContext
)) {
2870 return NS_ERROR_DOM_SECURITY_ERR
;
2874 if (windowContext
->GetIsSecure()) {
2879 if (windowContext
->IsTop()) {
2882 // If we're not a top window get the parent window context instead.
2883 windowContext
= windowContext
->GetParentWindowContext();
2886 if (NS_WARN_IF(!windowContext
)) {
2887 return NS_ERROR_DOM_SECURITY_ERR
;
2891 *aValue
= windowContext
->GetIsSecure();
2895 } // namespace mozilla::dom