1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 ts=8 et 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"
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/ScriptSettings.h"
17 #include "mozilla/dom/WorkerPrivate.h"
18 #include "mozilla/dom/WorkerRunnable.h"
19 #include "mozilla/dom/WorkerScope.h"
20 #include "nsIScriptGlobalObject.h"
21 #include "nsIDOMWindow.h"
22 #include "nsIDocument.h"
24 #include "nsIXPConnect.h"
25 #include "nsContentUtils.h"
27 #include "nsIScriptObjectPrincipal.h"
29 #include "nsIUnicodeEncoder.h"
30 #include "nsThreadUtils.h"
31 #include "nsIDOMMessageEvent.h"
32 #include "nsIPromptFactory.h"
33 #include "nsIWindowWatcher.h"
34 #include "nsIPrompt.h"
35 #include "nsIStringBundle.h"
36 #include "nsIConsoleService.h"
37 #include "mozilla/dom/CloseEvent.h"
38 #include "nsICryptoHash.h"
39 #include "nsJSUtils.h"
40 #include "nsIScriptError.h"
41 #include "nsNetUtil.h"
42 #include "nsILoadGroup.h"
43 #include "mozilla/Preferences.h"
44 #include "xpcpublic.h"
45 #include "nsContentPolicyUtils.h"
46 #include "nsWrapperCacheInlines.h"
47 #include "nsIObserverService.h"
48 #include "nsIEventTarget.h"
49 #include "nsIInterfaceRequestor.h"
50 #include "nsIObserver.h"
51 #include "nsIRequest.h"
52 #include "nsIThreadRetargetableRequest.h"
53 #include "nsIWebSocketChannel.h"
54 #include "nsIWebSocketListener.h"
55 #include "nsProxyRelease.h"
56 #include "nsWeakReference.h"
58 using namespace mozilla::net
;
59 using namespace mozilla::dom::workers
;
64 class WebSocketImpl MOZ_FINAL
: public nsIInterfaceRequestor
65 , public nsIWebSocketListener
67 , public nsSupportsWeakReference
69 , public nsIEventTarget
72 NS_DECL_NSIINTERFACEREQUESTOR
73 NS_DECL_NSIWEBSOCKETLISTENER
76 NS_DECL_THREADSAFE_ISUPPORTS
77 NS_DECL_NSIEVENTTARGET
79 explicit WebSocketImpl(WebSocket
* aWebSocket
)
80 : mWebSocket(aWebSocket
)
81 , mOnCloseScheduled(false)
83 , mDisconnectingOrDisconnected(false)
84 , mCloseEventWasClean(false)
85 , mCloseEventCode(nsIWebSocketChannel::CLOSE_ABNORMAL
)
88 , mWorkerPrivate(nullptr)
90 , mHasFeatureRegistered(false)
93 , mWorkerShuttingDown(false)
95 if (!NS_IsMainThread()) {
96 mWorkerPrivate
= GetCurrentThreadWorkerPrivate();
97 MOZ_ASSERT(mWorkerPrivate
);
98 mIsMainThread
= false;
102 void AssertIsOnTargetThread() const
104 MOZ_ASSERT(IsTargetThread());
107 bool IsTargetThread() const;
109 void Init(JSContext
* aCx
,
110 nsIPrincipal
* aPrincipal
,
111 const nsAString
& aURL
,
112 nsTArray
<nsString
>& aProtocolArray
,
113 const nsACString
& aScriptFile
,
114 uint32_t aScriptLine
,
116 bool* aConnectionFailed
);
118 void AsyncOpen(ErrorResult
& aRv
);
120 nsresult
ParseURL(const nsAString
& aURL
);
121 nsresult
InitializeConnection();
123 // These methods when called can release the WebSocket object
124 void FailConnection(uint16_t reasonCode
,
125 const nsACString
& aReasonString
= EmptyCString());
126 nsresult
CloseConnection(uint16_t reasonCode
,
127 const nsACString
& aReasonString
= EmptyCString());
128 nsresult
Disconnect();
129 void DisconnectInternal();
131 nsresult
ConsoleError();
132 nsresult
PrintErrorOnConsole(const char* aBundleURI
,
133 const char16_t
* aError
,
134 const char16_t
** aFormatStrings
,
135 uint32_t aFormatStringsLen
);
137 nsresult
DoOnMessageAvailable(const nsACString
& aMsg
,
140 // ConnectionCloseEvents: 'error' event if needed, then 'close' event.
141 // - These must not be dispatched while we are still within an incoming call
142 // from JS (ex: close()). Set 'sync' to false in that case to dispatch in a
143 // separate new event.
144 nsresult
ScheduleConnectionCloseEvents(nsISupports
* aContext
,
145 nsresult aStatusCode
,
147 // 2nd half of ScheduleConnectionCloseEvents, sometimes run in its own event.
148 void DispatchConnectionCloseEvents();
150 // Dispatch a runnable to the right thread.
151 nsresult
DispatchRunnable(nsIRunnable
* aRunnable
);
153 nsresult
UpdateURI();
156 void ReleaseObject();
158 bool RegisterFeature();
159 void UnregisterFeature();
161 nsresult
CancelInternal();
163 nsRefPtr
<WebSocket
> mWebSocket
;
165 nsCOMPtr
<nsIWebSocketChannel
> mChannel
;
167 bool mSecure
; // if true it is using SSL and the wss scheme,
168 // otherwise it is using the ws scheme with no SSL
170 bool mOnCloseScheduled
;
172 bool mDisconnectingOrDisconnected
;
174 // Set attributes of DOM 'onclose' message
175 bool mCloseEventWasClean
;
176 nsString mCloseEventReason
;
177 uint16_t mCloseEventCode
;
179 nsCString mAsciiHost
; // hostname
181 nsCString mResource
; // [filepath[?query]]
182 nsString mUTF16Origin
;
185 nsCString mRequestedProtocolList
;
187 nsCOMPtr
<nsIPrincipal
> mPrincipal
;
188 nsWeakPtr mOriginDocument
;
190 // Web Socket owner information:
191 // - the script file name, UTF8 encoded.
192 // - source code line number where the Web Socket object was constructed.
193 // - the ID of the inner window where the script lives. Note that this may not
194 // be the same as the Web Socket owner window.
195 // These attributes are used for error reporting.
196 nsCString mScriptFile
;
197 uint32_t mScriptLine
;
198 uint64_t mInnerWindowID
;
200 WorkerPrivate
* mWorkerPrivate
;
201 nsAutoPtr
<WorkerFeature
> mWorkerFeature
;
204 // This is protected by mutex.
205 bool mHasFeatureRegistered
;
207 bool HasFeatureRegistered()
209 MOZ_ASSERT(mWebSocket
);
210 MutexAutoLock
lock(mWebSocket
->mMutex
);
211 return mHasFeatureRegistered
;
214 void SetHasFeatureRegistered(bool aValue
)
216 MOZ_ASSERT(mWebSocket
);
217 MutexAutoLock
lock(mWebSocket
->mMutex
);
218 mHasFeatureRegistered
= aValue
;
222 nsWeakPtr mWeakLoadGroup
;
225 bool mWorkerShuttingDown
;
230 // If we threw during Init we never called disconnect
231 if (!mDisconnectingOrDisconnected
) {
237 NS_IMPL_ISUPPORTS(WebSocketImpl
,
238 nsIInterfaceRequestor
,
239 nsIWebSocketListener
,
241 nsISupportsWeakReference
,
245 class CallDispatchConnectionCloseEvents MOZ_FINAL
: public nsCancelableRunnable
248 explicit CallDispatchConnectionCloseEvents(WebSocketImpl
* aWebSocketImpl
)
249 : mWebSocketImpl(aWebSocketImpl
)
251 aWebSocketImpl
->AssertIsOnTargetThread();
256 mWebSocketImpl
->AssertIsOnTargetThread();
257 mWebSocketImpl
->DispatchConnectionCloseEvents();
262 nsRefPtr
<WebSocketImpl
> mWebSocketImpl
;
265 //-----------------------------------------------------------------------------
267 //-----------------------------------------------------------------------------
271 class PrintErrorOnConsoleRunnable MOZ_FINAL
: public WorkerMainThreadRunnable
274 PrintErrorOnConsoleRunnable(WebSocketImpl
* aImpl
,
275 const char* aBundleURI
,
276 const char16_t
* aError
,
277 const char16_t
** aFormatStrings
,
278 uint32_t aFormatStringsLen
)
279 : WorkerMainThreadRunnable(aImpl
->mWorkerPrivate
)
281 , mBundleURI(aBundleURI
)
283 , mFormatStrings(aFormatStrings
)
284 , mFormatStringsLen(aFormatStringsLen
)
285 , mRv(NS_ERROR_FAILURE
)
288 bool MainThreadRun() MOZ_OVERRIDE
290 mRv
= mImpl
->PrintErrorOnConsole(mBundleURI
, mError
, mFormatStrings
,
295 nsresult
ErrorCode() const
301 // Raw pointer because this runnable is sync.
302 WebSocketImpl
* mImpl
;
304 const char* mBundleURI
;
305 const char16_t
* mError
;
306 const char16_t
** mFormatStrings
;
307 uint32_t mFormatStringsLen
;
311 } // anonymous namespace
314 WebSocketImpl::PrintErrorOnConsole(const char *aBundleURI
,
315 const char16_t
*aError
,
316 const char16_t
**aFormatStrings
,
317 uint32_t aFormatStringsLen
)
319 // This method must run on the main thread.
321 if (!NS_IsMainThread()) {
322 MOZ_ASSERT(mWorkerPrivate
);
324 nsRefPtr
<PrintErrorOnConsoleRunnable
> runnable
=
325 new PrintErrorOnConsoleRunnable(this, aBundleURI
, aError
, aFormatStrings
,
327 runnable
->Dispatch(mWorkerPrivate
->GetJSContext());
328 return runnable
->ErrorCode();
332 nsCOMPtr
<nsIStringBundleService
> bundleService
=
333 do_GetService(NS_STRINGBUNDLE_CONTRACTID
, &rv
);
334 NS_ENSURE_SUCCESS(rv
, rv
);
336 nsCOMPtr
<nsIStringBundle
> strBundle
;
337 rv
= bundleService
->CreateBundle(aBundleURI
, getter_AddRefs(strBundle
));
338 NS_ENSURE_SUCCESS(rv
, rv
);
340 nsCOMPtr
<nsIConsoleService
> console(
341 do_GetService(NS_CONSOLESERVICE_CONTRACTID
, &rv
));
342 NS_ENSURE_SUCCESS(rv
, rv
);
344 nsCOMPtr
<nsIScriptError
> errorObject(
345 do_CreateInstance(NS_SCRIPTERROR_CONTRACTID
, &rv
));
346 NS_ENSURE_SUCCESS(rv
, rv
);
348 // Localize the error message
349 nsXPIDLString message
;
350 if (aFormatStrings
) {
351 rv
= strBundle
->FormatStringFromName(aError
, aFormatStrings
,
353 getter_Copies(message
));
355 rv
= strBundle
->GetStringFromName(aError
, getter_Copies(message
));
357 NS_ENSURE_SUCCESS(rv
, rv
);
359 if (mInnerWindowID
) {
360 rv
= errorObject
->InitWithWindowID(message
,
361 NS_ConvertUTF8toUTF16(mScriptFile
),
362 EmptyString(), mScriptLine
, 0,
363 nsIScriptError::errorFlag
, "Web Socket",
366 rv
= errorObject
->Init(message
,
367 NS_ConvertUTF8toUTF16(mScriptFile
),
368 EmptyString(), mScriptLine
, 0,
369 nsIScriptError::errorFlag
, "Web Socket");
372 NS_ENSURE_SUCCESS(rv
, rv
);
374 // print the error message directly to the JS console
375 rv
= console
->LogMessage(errorObject
);
376 NS_ENSURE_SUCCESS(rv
, rv
);
383 class CloseRunnable MOZ_FINAL
: public WorkerMainThreadRunnable
386 CloseRunnable(WebSocketImpl
* aImpl
, uint16_t aReasonCode
,
387 const nsACString
& aReasonString
)
388 : WorkerMainThreadRunnable(aImpl
->mWorkerPrivate
)
390 , mReasonCode(aReasonCode
)
391 , mReasonString(aReasonString
)
392 , mRv(NS_ERROR_FAILURE
)
395 bool MainThreadRun() MOZ_OVERRIDE
397 mRv
= mImpl
->mChannel
->Close(mReasonCode
, mReasonString
);
401 nsresult
ErrorCode() const
407 // A raw pointer because this runnable is sync.
408 WebSocketImpl
* mImpl
;
410 uint16_t mReasonCode
;
411 const nsACString
& mReasonString
;
415 class MOZ_STACK_CLASS MaybeDisconnect
418 explicit MaybeDisconnect(WebSocketImpl
* aImpl
)
425 if (mImpl
->mWorkerShuttingDown
) {
431 WebSocketImpl
* mImpl
;
434 } // anonymous namespace
437 WebSocketImpl::CloseConnection(uint16_t aReasonCode
,
438 const nsACString
& aReasonString
)
440 AssertIsOnTargetThread();
442 if (mDisconnectingOrDisconnected
) {
446 // If this method is called because the worker is going away, we will not
447 // receive the OnStop() method and we have to disconnect the WebSocket and
448 // release the WorkerFeature.
449 MaybeDisconnect
md(this);
451 uint16_t readyState
= mWebSocket
->ReadyState();
452 if (readyState
== WebSocket::CLOSING
||
453 readyState
== WebSocket::CLOSED
) {
457 // The common case...
459 mWebSocket
->SetReadyState(WebSocket::CLOSING
);
461 // The channel has to be closed on the main-thread.
463 if (NS_IsMainThread()) {
464 return mChannel
->Close(aReasonCode
, aReasonString
);
467 nsRefPtr
<CloseRunnable
> runnable
=
468 new CloseRunnable(this, aReasonCode
, aReasonString
);
469 runnable
->Dispatch(mWorkerPrivate
->GetJSContext());
470 return runnable
->ErrorCode();
473 // No channel, but not disconnected: canceled or failed early
475 MOZ_ASSERT(readyState
== WebSocket::CONNECTING
,
476 "Should only get here for early websocket cancel/error");
478 // Server won't be sending us a close code, so use what's passed in here.
479 mCloseEventCode
= aReasonCode
;
480 CopyUTF8toUTF16(aReasonString
, mCloseEventReason
);
482 mWebSocket
->SetReadyState(WebSocket::CLOSING
);
484 // Can be called from Cancel() or Init() codepaths, so need to dispatch
485 // onerror/onclose asynchronously
486 ScheduleConnectionCloseEvents(
488 (aReasonCode
== nsIWebSocketChannel::CLOSE_NORMAL
||
489 aReasonCode
== nsIWebSocketChannel::CLOSE_GOING_AWAY
) ?
490 NS_OK
: NS_ERROR_FAILURE
,
497 WebSocketImpl::ConsoleError()
499 AssertIsOnTargetThread();
501 NS_ConvertUTF8toUTF16
specUTF16(mURI
);
502 const char16_t
* formatStrings
[] = { specUTF16
.get() };
504 if (mWebSocket
->ReadyState() < WebSocket::OPEN
) {
505 PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
506 MOZ_UTF16("connectionFailure"),
507 formatStrings
, ArrayLength(formatStrings
));
509 PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
510 MOZ_UTF16("netInterrupt"),
511 formatStrings
, ArrayLength(formatStrings
));
513 /// todo some specific errors - like for message too large
518 WebSocketImpl::FailConnection(uint16_t aReasonCode
,
519 const nsACString
& aReasonString
)
521 AssertIsOnTargetThread();
523 if (mDisconnectingOrDisconnected
) {
529 CloseConnection(aReasonCode
, aReasonString
);
534 class DisconnectInternalRunnable MOZ_FINAL
: public WorkerMainThreadRunnable
537 explicit DisconnectInternalRunnable(WebSocketImpl
* aImpl
)
538 : WorkerMainThreadRunnable(aImpl
->mWorkerPrivate
)
542 bool MainThreadRun() MOZ_OVERRIDE
544 mImpl
->DisconnectInternal();
549 // A raw pointer because this runnable is sync.
550 WebSocketImpl
* mImpl
;
553 } // anonymous namespace
556 WebSocketImpl::Disconnect()
558 if (mDisconnectingOrDisconnected
) {
562 AssertIsOnTargetThread();
564 // Disconnect can be called from some control event (such as Notify() of
565 // WorkerFeature). This will be schedulated before any other sync/async
566 // runnable. In order to prevent some double Disconnect() calls, we use this
568 mDisconnectingOrDisconnected
= true;
570 // DisconnectInternal touches observers and nsILoadGroup and it must run on
573 if (NS_IsMainThread()) {
574 DisconnectInternal();
576 nsRefPtr
<DisconnectInternalRunnable
> runnable
=
577 new DisconnectInternalRunnable(this);
578 runnable
->Dispatch(mWorkerPrivate
->GetJSContext());
581 // DontKeepAliveAnyMore() can release the object. So hold a reference to this
582 // until the end of the method.
583 nsRefPtr
<WebSocketImpl
> kungfuDeathGrip
= this;
585 nsCOMPtr
<nsIThread
> mainThread
;
586 if (NS_FAILED(NS_GetMainThread(getter_AddRefs(mainThread
))) ||
587 NS_FAILED(NS_ProxyRelease(mainThread
, mChannel
))) {
588 NS_WARNING("Failed to proxy release of channel, leaking instead!");
591 mWebSocket
->DontKeepAliveAnyMore();
592 mWebSocket
->mImpl
= nullptr;
594 if (mWorkerPrivate
&& mWorkerFeature
) {
598 // We want to release the WebSocket in the correct thread.
599 mWebSocket
= nullptr;
605 WebSocketImpl::DisconnectInternal()
607 AssertIsOnMainThread();
609 nsCOMPtr
<nsILoadGroup
> loadGroup
= do_QueryReferent(mWeakLoadGroup
);
611 loadGroup
->RemoveRequest(this, nullptr, NS_OK
);
612 // mWeakLoadGroup has to be release on main-thread because WeakReferences
613 // are not thread-safe.
614 mWeakLoadGroup
= nullptr;
617 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
619 os
->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC
);
620 os
->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC
);
624 //-----------------------------------------------------------------------------
625 // WebSocketImpl::nsIWebSocketListener methods:
626 //-----------------------------------------------------------------------------
629 WebSocketImpl::DoOnMessageAvailable(const nsACString
& aMsg
, bool isBinary
)
631 AssertIsOnTargetThread();
633 if (mDisconnectingOrDisconnected
) {
637 int16_t readyState
= mWebSocket
->ReadyState();
638 if (readyState
== WebSocket::CLOSED
) {
639 NS_ERROR("Received message after CLOSED");
640 return NS_ERROR_UNEXPECTED
;
643 if (readyState
== WebSocket::OPEN
) {
644 // Dispatch New Message
645 nsresult rv
= mWebSocket
->CreateAndDispatchMessageEvent(aMsg
, isBinary
);
647 NS_WARNING("Failed to dispatch the message event");
653 // CLOSING should be the only other state where it's possible to get msgs
654 // from channel: Spec says to drop them.
655 MOZ_ASSERT(readyState
== WebSocket::CLOSING
,
656 "Received message while CONNECTING or CLOSED");
661 WebSocketImpl::OnMessageAvailable(nsISupports
* aContext
,
662 const nsACString
& aMsg
)
664 AssertIsOnTargetThread();
666 if (mDisconnectingOrDisconnected
) {
670 return DoOnMessageAvailable(aMsg
, false);
674 WebSocketImpl::OnBinaryMessageAvailable(nsISupports
* aContext
,
675 const nsACString
& aMsg
)
677 AssertIsOnTargetThread();
679 if (mDisconnectingOrDisconnected
) {
683 return DoOnMessageAvailable(aMsg
, true);
687 WebSocketImpl::OnStart(nsISupports
* aContext
)
689 AssertIsOnTargetThread();
691 if (mDisconnectingOrDisconnected
) {
695 int16_t readyState
= mWebSocket
->ReadyState();
697 // This is the only function that sets OPEN, and should be called only once
698 MOZ_ASSERT(readyState
!= WebSocket::OPEN
,
699 "readyState already OPEN! OnStart called twice?");
701 // Nothing to do if we've already closed/closing
702 if (readyState
!= WebSocket::CONNECTING
) {
706 // Attempt to kill "ghost" websocket: but usually too early for check to fail
707 nsresult rv
= mWebSocket
->CheckInnerWindowCorrectness();
709 CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY
);
713 if (!mRequestedProtocolList
.IsEmpty()) {
714 mChannel
->GetProtocol(mWebSocket
->mEstablishedProtocol
);
717 mChannel
->GetExtensions(mWebSocket
->mEstablishedExtensions
);
720 mWebSocket
->SetReadyState(WebSocket::OPEN
);
722 // Let's keep the object alive because the webSocket can be CCed in the
724 nsRefPtr
<WebSocket
> webSocket
= mWebSocket
;
727 rv
= webSocket
->CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("open"));
729 NS_WARNING("Failed to dispatch the open event");
732 webSocket
->UpdateMustKeepAlive();
737 WebSocketImpl::OnStop(nsISupports
* aContext
, nsresult aStatusCode
)
739 AssertIsOnTargetThread();
741 if (mDisconnectingOrDisconnected
) {
745 // We can be CONNECTING here if connection failed.
746 // We can be OPEN if we have encountered a fatal protocol error
747 // We can be CLOSING if close() was called and/or server initiated close.
748 MOZ_ASSERT(mWebSocket
->ReadyState() != WebSocket::CLOSED
,
749 "Shouldn't already be CLOSED when OnStop called");
751 // called by network stack, not JS, so can dispatch JS events synchronously
752 return ScheduleConnectionCloseEvents(aContext
, aStatusCode
, true);
756 WebSocketImpl::ScheduleConnectionCloseEvents(nsISupports
* aContext
,
757 nsresult aStatusCode
,
760 AssertIsOnTargetThread();
762 // no-op if some other code has already initiated close event
763 if (!mOnCloseScheduled
) {
764 mCloseEventWasClean
= NS_SUCCEEDED(aStatusCode
);
766 if (aStatusCode
== NS_BASE_STREAM_CLOSED
) {
767 // don't generate an error event just because of an unclean close
771 if (NS_FAILED(aStatusCode
)) {
776 mOnCloseScheduled
= true;
779 DispatchConnectionCloseEvents();
781 NS_DispatchToCurrentThread(new CallDispatchConnectionCloseEvents(this));
789 WebSocketImpl::OnAcknowledge(nsISupports
*aContext
, uint32_t aSize
)
791 AssertIsOnTargetThread();
793 if (mDisconnectingOrDisconnected
) {
797 if (aSize
> mWebSocket
->mOutgoingBufferedAmount
) {
798 return NS_ERROR_UNEXPECTED
;
801 mWebSocket
->mOutgoingBufferedAmount
-= aSize
;
806 WebSocketImpl::OnServerClose(nsISupports
*aContext
, uint16_t aCode
,
807 const nsACString
&aReason
)
809 AssertIsOnTargetThread();
811 if (mDisconnectingOrDisconnected
) {
815 int16_t readyState
= mWebSocket
->ReadyState();
817 MOZ_ASSERT(readyState
!= WebSocket::CONNECTING
,
818 "Received server close before connected?");
819 MOZ_ASSERT(readyState
!= WebSocket::CLOSED
,
820 "Received server close after already closed!");
822 // store code/string for onclose DOM event
823 mCloseEventCode
= aCode
;
824 CopyUTF8toUTF16(aReason
, mCloseEventReason
);
826 if (readyState
== WebSocket::OPEN
) {
827 // Server initiating close.
828 // RFC 6455, 5.5.1: "When sending a Close frame in response, the endpoint
829 // typically echos the status code it received".
830 // But never send certain codes, per section 7.4.1
831 if (aCode
== 1005 || aCode
== 1006 || aCode
== 1015) {
832 CloseConnection(0, EmptyCString());
834 CloseConnection(aCode
, aReason
);
837 // We initiated close, and server has replied: OnStop does rest of the work.
838 MOZ_ASSERT(readyState
== WebSocket::CLOSING
, "unknown state");
844 //-----------------------------------------------------------------------------
845 // WebSocketImpl::nsIInterfaceRequestor
846 //-----------------------------------------------------------------------------
849 WebSocketImpl::GetInterface(const nsIID
& aIID
, void** aResult
)
851 AssertIsOnMainThread();
853 if (!mWebSocket
|| mWebSocket
->ReadyState() == WebSocket::CLOSED
) {
854 return NS_ERROR_FAILURE
;
857 if (aIID
.Equals(NS_GET_IID(nsIAuthPrompt
)) ||
858 aIID
.Equals(NS_GET_IID(nsIAuthPrompt2
))) {
860 nsIScriptContext
* sc
= mWebSocket
->GetContextForEventHandlers(&rv
);
861 nsCOMPtr
<nsIDocument
> doc
=
862 nsContentUtils::GetDocumentFromScriptContext(sc
);
864 return NS_ERROR_NOT_AVAILABLE
;
867 nsCOMPtr
<nsIPromptFactory
> wwatch
=
868 do_GetService(NS_WINDOWWATCHER_CONTRACTID
, &rv
);
869 NS_ENSURE_SUCCESS(rv
, rv
);
871 nsCOMPtr
<nsPIDOMWindow
> outerWindow
= doc
->GetWindow();
872 return wwatch
->GetPrompt(outerWindow
, aIID
, aResult
);
875 return QueryInterface(aIID
, aResult
);
878 ////////////////////////////////////////////////////////////////////////////////
880 ////////////////////////////////////////////////////////////////////////////////
882 WebSocket::WebSocket(nsPIDOMWindow
* aOwnerWindow
)
883 : DOMEventTargetHelper(aOwnerWindow
)
884 , mIsMainThread(true)
885 , mKeepingAlive(false)
886 , mCheckMustKeepAlive(true)
887 , mOutgoingBufferedAmount(0)
888 , mBinaryType(dom::BinaryType::Blob
)
889 , mMutex("WebSocketImpl::mMutex")
890 , mReadyState(CONNECTING
)
892 mImpl
= new WebSocketImpl(this);
893 mIsMainThread
= mImpl
->mIsMainThread
;
896 WebSocket::~WebSocket()
901 WebSocket::WrapObject(JSContext
* cx
)
903 return WebSocketBinding::Wrap(cx
, this);
906 //---------------------------------------------------------------------------
908 //---------------------------------------------------------------------------
911 already_AddRefed
<WebSocket
>
912 WebSocket::Constructor(const GlobalObject
& aGlobal
,
913 const nsAString
& aUrl
,
916 Sequence
<nsString
> protocols
;
917 return WebSocket::Constructor(aGlobal
, aUrl
, protocols
, aRv
);
920 already_AddRefed
<WebSocket
>
921 WebSocket::Constructor(const GlobalObject
& aGlobal
,
922 const nsAString
& aUrl
,
923 const nsAString
& aProtocol
,
926 Sequence
<nsString
> protocols
;
927 protocols
.AppendElement(aProtocol
);
928 return WebSocket::Constructor(aGlobal
, aUrl
, protocols
, aRv
);
933 // This class is used to clear any exception.
934 class MOZ_STACK_CLASS ClearException
937 explicit ClearException(JSContext
* aCx
)
944 JS_ClearPendingException(mCx
);
951 class InitRunnable MOZ_FINAL
: public WorkerMainThreadRunnable
954 InitRunnable(WebSocketImpl
* aImpl
, const nsAString
& aURL
,
955 nsTArray
<nsString
>& aProtocolArray
,
956 const nsACString
& aScriptFile
, uint32_t aScriptLine
,
957 ErrorResult
& aRv
, bool* aConnectionFailed
)
958 : WorkerMainThreadRunnable(aImpl
->mWorkerPrivate
)
961 , mProtocolArray(aProtocolArray
)
962 , mScriptFile(aScriptFile
)
963 , mScriptLine(aScriptLine
)
965 , mConnectionFailed(aConnectionFailed
)
967 MOZ_ASSERT(mWorkerPrivate
);
968 mWorkerPrivate
->AssertIsOnWorkerThread();
971 bool MainThreadRun() MOZ_OVERRIDE
973 AssertIsOnMainThread();
975 // Walk up to our containing page
976 WorkerPrivate
* wp
= mWorkerPrivate
;
977 while (wp
->GetParent()) {
978 wp
= wp
->GetParent();
981 nsPIDOMWindow
* window
= wp
->GetWindow();
983 return InitWithWindow(window
);
986 return InitWindowless();
990 bool InitWithWindow(nsPIDOMWindow
* aWindow
)
993 if (NS_WARN_IF(!jsapi
.Init(aWindow
))) {
994 mRv
.Throw(NS_ERROR_FAILURE
);
998 ClearException
ce(jsapi
.cx());
1000 nsIDocument
* doc
= aWindow
->GetExtantDoc();
1002 mRv
.Throw(NS_ERROR_FAILURE
);
1006 nsCOMPtr
<nsIPrincipal
> principal
= doc
->NodePrincipal();
1008 mRv
.Throw(NS_ERROR_FAILURE
);
1012 mImpl
->Init(jsapi
.cx(), principal
, mURL
, mProtocolArray
, mScriptFile
,
1013 mScriptLine
, mRv
, mConnectionFailed
);
1017 bool InitWindowless()
1019 MOZ_ASSERT(NS_IsMainThread());
1021 WorkerPrivate
* wp
= mWorkerPrivate
;
1022 while (wp
->GetParent()) {
1023 wp
= wp
->GetParent();
1026 MOZ_ASSERT(!wp
->GetWindow());
1028 mImpl
->Init(nullptr, wp
->GetPrincipal(), mURL
, mProtocolArray
, mScriptFile
,
1029 mScriptLine
, mRv
, mConnectionFailed
);
1033 // Raw pointer. This worker runs synchronously.
1034 WebSocketImpl
* mImpl
;
1036 const nsAString
& mURL
;
1037 nsTArray
<nsString
>& mProtocolArray
;
1038 nsCString mScriptFile
;
1039 uint32_t mScriptLine
;
1041 bool* mConnectionFailed
;
1044 class AsyncOpenRunnable MOZ_FINAL
: public WorkerMainThreadRunnable
1047 AsyncOpenRunnable(WebSocketImpl
* aImpl
, ErrorResult
& aRv
)
1048 : WorkerMainThreadRunnable(aImpl
->mWorkerPrivate
)
1052 MOZ_ASSERT(mWorkerPrivate
);
1053 mWorkerPrivate
->AssertIsOnWorkerThread();
1056 bool MainThreadRun() MOZ_OVERRIDE
1058 AssertIsOnMainThread();
1059 mImpl
->AsyncOpen(mRv
);
1064 // Raw pointer. This worker runs synchronously.
1065 WebSocketImpl
* mImpl
;
1070 } // anonymous namespace
1072 already_AddRefed
<WebSocket
>
1073 WebSocket::Constructor(const GlobalObject
& aGlobal
,
1074 const nsAString
& aUrl
,
1075 const Sequence
<nsString
>& aProtocols
,
1078 nsCOMPtr
<nsIPrincipal
> principal
;
1079 nsCOMPtr
<nsPIDOMWindow
> ownerWindow
;
1081 if (NS_IsMainThread()) {
1082 nsCOMPtr
<nsIScriptObjectPrincipal
> scriptPrincipal
=
1083 do_QueryInterface(aGlobal
.GetAsSupports());
1084 if (!scriptPrincipal
) {
1085 aRv
.Throw(NS_ERROR_FAILURE
);
1089 principal
= scriptPrincipal
->GetPrincipal();
1091 aRv
.Throw(NS_ERROR_FAILURE
);
1095 nsCOMPtr
<nsIScriptGlobalObject
> sgo
=
1096 do_QueryInterface(aGlobal
.GetAsSupports());
1098 aRv
.Throw(NS_ERROR_FAILURE
);
1102 ownerWindow
= do_QueryInterface(aGlobal
.GetAsSupports());
1104 aRv
.Throw(NS_ERROR_FAILURE
);
1109 nsTArray
<nsString
> protocolArray
;
1111 for (uint32_t index
= 0, len
= aProtocols
.Length(); index
< len
; ++index
) {
1113 const nsString
& protocolElement
= aProtocols
[index
];
1115 if (protocolElement
.IsEmpty()) {
1116 aRv
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
1119 if (protocolArray
.Contains(protocolElement
)) {
1120 aRv
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
1123 if (protocolElement
.FindChar(',') != -1) /* interferes w/list */ {
1124 aRv
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
1128 protocolArray
.AppendElement(protocolElement
);
1131 nsRefPtr
<WebSocket
> webSocket
= new WebSocket(ownerWindow
);
1132 nsRefPtr
<WebSocketImpl
> kungfuDeathGrip
= webSocket
->mImpl
;
1134 bool connectionFailed
= true;
1136 if (NS_IsMainThread()) {
1137 webSocket
->mImpl
->Init(aGlobal
.Context(), principal
, aUrl
, protocolArray
,
1138 EmptyCString(), 0, aRv
, &connectionFailed
);
1140 // In workers we have to keep the worker alive using a feature in order to
1141 // dispatch messages correctly.
1142 if (!webSocket
->mImpl
->RegisterFeature()) {
1143 aRv
.Throw(NS_ERROR_FAILURE
);
1148 JS::AutoFilename file
;
1149 if (!JS::DescribeScriptedCaller(aGlobal
.Context(), &file
, &lineno
)) {
1150 NS_WARNING("Failed to get line number and filename in workers.");
1153 nsRefPtr
<InitRunnable
> runnable
=
1154 new InitRunnable(webSocket
->mImpl
, aUrl
, protocolArray
,
1155 nsAutoCString(file
.get()), lineno
, aRv
,
1157 runnable
->Dispatch(aGlobal
.Context());
1160 if (NS_WARN_IF(aRv
.Failed())) {
1164 // It can be that we have been already disconnected because the WebSocket is
1165 // gone away while we where initializing the webSocket.
1166 if (!webSocket
->mImpl
) {
1167 aRv
.Throw(NS_ERROR_FAILURE
);
1171 // We don't return an error if the connection just failed. Instead we dispatch
1173 if (connectionFailed
) {
1174 webSocket
->mImpl
->FailConnection(nsIWebSocketChannel::CLOSE_ABNORMAL
);
1177 // If we don't have a channel, the connection is failed and onerror() will be
1178 // called asynchrounsly.
1179 if (!webSocket
->mImpl
->mChannel
) {
1180 return webSocket
.forget();
1183 class MOZ_STACK_CLASS ClearWebSocket
1186 explicit ClearWebSocket(WebSocketImpl
* aWebSocketImpl
)
1187 : mWebSocketImpl(aWebSocketImpl
)
1200 mWebSocketImpl
->mChannel
= nullptr;
1201 mWebSocketImpl
->FailConnection(nsIWebSocketChannel::CLOSE_ABNORMAL
);
1205 WebSocketImpl
* mWebSocketImpl
;
1209 ClearWebSocket
cws(webSocket
->mImpl
);
1211 // This operation must be done on the correct thread. The rest must run on the
1213 aRv
= webSocket
->mImpl
->mChannel
->SetNotificationCallbacks(webSocket
->mImpl
);
1214 if (NS_WARN_IF(aRv
.Failed())) {
1218 if (NS_IsMainThread()) {
1219 webSocket
->mImpl
->AsyncOpen(aRv
);
1221 nsRefPtr
<AsyncOpenRunnable
> runnable
=
1222 new AsyncOpenRunnable(webSocket
->mImpl
, aRv
);
1223 runnable
->Dispatch(aGlobal
.Context());
1226 if (NS_WARN_IF(aRv
.Failed())) {
1230 // It can be that we have been already disconnected because the WebSocket is
1231 // gone away while we where initializing the webSocket.
1232 if (!webSocket
->mImpl
) {
1233 aRv
.Throw(NS_ERROR_FAILURE
);
1238 return webSocket
.forget();
1241 NS_IMPL_CYCLE_COLLECTION_CLASS(WebSocket
)
1243 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(WebSocket
)
1244 bool isBlack
= tmp
->IsBlack();
1245 if (isBlack
|| tmp
->mKeepingAlive
) {
1246 if (tmp
->mListenerManager
) {
1247 tmp
->mListenerManager
->MarkForCC();
1249 if (!isBlack
&& tmp
->PreservingWrapper()) {
1250 // This marks the wrapper black.
1255 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
1257 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(WebSocket
)
1258 return tmp
->IsBlack();
1259 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
1261 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(WebSocket
)
1262 return tmp
->IsBlack();
1263 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
1265 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WebSocket
,
1266 DOMEventTargetHelper
)
1267 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1269 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WebSocket
,
1270 DOMEventTargetHelper
)
1272 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImpl
->mPrincipal
)
1273 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImpl
->mChannel
)
1275 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1277 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WebSocket
,
1278 DOMEventTargetHelper
)
1280 NS_IMPL_CYCLE_COLLECTION_UNLINK(mImpl
->mPrincipal
)
1281 NS_IMPL_CYCLE_COLLECTION_UNLINK(mImpl
->mChannel
)
1282 tmp
->mImpl
->Disconnect();
1283 MOZ_ASSERT(!tmp
->mImpl
);
1285 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1287 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WebSocket
)
1288 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper
)
1290 NS_IMPL_ADDREF_INHERITED(WebSocket
, DOMEventTargetHelper
)
1291 NS_IMPL_RELEASE_INHERITED(WebSocket
, DOMEventTargetHelper
)
1294 WebSocket::DisconnectFromOwner()
1296 AssertIsOnMainThread();
1297 DOMEventTargetHelper::DisconnectFromOwner();
1300 mImpl
->CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY
);
1303 DontKeepAliveAnyMore();
1306 //-----------------------------------------------------------------------------
1307 // WebSocketImpl:: initialization
1308 //-----------------------------------------------------------------------------
1311 WebSocketImpl::Init(JSContext
* aCx
,
1312 nsIPrincipal
* aPrincipal
,
1313 const nsAString
& aURL
,
1314 nsTArray
<nsString
>& aProtocolArray
,
1315 const nsACString
& aScriptFile
,
1316 uint32_t aScriptLine
,
1318 bool* aConnectionFailed
)
1320 AssertIsOnMainThread();
1321 MOZ_ASSERT(aPrincipal
);
1323 // We need to keep the implementation alive in case the init disconnects it
1324 // because of some error.
1325 nsRefPtr
<WebSocketImpl
> kungfuDeathGrip
= this;
1327 mPrincipal
= aPrincipal
;
1329 // Attempt to kill "ghost" websocket: but usually too early for check to fail
1330 aRv
= mWebSocket
->CheckInnerWindowCorrectness();
1331 if (NS_WARN_IF(aRv
.Failed())) {
1335 // Shut down websocket if window is frozen or destroyed (only needed for
1336 // "ghost" websockets--see bug 696085)
1337 if (!mWorkerPrivate
) {
1338 nsCOMPtr
<nsIObserverService
> os
= mozilla::services::GetObserverService();
1339 if (NS_WARN_IF(!os
)) {
1340 aRv
.Throw(NS_ERROR_FAILURE
);
1344 aRv
= os
->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC
, true);
1345 if (NS_WARN_IF(aRv
.Failed())) {
1349 aRv
= os
->AddObserver(this, DOM_WINDOW_FROZEN_TOPIC
, true);
1350 if (NS_WARN_IF(aRv
.Failed())) {
1355 if (mWorkerPrivate
) {
1356 mScriptFile
= aScriptFile
;
1357 mScriptLine
= aScriptLine
;
1362 JS::AutoFilename file
;
1363 if (JS::DescribeScriptedCaller(aCx
, &file
, &lineno
)) {
1364 mScriptFile
= file
.get();
1365 mScriptLine
= lineno
;
1369 // If we don't have aCx, we are window-less, so we don't have a
1370 // inner-windowID. This can happen in sharedWorkers and ServiceWorkers or in
1371 // DedicateWorkers created by JSM.
1373 mInnerWindowID
= nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(aCx
);
1377 aRv
= ParseURL(PromiseFlatString(aURL
));
1378 if (NS_WARN_IF(aRv
.Failed())) {
1382 nsIScriptContext
* sc
= nullptr;
1385 sc
= mWebSocket
->GetContextForEventHandlers(&rv
);
1386 if (NS_WARN_IF(NS_FAILED(rv
))) {
1392 // Don't allow https:// to open ws://
1394 !Preferences::GetBool("network.websocket.allowInsecureFromHTTPS",
1396 // Confirmed we are opening plain ws:// and want to prevent this from a
1397 // secure context (e.g. https). Check the principal's uri to determine if
1398 // we were loaded from https.
1399 nsCOMPtr
<nsIGlobalObject
> globalObject(GetEntryGlobal());
1401 nsCOMPtr
<nsIPrincipal
> principal(globalObject
->PrincipalOrNull());
1403 nsCOMPtr
<nsIURI
> uri
;
1404 principal
->GetURI(getter_AddRefs(uri
));
1406 bool originIsHttps
= false;
1407 aRv
= uri
->SchemeIs("https", &originIsHttps
);
1408 if (NS_WARN_IF(aRv
.Failed())) {
1412 if (originIsHttps
) {
1413 aRv
.Throw(NS_ERROR_DOM_SECURITY_ERR
);
1421 // Assign the sub protocol list and scan it for illegal values
1422 for (uint32_t index
= 0; index
< aProtocolArray
.Length(); ++index
) {
1423 for (uint32_t i
= 0; i
< aProtocolArray
[index
].Length(); ++i
) {
1424 if (aProtocolArray
[index
][i
] < static_cast<char16_t
>(0x0021) ||
1425 aProtocolArray
[index
][i
] > static_cast<char16_t
>(0x007E)) {
1426 aRv
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
1431 if (!mRequestedProtocolList
.IsEmpty()) {
1432 mRequestedProtocolList
.AppendLiteral(", ");
1435 AppendUTF16toUTF8(aProtocolArray
[index
], mRequestedProtocolList
);
1438 nsCOMPtr
<nsIURI
> uri
;
1440 nsresult rv
= NS_NewURI(getter_AddRefs(uri
), mURI
);
1442 // We crash here because we are sure that mURI is a valid URI, so either we
1443 // are OOM'ing or something else bad is happening.
1444 if (NS_FAILED(rv
)) {
1449 // Check content policy.
1450 int16_t shouldLoad
= nsIContentPolicy::ACCEPT
;
1451 nsCOMPtr
<nsIDocument
> originDoc
= nsContentUtils::GetDocumentFromScriptContext(sc
);
1452 mOriginDocument
= do_GetWeakReference(originDoc
);
1453 aRv
= NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_WEBSOCKET
,
1460 nsContentUtils::GetContentPolicy(),
1461 nsContentUtils::GetSecurityManager());
1462 if (NS_WARN_IF(aRv
.Failed())) {
1466 if (NS_CP_REJECTED(shouldLoad
)) {
1467 // Disallowed by content policy.
1468 aRv
.Throw(NS_ERROR_CONTENT_BLOCKED
);
1472 // the constructor should throw a SYNTAX_ERROR only if it fails to parse the
1473 // url parameter, so don't throw if InitializeConnection fails, and call
1474 // onerror/onclose asynchronously
1475 if (NS_FAILED(InitializeConnection())) {
1476 *aConnectionFailed
= true;
1478 *aConnectionFailed
= false;
1483 WebSocketImpl::AsyncOpen(ErrorResult
& aRv
)
1485 NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
1487 nsCString asciiOrigin
;
1488 aRv
= nsContentUtils::GetASCIIOrigin(mPrincipal
, asciiOrigin
);
1489 if (NS_WARN_IF(aRv
.Failed())) {
1493 ToLowerCase(asciiOrigin
);
1495 nsCOMPtr
<nsIURI
> uri
;
1496 aRv
= NS_NewURI(getter_AddRefs(uri
), mURI
);
1497 MOZ_ASSERT(!aRv
.Failed());
1499 aRv
= mChannel
->AsyncOpen(uri
, asciiOrigin
, this, nullptr);
1500 if (NS_WARN_IF(aRv
.Failed())) {
1505 //-----------------------------------------------------------------------------
1506 // WebSocketImpl methods:
1507 //-----------------------------------------------------------------------------
1509 class nsAutoCloseWS MOZ_FINAL
1512 explicit nsAutoCloseWS(WebSocketImpl
* aWebSocketImpl
)
1513 : mWebSocketImpl(aWebSocketImpl
)
1518 if (!mWebSocketImpl
->mChannel
) {
1519 mWebSocketImpl
->CloseConnection(nsIWebSocketChannel::CLOSE_INTERNAL_ERROR
);
1523 nsRefPtr
<WebSocketImpl
> mWebSocketImpl
;
1527 WebSocketImpl::InitializeConnection()
1529 AssertIsOnMainThread();
1530 NS_ABORT_IF_FALSE(!mChannel
, "mChannel should be null");
1532 nsCOMPtr
<nsIWebSocketChannel
> wsChannel
;
1533 nsAutoCloseWS
autoClose(this);
1538 do_CreateInstance("@mozilla.org/network/protocol;1?name=wss", &rv
);
1541 do_CreateInstance("@mozilla.org/network/protocol;1?name=ws", &rv
);
1543 NS_ENSURE_SUCCESS(rv
, rv
);
1545 // add ourselves to the document's load group and
1546 // provide the http stack the loadgroup info too
1547 nsCOMPtr
<nsILoadGroup
> loadGroup
;
1548 rv
= GetLoadGroup(getter_AddRefs(loadGroup
));
1550 rv
= wsChannel
->SetLoadGroup(loadGroup
);
1551 NS_ENSURE_SUCCESS(rv
, rv
);
1552 rv
= loadGroup
->AddRequest(this, nullptr);
1553 NS_ENSURE_SUCCESS(rv
, rv
);
1555 mWeakLoadGroup
= do_GetWeakReference(loadGroup
);
1558 // manually adding loadinfo to the channel since it
1559 // was not set during channel creation.
1560 nsCOMPtr
<nsIDocument
> doc
= do_QueryReferent(mOriginDocument
);
1562 // mOriginDocument has to be release on main-thread because WeakReferences
1563 // are not thread-safe.
1564 mOriginDocument
= nullptr;
1566 nsCOMPtr
<nsILoadInfo
> loadInfo
=
1568 doc
->NodePrincipal() : mPrincipal
.get(),
1571 nsILoadInfo::SEC_NORMAL
,
1572 nsIContentPolicy::TYPE_WEBSOCKET
);
1573 rv
= wsChannel
->SetLoadInfo(loadInfo
);
1574 NS_ENSURE_SUCCESS(rv
, rv
);
1576 if (!mRequestedProtocolList
.IsEmpty()) {
1577 rv
= wsChannel
->SetProtocol(mRequestedProtocolList
);
1578 NS_ENSURE_SUCCESS(rv
, rv
);
1581 nsCOMPtr
<nsIThreadRetargetableRequest
> rr
= do_QueryInterface(wsChannel
);
1582 NS_ENSURE_TRUE(rr
, NS_ERROR_FAILURE
);
1584 rv
= rr
->RetargetDeliveryTo(this);
1585 NS_ENSURE_SUCCESS(rv
, rv
);
1587 mChannel
= wsChannel
;
1593 WebSocketImpl::DispatchConnectionCloseEvents()
1595 AssertIsOnTargetThread();
1597 if (mDisconnectingOrDisconnected
) {
1601 mWebSocket
->SetReadyState(WebSocket::CLOSED
);
1603 // Let's keep the object alive because the webSocket can be CCed in the
1604 // onerror or in the onclose callback.
1605 nsRefPtr
<WebSocket
> webSocket
= mWebSocket
;
1607 // Call 'onerror' if needed
1610 webSocket
->CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error"));
1611 if (NS_FAILED(rv
)) {
1612 NS_WARNING("Failed to dispatch the error event");
1616 nsresult rv
= webSocket
->CreateAndDispatchCloseEvent(mCloseEventWasClean
,
1619 if (NS_FAILED(rv
)) {
1620 NS_WARNING("Failed to dispatch the close event");
1623 webSocket
->UpdateMustKeepAlive();
1628 WebSocket::CreateAndDispatchSimpleEvent(const nsAString
& aName
)
1631 AssertIsOnTargetThread();
1633 nsresult rv
= CheckInnerWindowCorrectness();
1634 if (NS_FAILED(rv
)) {
1638 nsCOMPtr
<nsIDOMEvent
> event
;
1639 rv
= NS_NewDOMEvent(getter_AddRefs(event
), this, nullptr, nullptr);
1640 NS_ENSURE_SUCCESS(rv
, rv
);
1642 // it doesn't bubble, and it isn't cancelable
1643 rv
= event
->InitEvent(aName
, false, false);
1644 NS_ENSURE_SUCCESS(rv
, rv
);
1646 event
->SetTrusted(true);
1648 return DispatchDOMEvent(nullptr, event
, nullptr, nullptr);
1652 WebSocket::CreateAndDispatchMessageEvent(const nsACString
& aData
,
1656 AssertIsOnTargetThread();
1660 if (NS_IsMainThread()) {
1661 if (NS_WARN_IF(!jsapi
.Init(GetOwner()))) {
1662 return NS_ERROR_FAILURE
;
1665 MOZ_ASSERT(!mIsMainThread
);
1666 MOZ_ASSERT(mImpl
->mWorkerPrivate
);
1667 if (NS_WARN_IF(!jsapi
.Init(mImpl
->mWorkerPrivate
->GlobalScope()))) {
1668 return NS_ERROR_FAILURE
;
1672 return CreateAndDispatchMessageEvent(jsapi
.cx(), aData
, aIsBinary
);
1676 WebSocket::CreateAndDispatchMessageEvent(JSContext
* aCx
,
1677 const nsACString
& aData
,
1681 AssertIsOnTargetThread();
1683 nsresult rv
= CheckInnerWindowCorrectness();
1684 if (NS_FAILED(rv
)) {
1688 // Create appropriate JS object for message
1689 JS::Rooted
<JS::Value
> jsData(aCx
);
1691 if (mBinaryType
== dom::BinaryType::Blob
) {
1692 nsresult rv
= nsContentUtils::CreateBlobBuffer(aCx
, GetOwner(), aData
,
1694 NS_ENSURE_SUCCESS(rv
, rv
);
1695 } else if (mBinaryType
== dom::BinaryType::Arraybuffer
) {
1696 JS::Rooted
<JSObject
*> arrayBuf(aCx
);
1697 nsresult rv
= nsContentUtils::CreateArrayBuffer(aCx
, aData
,
1698 arrayBuf
.address());
1699 NS_ENSURE_SUCCESS(rv
, rv
);
1700 jsData
= OBJECT_TO_JSVAL(arrayBuf
);
1702 NS_RUNTIMEABORT("Unknown binary type!");
1703 return NS_ERROR_UNEXPECTED
;
1707 NS_ConvertUTF8toUTF16
utf16Data(aData
);
1709 jsString
= JS_NewUCStringCopyN(aCx
, utf16Data
.get(), utf16Data
.Length());
1710 NS_ENSURE_TRUE(jsString
, NS_ERROR_FAILURE
);
1712 jsData
= STRING_TO_JSVAL(jsString
);
1715 // create an event that uses the MessageEvent interface,
1716 // which does not bubble, is not cancelable, and has no default action
1718 nsCOMPtr
<nsIDOMEvent
> event
;
1719 rv
= NS_NewDOMMessageEvent(getter_AddRefs(event
), this, nullptr, nullptr);
1720 NS_ENSURE_SUCCESS(rv
, rv
);
1722 nsCOMPtr
<nsIDOMMessageEvent
> messageEvent
= do_QueryInterface(event
);
1723 rv
= messageEvent
->InitMessageEvent(NS_LITERAL_STRING("message"),
1726 mImpl
->mUTF16Origin
,
1727 EmptyString(), nullptr);
1728 NS_ENSURE_SUCCESS(rv
, rv
);
1730 event
->SetTrusted(true);
1732 return DispatchDOMEvent(nullptr, event
, nullptr, nullptr);
1736 WebSocket::CreateAndDispatchCloseEvent(bool aWasClean
,
1738 const nsAString
&aReason
)
1741 AssertIsOnTargetThread();
1743 nsresult rv
= CheckInnerWindowCorrectness();
1744 if (NS_FAILED(rv
)) {
1748 CloseEventInit init
;
1749 init
.mBubbles
= false;
1750 init
.mCancelable
= false;
1751 init
.mWasClean
= aWasClean
;
1753 init
.mReason
= aReason
;
1755 nsRefPtr
<CloseEvent
> event
=
1756 CloseEvent::Constructor(this, NS_LITERAL_STRING("close"), init
);
1757 event
->SetTrusted(true);
1759 return DispatchDOMEvent(nullptr, event
, nullptr, nullptr);
1764 class PrefEnabledRunnable MOZ_FINAL
: public WorkerMainThreadRunnable
1767 explicit PrefEnabledRunnable(WorkerPrivate
* aWorkerPrivate
)
1768 : WorkerMainThreadRunnable(aWorkerPrivate
)
1772 bool MainThreadRun() MOZ_OVERRIDE
1774 AssertIsOnMainThread();
1775 mEnabled
= Preferences::GetBool("dom.workers.websocket.enabled", false);
1779 bool IsEnabled() const
1788 } // anonymous namespace
1791 WebSocket::PrefEnabled(JSContext
* /* aCx */, JSObject
* /* aGlobal */)
1793 // WebSockets are always enabled on main-thread.
1794 if (NS_IsMainThread()) {
1798 WorkerPrivate
* workerPrivate
= GetCurrentThreadWorkerPrivate();
1799 MOZ_ASSERT(workerPrivate
);
1800 workerPrivate
->AssertIsOnWorkerThread();
1802 nsRefPtr
<PrefEnabledRunnable
> runnable
=
1803 new PrefEnabledRunnable(workerPrivate
);
1804 runnable
->Dispatch(workerPrivate
->GetJSContext());
1806 return runnable
->IsEnabled();
1810 WebSocketImpl::ParseURL(const nsAString
& aURL
)
1812 AssertIsOnMainThread();
1813 NS_ENSURE_TRUE(!aURL
.IsEmpty(), NS_ERROR_DOM_SYNTAX_ERR
);
1815 nsCOMPtr
<nsIURI
> uri
;
1816 nsresult rv
= NS_NewURI(getter_AddRefs(uri
), aURL
);
1817 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SYNTAX_ERR
);
1819 nsCOMPtr
<nsIURL
> parsedURL
= do_QueryInterface(uri
, &rv
);
1820 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SYNTAX_ERR
);
1822 nsAutoCString fragment
;
1823 rv
= parsedURL
->GetRef(fragment
);
1824 NS_ENSURE_TRUE(NS_SUCCEEDED(rv
) && fragment
.IsEmpty(),
1825 NS_ERROR_DOM_SYNTAX_ERR
);
1827 nsAutoCString scheme
;
1828 rv
= parsedURL
->GetScheme(scheme
);
1829 NS_ENSURE_TRUE(NS_SUCCEEDED(rv
) && !scheme
.IsEmpty(),
1830 NS_ERROR_DOM_SYNTAX_ERR
);
1833 rv
= parsedURL
->GetAsciiHost(host
);
1834 NS_ENSURE_TRUE(NS_SUCCEEDED(rv
) && !host
.IsEmpty(), NS_ERROR_DOM_SYNTAX_ERR
);
1837 rv
= parsedURL
->GetPort(&port
);
1838 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SYNTAX_ERR
);
1840 rv
= NS_CheckPortSafety(port
, scheme
.get());
1841 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SYNTAX_ERR
);
1843 nsAutoCString filePath
;
1844 rv
= parsedURL
->GetFilePath(filePath
);
1845 if (filePath
.IsEmpty()) {
1846 filePath
.Assign('/');
1848 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SYNTAX_ERR
);
1850 nsAutoCString query
;
1851 rv
= parsedURL
->GetQuery(query
);
1852 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SYNTAX_ERR
);
1854 if (scheme
.LowerCaseEqualsLiteral("ws")) {
1856 mPort
= (port
== -1) ? DEFAULT_WS_SCHEME_PORT
: port
;
1857 } else if (scheme
.LowerCaseEqualsLiteral("wss")) {
1859 mPort
= (port
== -1) ? DEFAULT_WSS_SCHEME_PORT
: port
;
1861 return NS_ERROR_DOM_SYNTAX_ERR
;
1864 rv
= nsContentUtils::GetUTFOrigin(parsedURL
, mUTF16Origin
);
1865 NS_ENSURE_SUCCESS(rv
, NS_ERROR_DOM_SYNTAX_ERR
);
1868 ToLowerCase(mAsciiHost
);
1870 mResource
= filePath
;
1871 if (!query
.IsEmpty()) {
1872 mResource
.Append('?');
1873 mResource
.Append(query
);
1875 uint32_t length
= mResource
.Length();
1877 for (i
= 0; i
< length
; ++i
) {
1878 if (mResource
[i
] < static_cast<char16_t
>(0x0021) ||
1879 mResource
[i
] > static_cast<char16_t
>(0x007E)) {
1880 return NS_ERROR_DOM_SYNTAX_ERR
;
1884 mWebSocket
->mOriginalURL
= aURL
;
1886 rv
= parsedURL
->GetSpec(mURI
);
1887 MOZ_ASSERT(NS_SUCCEEDED(rv
));
1892 //-----------------------------------------------------------------------------
1893 // Methods that keep alive the WebSocket object when:
1894 // 1. the object has registered event listeners that can be triggered
1895 // ("strong event listeners");
1896 // 2. there are outgoing not sent messages.
1897 //-----------------------------------------------------------------------------
1900 WebSocket::UpdateMustKeepAlive()
1902 // Here we could not have mImpl.
1903 MOZ_ASSERT(NS_IsMainThread() == mIsMainThread
);
1905 if (!mCheckMustKeepAlive
|| !mImpl
) {
1909 bool shouldKeepAlive
= false;
1910 uint16_t readyState
= ReadyState();
1912 if (mListenerManager
) {
1917 if (mListenerManager
->HasListenersFor(nsGkAtoms::onopen
) ||
1918 mListenerManager
->HasListenersFor(nsGkAtoms::onmessage
) ||
1919 mListenerManager
->HasListenersFor(nsGkAtoms::onerror
) ||
1920 mListenerManager
->HasListenersFor(nsGkAtoms::onclose
)) {
1921 shouldKeepAlive
= true;
1929 if (mListenerManager
->HasListenersFor(nsGkAtoms::onmessage
) ||
1930 mListenerManager
->HasListenersFor(nsGkAtoms::onerror
) ||
1931 mListenerManager
->HasListenersFor(nsGkAtoms::onclose
) ||
1932 mOutgoingBufferedAmount
!= 0) {
1933 shouldKeepAlive
= true;
1940 shouldKeepAlive
= false;
1945 if (mKeepingAlive
&& !shouldKeepAlive
) {
1946 mKeepingAlive
= false;
1947 mImpl
->ReleaseObject();
1948 } else if (!mKeepingAlive
&& shouldKeepAlive
) {
1949 mKeepingAlive
= true;
1950 mImpl
->AddRefObject();
1955 WebSocket::DontKeepAliveAnyMore()
1957 // Here we could not have mImpl.
1958 MOZ_ASSERT(NS_IsMainThread() == mIsMainThread
);
1960 if (mKeepingAlive
) {
1963 mKeepingAlive
= false;
1964 mImpl
->ReleaseObject();
1967 mCheckMustKeepAlive
= false;
1972 class WebSocketWorkerFeature MOZ_FINAL
: public WorkerFeature
1975 explicit WebSocketWorkerFeature(WebSocketImpl
* aWebSocketImpl
)
1976 : mWebSocketImpl(aWebSocketImpl
)
1980 bool Notify(JSContext
* aCx
, Status aStatus
) MOZ_OVERRIDE
1982 MOZ_ASSERT(aStatus
> workers::Running
);
1984 if (aStatus
>= Canceling
) {
1985 mWebSocketImpl
->mWorkerShuttingDown
= true;
1986 mWebSocketImpl
->CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY
);
1992 bool Suspend(JSContext
* aCx
) MOZ_OVERRIDE
1994 mWebSocketImpl
->mWorkerShuttingDown
= true;
1995 mWebSocketImpl
->CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY
);
2000 WebSocketImpl
* mWebSocketImpl
;
2003 } // anonymous namespace
2006 WebSocketImpl::AddRefObject()
2008 AssertIsOnTargetThread();
2013 WebSocketImpl::ReleaseObject()
2015 AssertIsOnTargetThread();
2020 WebSocketImpl::RegisterFeature()
2022 mWorkerPrivate
->AssertIsOnWorkerThread();
2023 MOZ_ASSERT(!mWorkerFeature
);
2024 mWorkerFeature
= new WebSocketWorkerFeature(this);
2026 JSContext
* cx
= GetCurrentThreadJSContext();
2027 if (!mWorkerPrivate
->AddFeature(cx
, mWorkerFeature
)) {
2028 NS_WARNING("Failed to register a feature.");
2029 mWorkerFeature
= nullptr;
2034 SetHasFeatureRegistered(true);
2041 WebSocketImpl::UnregisterFeature()
2043 MOZ_ASSERT(mDisconnectingOrDisconnected
);
2044 MOZ_ASSERT(mWorkerPrivate
);
2045 mWorkerPrivate
->AssertIsOnWorkerThread();
2046 MOZ_ASSERT(mWorkerFeature
);
2048 JSContext
* cx
= GetCurrentThreadJSContext();
2049 mWorkerPrivate
->RemoveFeature(cx
, mWorkerFeature
);
2050 mWorkerFeature
= nullptr;
2051 mWorkerPrivate
= nullptr;
2054 SetHasFeatureRegistered(false);
2059 WebSocketImpl::UpdateURI()
2061 AssertIsOnTargetThread();
2063 // Check for Redirections
2064 nsRefPtr
<BaseWebSocketChannel
> channel
;
2065 channel
= static_cast<BaseWebSocketChannel
*>(mChannel
.get());
2066 MOZ_ASSERT(channel
);
2068 channel
->GetEffectiveURL(mWebSocket
->mEffectiveURL
);
2069 mSecure
= channel
->IsEncrypted();
2075 WebSocket::EventListenerAdded(nsIAtom
* aType
)
2077 AssertIsOnMainThread();
2078 UpdateMustKeepAlive();
2082 WebSocket::EventListenerRemoved(nsIAtom
* aType
)
2084 AssertIsOnMainThread();
2085 UpdateMustKeepAlive();
2088 //-----------------------------------------------------------------------------
2089 // WebSocket - methods
2090 //-----------------------------------------------------------------------------
2092 // webIDL: readonly attribute unsigned short readyState;
2094 WebSocket::ReadyState()
2096 MutexAutoLock
lock(mMutex
);
2101 WebSocket::SetReadyState(uint16_t aReadyState
)
2103 MutexAutoLock
lock(mMutex
);
2104 mReadyState
= aReadyState
;
2107 // webIDL: readonly attribute unsigned long bufferedAmount;
2109 WebSocket::BufferedAmount() const
2111 AssertIsOnTargetThread();
2112 return mOutgoingBufferedAmount
;
2115 // webIDL: attribute BinaryType binaryType;
2117 WebSocket::BinaryType() const
2119 AssertIsOnTargetThread();
2123 // webIDL: attribute BinaryType binaryType;
2125 WebSocket::SetBinaryType(dom::BinaryType aData
)
2127 AssertIsOnTargetThread();
2128 mBinaryType
= aData
;
2131 // webIDL: readonly attribute DOMString url
2133 WebSocket::GetUrl(nsAString
& aURL
)
2135 AssertIsOnTargetThread();
2137 if (mEffectiveURL
.IsEmpty()) {
2138 aURL
= mOriginalURL
;
2140 aURL
= mEffectiveURL
;
2144 // webIDL: readonly attribute DOMString extensions;
2146 WebSocket::GetExtensions(nsAString
& aExtensions
)
2148 AssertIsOnTargetThread();
2149 CopyUTF8toUTF16(mEstablishedExtensions
, aExtensions
);
2152 // webIDL: readonly attribute DOMString protocol;
2154 WebSocket::GetProtocol(nsAString
& aProtocol
)
2156 AssertIsOnTargetThread();
2157 CopyUTF8toUTF16(mEstablishedProtocol
, aProtocol
);
2160 // webIDL: void send(DOMString data);
2162 WebSocket::Send(const nsAString
& aData
,
2165 AssertIsOnTargetThread();
2167 NS_ConvertUTF16toUTF8
msgString(aData
);
2168 Send(nullptr, msgString
, msgString
.Length(), false, aRv
);
2172 WebSocket::Send(File
& aData
, ErrorResult
& aRv
)
2174 AssertIsOnTargetThread();
2176 nsCOMPtr
<nsIInputStream
> msgStream
;
2177 nsresult rv
= aData
.GetInternalStream(getter_AddRefs(msgStream
));
2178 if (NS_FAILED(rv
)) {
2184 rv
= aData
.GetSize(&msgLength
);
2185 if (NS_FAILED(rv
)) {
2190 if (msgLength
> UINT32_MAX
) {
2191 aRv
.Throw(NS_ERROR_FILE_TOO_BIG
);
2195 Send(msgStream
, EmptyCString(), msgLength
, true, aRv
);
2199 WebSocket::Send(const ArrayBuffer
& aData
,
2202 AssertIsOnTargetThread();
2204 aData
.ComputeLengthAndData();
2206 static_assert(sizeof(*aData
.Data()) == 1, "byte-sized data required");
2208 uint32_t len
= aData
.Length();
2209 char* data
= reinterpret_cast<char*>(aData
.Data());
2211 nsDependentCSubstring
msgString(data
, len
);
2212 Send(nullptr, msgString
, len
, true, aRv
);
2216 WebSocket::Send(const ArrayBufferView
& aData
,
2219 AssertIsOnTargetThread();
2221 aData
.ComputeLengthAndData();
2223 static_assert(sizeof(*aData
.Data()) == 1, "byte-sized data required");
2225 uint32_t len
= aData
.Length();
2226 char* data
= reinterpret_cast<char*>(aData
.Data());
2228 nsDependentCSubstring
msgString(data
, len
);
2229 Send(nullptr, msgString
, len
, true, aRv
);
2233 WebSocket::Send(nsIInputStream
* aMsgStream
,
2234 const nsACString
& aMsgString
,
2235 uint32_t aMsgLength
,
2239 AssertIsOnTargetThread();
2241 int64_t readyState
= ReadyState();
2242 if (readyState
== CONNECTING
) {
2243 aRv
.Throw(NS_ERROR_DOM_INVALID_STATE_ERR
);
2247 // Always increment outgoing buffer len, even if closed
2248 mOutgoingBufferedAmount
+= aMsgLength
;
2250 if (readyState
== CLOSING
||
2251 readyState
== CLOSED
) {
2255 // We must have mImpl when connected.
2257 MOZ_ASSERT(readyState
== OPEN
, "Unknown state in WebSocket::Send");
2261 rv
= mImpl
->mChannel
->SendBinaryStream(aMsgStream
, aMsgLength
);
2264 rv
= mImpl
->mChannel
->SendBinaryMsg(aMsgString
);
2266 rv
= mImpl
->mChannel
->SendMsg(aMsgString
);
2270 if (NS_FAILED(rv
)) {
2275 UpdateMustKeepAlive();
2278 // webIDL: void close(optional unsigned short code, optional DOMString reason):
2280 WebSocket::Close(const Optional
<uint16_t>& aCode
,
2281 const Optional
<nsAString
>& aReason
,
2284 AssertIsOnTargetThread();
2286 // the reason code is optional, but if provided it must be in a specific range
2287 uint16_t closeCode
= 0;
2288 if (aCode
.WasPassed()) {
2289 if (aCode
.Value() != 1000 && (aCode
.Value() < 3000 || aCode
.Value() > 4999)) {
2290 aRv
.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR
);
2293 closeCode
= aCode
.Value();
2296 nsCString closeReason
;
2297 if (aReason
.WasPassed()) {
2298 CopyUTF16toUTF8(aReason
.Value(), closeReason
);
2300 // The API requires the UTF-8 string to be 123 or less bytes
2301 if (closeReason
.Length() > 123) {
2302 aRv
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
2307 int64_t readyState
= ReadyState();
2308 if (readyState
== CLOSING
||
2309 readyState
== CLOSED
) {
2313 // If the webSocket is not closed we MUST have a mImpl.
2316 if (readyState
== CONNECTING
) {
2317 mImpl
->FailConnection(closeCode
, closeReason
);
2321 MOZ_ASSERT(readyState
== OPEN
);
2322 mImpl
->CloseConnection(closeCode
, closeReason
);
2325 //-----------------------------------------------------------------------------
2326 // WebSocketImpl::nsIObserver
2327 //-----------------------------------------------------------------------------
2330 WebSocketImpl::Observe(nsISupports
* aSubject
,
2332 const char16_t
* aData
)
2334 AssertIsOnMainThread();
2336 int64_t readyState
= mWebSocket
->ReadyState();
2337 if ((readyState
== WebSocket::CLOSING
) ||
2338 (readyState
== WebSocket::CLOSED
)) {
2342 nsCOMPtr
<nsPIDOMWindow
> window
= do_QueryInterface(aSubject
);
2343 if (!mWebSocket
->GetOwner() || window
!= mWebSocket
->GetOwner()) {
2347 if ((strcmp(aTopic
, DOM_WINDOW_FROZEN_TOPIC
) == 0) ||
2348 (strcmp(aTopic
, DOM_WINDOW_DESTROYED_TOPIC
) == 0))
2350 CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY
);
2356 //-----------------------------------------------------------------------------
2357 // WebSocketImpl::nsIRequest
2358 //-----------------------------------------------------------------------------
2361 WebSocketImpl::GetName(nsACString
& aName
)
2363 AssertIsOnMainThread();
2365 CopyUTF16toUTF8(mWebSocket
->mOriginalURL
, aName
);
2370 WebSocketImpl::IsPending(bool* aValue
)
2372 AssertIsOnTargetThread();
2374 int64_t readyState
= mWebSocket
->ReadyState();
2375 *aValue
= (readyState
!= WebSocket::CLOSED
);
2380 WebSocketImpl::GetStatus(nsresult
* aStatus
)
2382 AssertIsOnTargetThread();
2390 class CancelRunnable MOZ_FINAL
: public WorkerRunnable
2393 CancelRunnable(WorkerPrivate
* aWorkerPrivate
, WebSocketImpl
* aImpl
)
2394 : WorkerRunnable(aWorkerPrivate
, WorkerThreadUnchangedBusyCount
)
2399 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
)
2401 aWorkerPrivate
->AssertIsOnWorkerThread();
2402 aWorkerPrivate
->ModifyBusyCountFromWorker(aCx
, true);
2403 return !NS_FAILED(mImpl
->CancelInternal());
2406 void PostRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
, bool aRunResult
)
2408 aWorkerPrivate
->ModifyBusyCountFromWorker(aCx
, false);
2412 PreDispatch(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
)
2418 PostDispatch(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
,
2419 bool aDispatchResult
)
2424 nsRefPtr
<WebSocketImpl
> mImpl
;
2427 } // anonymous namespace
2429 // Window closed, stop/reload button pressed, user navigated away from page, etc.
2431 WebSocketImpl::Cancel(nsresult aStatus
)
2433 AssertIsOnMainThread();
2435 if (!mIsMainThread
) {
2436 MOZ_ASSERT(mWorkerPrivate
);
2437 nsRefPtr
<CancelRunnable
> runnable
=
2438 new CancelRunnable(mWorkerPrivate
, this);
2439 if (!runnable
->Dispatch(nullptr)) {
2440 return NS_ERROR_FAILURE
;
2446 return CancelInternal();
2450 WebSocketImpl::CancelInternal()
2452 AssertIsOnTargetThread();
2454 // If CancelInternal is called by a runnable, we may already be disconnected
2455 // by the time it runs.
2456 if (mDisconnectingOrDisconnected
) {
2460 int64_t readyState
= mWebSocket
->ReadyState();
2461 if (readyState
== WebSocket::CLOSING
|| readyState
== WebSocket::CLOSED
) {
2467 return CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY
);
2471 WebSocketImpl::Suspend()
2473 AssertIsOnMainThread();
2474 return NS_ERROR_NOT_IMPLEMENTED
;
2478 WebSocketImpl::Resume()
2480 AssertIsOnMainThread();
2481 return NS_ERROR_NOT_IMPLEMENTED
;
2485 WebSocketImpl::GetLoadGroup(nsILoadGroup
** aLoadGroup
)
2487 AssertIsOnMainThread();
2489 *aLoadGroup
= nullptr;
2491 if (mIsMainThread
) {
2493 nsIScriptContext
* sc
= mWebSocket
->GetContextForEventHandlers(&rv
);
2494 nsCOMPtr
<nsIDocument
> doc
=
2495 nsContentUtils::GetDocumentFromScriptContext(sc
);
2498 *aLoadGroup
= doc
->GetDocumentLoadGroup().take();
2504 MOZ_ASSERT(mWorkerPrivate
);
2506 // Walk up to our containing page
2507 WorkerPrivate
* wp
= mWorkerPrivate
;
2508 while (wp
->GetParent()) {
2509 wp
= wp
->GetParent();
2512 nsPIDOMWindow
* window
= wp
->GetWindow();
2517 nsIDocument
* doc
= window
->GetExtantDoc();
2519 *aLoadGroup
= doc
->GetDocumentLoadGroup().take();
2526 WebSocketImpl::SetLoadGroup(nsILoadGroup
* aLoadGroup
)
2528 AssertIsOnMainThread();
2529 return NS_ERROR_UNEXPECTED
;
2533 WebSocketImpl::GetLoadFlags(nsLoadFlags
* aLoadFlags
)
2535 AssertIsOnMainThread();
2537 *aLoadFlags
= nsIRequest::LOAD_BACKGROUND
;
2542 WebSocketImpl::SetLoadFlags(nsLoadFlags aLoadFlags
)
2544 AssertIsOnMainThread();
2546 // we won't change the load flags at all.
2552 class WorkerRunnableDispatcher MOZ_FINAL
: public WorkerRunnable
2555 WorkerRunnableDispatcher(WorkerPrivate
* aWorkerPrivate
, nsIRunnable
* aEvent
)
2556 : WorkerRunnable(aWorkerPrivate
, WorkerThreadUnchangedBusyCount
)
2561 bool WorkerRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
)
2563 aWorkerPrivate
->AssertIsOnWorkerThread();
2564 aWorkerPrivate
->ModifyBusyCountFromWorker(aCx
, true);
2565 return !NS_FAILED(mEvent
->Run());
2568 void PostRun(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
, bool aRunResult
)
2570 aWorkerPrivate
->ModifyBusyCountFromWorker(aCx
, false);
2574 PreDispatch(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
)
2580 PostDispatch(JSContext
* aCx
, WorkerPrivate
* aWorkerPrivate
,
2581 bool aDispatchResult
)
2586 nsCOMPtr
<nsIRunnable
> mEvent
;
2589 } // anonymous namespace
2592 WebSocketImpl::Dispatch(nsIRunnable
* aEvent
, uint32_t aFlags
)
2594 // If the target is the main-thread we can just dispatch the runnable.
2595 if (mIsMainThread
) {
2596 return NS_DispatchToMainThread(aEvent
);
2599 // No messages when disconnected.
2600 if (mDisconnectingOrDisconnected
) {
2601 NS_WARNING("Dispatching a WebSocket event after the disconnection!");
2605 if (mWorkerShuttingDown
) {
2609 MOZ_ASSERT(mWorkerPrivate
);
2612 MOZ_ASSERT(HasFeatureRegistered());
2615 // If the target is a worker, we have to use a custom WorkerRunnableDispatcher
2617 nsRefPtr
<WorkerRunnableDispatcher
> event
=
2618 new WorkerRunnableDispatcher(mWorkerPrivate
, aEvent
);
2619 if (!event
->Dispatch(nullptr)) {
2620 return NS_ERROR_FAILURE
;
2627 WebSocketImpl::IsOnCurrentThread(bool* aResult
)
2629 *aResult
= IsTargetThread();
2634 WebSocketImpl::IsTargetThread() const
2636 return NS_IsMainThread() == mIsMainThread
;
2640 WebSocket::AssertIsOnTargetThread() const
2642 MOZ_ASSERT(NS_IsMainThread() == mIsMainThread
);
2646 } // mozilla namespace