Backed out changeset 818165fa787b (bug 1257415) for breaking l10n nightly's
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- *//* vim: set ts=8 sts=2 et sw=2 tw=80: *//* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"WebSocket.h"#include"mozilla/dom/WebSocketBinding.h"#include"mozilla/net/WebSocketChannel.h"#include"jsapi.h"#include"jsfriendapi.h"#include"mozilla/DOMEventTargetHelper.h"#include"mozilla/net/WebSocketChannel.h"#include"mozilla/dom/File.h"#include"mozilla/dom/MessageEvent.h"#include"mozilla/dom/MessageEventBinding.h"#include"mozilla/dom/nsCSPContext.h"#include"mozilla/dom/nsCSPUtils.h"#include"mozilla/dom/ScriptSettings.h"#include"mozilla/dom/WorkerPrivate.h"#include"mozilla/dom/WorkerRunnable.h"#include"mozilla/dom/WorkerScope.h"#include"nsGlobalWindow.h"#include"nsIScriptGlobalObject.h"#include"nsIDOMWindow.h"#include"nsIDocument.h"#include"nsXPCOM.h"#include"nsIXPConnect.h"#include"nsContentUtils.h"#include"nsError.h"#include"nsIScriptObjectPrincipal.h"#include"nsIURL.h"#include"nsIUnicodeEncoder.h"#include"nsThreadUtils.h"#include"nsIPromptFactory.h"#include"nsIWindowWatcher.h"#include"nsIPrompt.h"#include"nsIStringBundle.h"#include"nsIConsoleService.h"#include"mozilla/dom/CloseEvent.h"#include"mozilla/net/WebSocketEventService.h"#include"nsICryptoHash.h"#include"nsJSUtils.h"#include"nsIScriptError.h"#include"nsNetUtil.h"#include"nsIAuthPrompt.h"#include"nsIAuthPrompt2.h"#include"nsILoadGroup.h"#include"mozilla/Preferences.h"#include"xpcpublic.h"#include"nsContentPolicyUtils.h"#include"nsWrapperCacheInlines.h"#include"nsIObserverService.h"#include"nsIEventTarget.h"#include"nsIInterfaceRequestor.h"#include"nsIObserver.h"#include"nsIRequest.h"#include"nsIThreadRetargetableRequest.h"#include"nsIWebSocketChannel.h"#include"nsIWebSocketListener.h"#include"nsProxyRelease.h"#include"nsWeakReference.h"usingnamespacemozilla::net;usingnamespacemozilla::dom::workers;namespacemozilla{namespacedom{classWebSocketImplfinal:publicnsIInterfaceRequestor,publicnsIWebSocketListener,publicnsIObserver,publicnsSupportsWeakReference,publicnsIRequest,publicnsIEventTarget{public:NS_DECL_NSIINTERFACEREQUESTORNS_DECL_NSIWEBSOCKETLISTENERNS_DECL_NSIOBSERVERNS_DECL_NSIREQUESTNS_DECL_THREADSAFE_ISUPPORTSNS_DECL_NSIEVENTTARGETusingnsIEventTarget::Dispatch;explicitWebSocketImpl(WebSocket*aWebSocket):mWebSocket(aWebSocket),mOnCloseScheduled(false),mFailed(false),mDisconnectingOrDisconnected(false),mCloseEventWasClean(false),mCloseEventCode(nsIWebSocketChannel::CLOSE_ABNORMAL),mScriptLine(0),mScriptColumn(0),mInnerWindowID(0),mWorkerPrivate(nullptr)#ifdef DEBUG,mHasFeatureRegistered(false)#endif,mIsMainThread(true),mMutex("WebSocketImpl::mMutex"),mWorkerShuttingDown(false){if(!NS_IsMainThread()){mWorkerPrivate=GetCurrentThreadWorkerPrivate();MOZ_ASSERT(mWorkerPrivate);mIsMainThread=false;}}voidAssertIsOnTargetThread()const{MOZ_ASSERT(IsTargetThread());}boolIsTargetThread()const;voidInit(JSContext*aCx,nsIPrincipal*aPrincipal,constnsAString&aURL,nsTArray<nsString>&aProtocolArray,constnsACString&aScriptFile,uint32_taScriptLine,uint32_taScriptColumn,ErrorResult&aRv,bool*aConnectionFailed);voidAsyncOpen(nsIPrincipal*aPrincipal,uint64_taInnerWindowID,ErrorResult&aRv);nsresultParseURL(constnsAString&aURL);nsresultInitializeConnection(nsIPrincipal*aPrincipal);// These methods when called can release the WebSocket objectvoidFailConnection(uint16_treasonCode,constnsACString&aReasonString=EmptyCString());nsresultCloseConnection(uint16_treasonCode,constnsACString&aReasonString=EmptyCString());voidDisconnect();voidDisconnectInternal();nsresultConsoleError();voidPrintErrorOnConsole(constchar*aBundleURI,constchar16_t*aError,constchar16_t**aFormatStrings,uint32_taFormatStringsLen);nsresultDoOnMessageAvailable(constnsACString&aMsg,boolisBinary);// ConnectionCloseEvents: 'error' event if needed, then 'close' event.nsresultScheduleConnectionCloseEvents(nsISupports*aContext,nsresultaStatusCode);// 2nd half of ScheduleConnectionCloseEvents, run in its own event.voidDispatchConnectionCloseEvents();nsresultUpdateURI();voidAddRefObject();voidReleaseObject();boolRegisterFeature();voidUnregisterFeature();nsresultCancelInternal();RefPtr<WebSocket>mWebSocket;nsCOMPtr<nsIWebSocketChannel>mChannel;boolmSecure;// if true it is using SSL and the wss scheme,// otherwise it is using the ws scheme with no SSLboolmOnCloseScheduled;boolmFailed;boolmDisconnectingOrDisconnected;// Set attributes of DOM 'onclose' messageboolmCloseEventWasClean;nsStringmCloseEventReason;uint16_tmCloseEventCode;nsCStringmAsciiHost;// hostnameuint32_tmPort;nsCStringmResource;// [filepath[?query]]nsStringmUTF16Origin;nsCStringmURI;nsCStringmRequestedProtocolList;nsWeakPtrmOriginDocument;// Web Socket owner information:// - the script file name, UTF8 encoded.// - source code line number and column number where the Web Socket object// was constructed.// - the ID of the inner window where the script lives. Note that this may not// be the same as the Web Socket owner window.// These attributes are used for error reporting.nsCStringmScriptFile;uint32_tmScriptLine;uint32_tmScriptColumn;uint64_tmInnerWindowID;WorkerPrivate*mWorkerPrivate;nsAutoPtr<WorkerFeature>mWorkerFeature;#ifdef DEBUG// This is protected by mutex.boolmHasFeatureRegistered;boolHasFeatureRegistered(){MOZ_ASSERT(mWebSocket);MutexAutoLocklock(mWebSocket->mMutex);returnmHasFeatureRegistered;}voidSetHasFeatureRegistered(boolaValue){MOZ_ASSERT(mWebSocket);MutexAutoLocklock(mWebSocket->mMutex);mHasFeatureRegistered=aValue;}#endifnsWeakPtrmWeakLoadGroup;boolmIsMainThread;// This mutex protects mWorkerShuttingDown.mozilla::MutexmMutex;boolmWorkerShuttingDown;RefPtr<WebSocketEventService>mService;private:~WebSocketImpl(){// If we threw during Init we never called disconnectif(!mDisconnectingOrDisconnected){Disconnect();}}};NS_IMPL_ISUPPORTS(WebSocketImpl,nsIInterfaceRequestor,nsIWebSocketListener,nsIObserver,nsISupportsWeakReference,nsIRequest,nsIEventTarget)classCallDispatchConnectionCloseEventsfinal:publicnsCancelableRunnable{public:explicitCallDispatchConnectionCloseEvents(WebSocketImpl*aWebSocketImpl):mWebSocketImpl(aWebSocketImpl){aWebSocketImpl->AssertIsOnTargetThread();}NS_IMETHODRun(){mWebSocketImpl->AssertIsOnTargetThread();mWebSocketImpl->DispatchConnectionCloseEvents();returnNS_OK;}private:RefPtr<WebSocketImpl>mWebSocketImpl;};//-----------------------------------------------------------------------------// WebSocketImpl//-----------------------------------------------------------------------------namespace{classPrintErrorOnConsoleRunnablefinal:publicWorkerMainThreadRunnable{public:PrintErrorOnConsoleRunnable(WebSocketImpl*aImpl,constchar*aBundleURI,constchar16_t*aError,constchar16_t**aFormatStrings,uint32_taFormatStringsLen):WorkerMainThreadRunnable(aImpl->mWorkerPrivate),mImpl(aImpl),mBundleURI(aBundleURI),mError(aError),mFormatStrings(aFormatStrings),mFormatStringsLen(aFormatStringsLen){}boolMainThreadRun()override{mImpl->PrintErrorOnConsole(mBundleURI,mError,mFormatStrings,mFormatStringsLen);returntrue;}private:// Raw pointer because this runnable is sync.WebSocketImpl*mImpl;constchar*mBundleURI;constchar16_t*mError;constchar16_t**mFormatStrings;uint32_tmFormatStringsLen;};}// namespacevoidWebSocketImpl::PrintErrorOnConsole(constchar*aBundleURI,constchar16_t*aError,constchar16_t**aFormatStrings,uint32_taFormatStringsLen){// This method must run on the main thread.if(!NS_IsMainThread()){MOZ_ASSERT(mWorkerPrivate);RefPtr<PrintErrorOnConsoleRunnable>runnable=newPrintErrorOnConsoleRunnable(this,aBundleURI,aError,aFormatStrings,aFormatStringsLen);ErrorResultrv;runnable->Dispatch(rv);// XXXbz this seems totally broken. We should be propagating this out, but// none of our callers really propagate anything usefully. Come to think of// it, why is this a syncrunnable anyway? Can't this be a fire-and-forget// runnable??rv.SuppressException();return;}nsresultrv;nsCOMPtr<nsIStringBundleService>bundleService=do_GetService(NS_STRINGBUNDLE_CONTRACTID,&rv);NS_ENSURE_SUCCESS_VOID(rv);nsCOMPtr<nsIStringBundle>strBundle;rv=bundleService->CreateBundle(aBundleURI,getter_AddRefs(strBundle));NS_ENSURE_SUCCESS_VOID(rv);nsCOMPtr<nsIConsoleService>console(do_GetService(NS_CONSOLESERVICE_CONTRACTID,&rv));NS_ENSURE_SUCCESS_VOID(rv);nsCOMPtr<nsIScriptError>errorObject(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID,&rv));NS_ENSURE_SUCCESS_VOID(rv);// Localize the error messagensXPIDLStringmessage;if(aFormatStrings){rv=strBundle->FormatStringFromName(aError,aFormatStrings,aFormatStringsLen,getter_Copies(message));}else{rv=strBundle->GetStringFromName(aError,getter_Copies(message));}NS_ENSURE_SUCCESS_VOID(rv);if(mInnerWindowID){rv=errorObject->InitWithWindowID(message,NS_ConvertUTF8toUTF16(mScriptFile),EmptyString(),mScriptLine,mScriptColumn,nsIScriptError::errorFlag,"Web Socket",mInnerWindowID);}else{rv=errorObject->Init(message,NS_ConvertUTF8toUTF16(mScriptFile),EmptyString(),mScriptLine,mScriptColumn,nsIScriptError::errorFlag,"Web Socket");}NS_ENSURE_SUCCESS_VOID(rv);// print the error message directly to the JS consolerv=console->LogMessage(errorObject);NS_ENSURE_SUCCESS_VOID(rv);}namespace{classCancelWebSocketRunnablefinal:publicnsRunnable{public:CancelWebSocketRunnable(nsIWebSocketChannel*aChannel,uint16_taReasonCode,constnsACString&aReasonString):mChannel(aChannel),mReasonCode(aReasonCode),mReasonString(aReasonString){}NS_IMETHODRun()override{mChannel->Close(mReasonCode,mReasonString);returnNS_OK;}private:nsCOMPtr<nsIWebSocketChannel>mChannel;uint16_tmReasonCode;nsCStringmReasonString;};classMOZ_STACK_CLASSMaybeDisconnect{public:explicitMaybeDisconnect(WebSocketImpl*aImpl):mImpl(aImpl){}~MaybeDisconnect(){booltoDisconnect=false;{MutexAutoLocklock(mImpl->mMutex);toDisconnect=mImpl->mWorkerShuttingDown;}if(toDisconnect){mImpl->Disconnect();}}private:WebSocketImpl*mImpl;};classCloseConnectionRunnablefinal:publicnsRunnable{public:CloseConnectionRunnable(WebSocketImpl*aImpl,uint16_taReasonCode,constnsACString&aReasonString):mImpl(aImpl),mReasonCode(aReasonCode),mReasonString(aReasonString){}NS_IMETHODRun()override{returnmImpl->CloseConnection(mReasonCode,mReasonString);}private:RefPtr<WebSocketImpl>mImpl;uint16_tmReasonCode;constnsCStringmReasonString;};}// namespacensresultWebSocketImpl::CloseConnection(uint16_taReasonCode,constnsACString&aReasonString){if(!IsTargetThread()){RefPtr<nsRunnable>runnable=newCloseConnectionRunnable(this,aReasonCode,aReasonString);returnDispatch(runnable,NS_DISPATCH_NORMAL);}AssertIsOnTargetThread();if(mDisconnectingOrDisconnected){returnNS_OK;}// If this method is called because the worker is going away, we will not// receive the OnStop() method and we have to disconnect the WebSocket and// release the WorkerFeature.MaybeDisconnectmd(this);uint16_treadyState=mWebSocket->ReadyState();if(readyState==WebSocket::CLOSING||readyState==WebSocket::CLOSED){returnNS_OK;}// The common case...if(mChannel){mWebSocket->SetReadyState(WebSocket::CLOSING);// The channel has to be closed on the main-thread.if(NS_IsMainThread()){returnmChannel->Close(aReasonCode,aReasonString);}RefPtr<CancelWebSocketRunnable>runnable=newCancelWebSocketRunnable(mChannel,aReasonCode,aReasonString);returnNS_DispatchToMainThread(runnable);}// No channel, but not disconnected: canceled or failed earlyMOZ_ASSERT(readyState==WebSocket::CONNECTING,"Should only get here for early websocket cancel/error");// Server won't be sending us a close code, so use what's passed in here.mCloseEventCode=aReasonCode;CopyUTF8toUTF16(aReasonString,mCloseEventReason);mWebSocket->SetReadyState(WebSocket::CLOSING);ScheduleConnectionCloseEvents(nullptr,(aReasonCode==nsIWebSocketChannel::CLOSE_NORMAL||aReasonCode==nsIWebSocketChannel::CLOSE_GOING_AWAY)?NS_OK:NS_ERROR_FAILURE);returnNS_OK;}nsresultWebSocketImpl::ConsoleError(){AssertIsOnTargetThread();{MutexAutoLocklock(mMutex);if(mWorkerShuttingDown){// Too late to report anything, bail out.returnNS_OK;}}NS_ConvertUTF8toUTF16specUTF16(mURI);constchar16_t*formatStrings[]={specUTF16.get()};if(mWebSocket->ReadyState()<WebSocket::OPEN){PrintErrorOnConsole("chrome://global/locale/appstrings.properties",MOZ_UTF16("connectionFailure"),formatStrings,ArrayLength(formatStrings));}else{PrintErrorOnConsole("chrome://global/locale/appstrings.properties",MOZ_UTF16("netInterrupt"),formatStrings,ArrayLength(formatStrings));}/// todo some specific errors - like for message too largereturnNS_OK;}voidWebSocketImpl::FailConnection(uint16_taReasonCode,constnsACString&aReasonString){AssertIsOnTargetThread();if(mDisconnectingOrDisconnected){return;}ConsoleError();mFailed=true;CloseConnection(aReasonCode,aReasonString);}namespace{classDisconnectInternalRunnablefinal:publicWorkerMainThreadRunnable{public:explicitDisconnectInternalRunnable(WebSocketImpl*aImpl):WorkerMainThreadRunnable(aImpl->mWorkerPrivate),mImpl(aImpl){}boolMainThreadRun()override{mImpl->DisconnectInternal();returntrue;}private:// A raw pointer because this runnable is sync.WebSocketImpl*mImpl;};}// namespacevoidWebSocketImpl::Disconnect(){if(mDisconnectingOrDisconnected){return;}AssertIsOnTargetThread();// Disconnect can be called from some control event (such as Notify() of// WorkerFeature). This will be schedulated before any other sync/async// runnable. In order to prevent some double Disconnect() calls, we use this// boolean.mDisconnectingOrDisconnected=true;// DisconnectInternal touches observers and nsILoadGroup and it must run on// the main thread.if(NS_IsMainThread()){DisconnectInternal();}else{RefPtr<DisconnectInternalRunnable>runnable=newDisconnectInternalRunnable(this);ErrorResultrv;runnable->Dispatch(rv);// XXXbz this seems totally broken. We should be propagating this out, but// where to, exactly?rv.SuppressException();}// DontKeepAliveAnyMore() can release the object. So hold a reference to this// until the end of the method.RefPtr<WebSocketImpl>kungfuDeathGrip=this;NS_ReleaseOnMainThread(mChannel.forget());NS_ReleaseOnMainThread(mService.forget());mWebSocket->DontKeepAliveAnyMore();mWebSocket->mImpl=nullptr;if(mWorkerPrivate&&mWorkerFeature){UnregisterFeature();}// We want to release the WebSocket in the correct thread.mWebSocket=nullptr;}voidWebSocketImpl::DisconnectInternal(){AssertIsOnMainThread();nsCOMPtr<nsILoadGroup>loadGroup=do_QueryReferent(mWeakLoadGroup);if(loadGroup){loadGroup->RemoveRequest(this,nullptr,NS_OK);// mWeakLoadGroup has to be release on main-thread because WeakReferences// are not thread-safe.mWeakLoadGroup=nullptr;}if(!mWorkerPrivate){nsCOMPtr<nsIObserverService>os=mozilla::services::GetObserverService();if(os){os->RemoveObserver(this,DOM_WINDOW_DESTROYED_TOPIC);os->RemoveObserver(this,DOM_WINDOW_FROZEN_TOPIC);}}}//-----------------------------------------------------------------------------// WebSocketImpl::nsIWebSocketListener methods://-----------------------------------------------------------------------------nsresultWebSocketImpl::DoOnMessageAvailable(constnsACString&aMsg,boolisBinary){AssertIsOnTargetThread();if(mDisconnectingOrDisconnected){returnNS_OK;}int16_treadyState=mWebSocket->ReadyState();if(readyState==WebSocket::CLOSED){NS_ERROR("Received message after CLOSED");returnNS_ERROR_UNEXPECTED;}if(readyState==WebSocket::OPEN){// Dispatch New Messagensresultrv=mWebSocket->CreateAndDispatchMessageEvent(aMsg,isBinary);if(NS_FAILED(rv)){NS_WARNING("Failed to dispatch the message event");}returnNS_OK;}// CLOSING should be the only other state where it's possible to get msgs// from channel: Spec says to drop them.MOZ_ASSERT(readyState==WebSocket::CLOSING,"Received message while CONNECTING or CLOSED");returnNS_OK;}NS_IMETHODIMPWebSocketImpl::OnMessageAvailable(nsISupports*aContext,constnsACString&aMsg){AssertIsOnTargetThread();if(mDisconnectingOrDisconnected){returnNS_OK;}returnDoOnMessageAvailable(aMsg,false);}NS_IMETHODIMPWebSocketImpl::OnBinaryMessageAvailable(nsISupports*aContext,constnsACString&aMsg){AssertIsOnTargetThread();if(mDisconnectingOrDisconnected){returnNS_OK;}returnDoOnMessageAvailable(aMsg,true);}NS_IMETHODIMPWebSocketImpl::OnStart(nsISupports*aContext){AssertIsOnTargetThread();if(mDisconnectingOrDisconnected){returnNS_OK;}int16_treadyState=mWebSocket->ReadyState();// This is the only function that sets OPEN, and should be called only onceMOZ_ASSERT(readyState!=WebSocket::OPEN,"readyState already OPEN! OnStart called twice?");// Nothing to do if we've already closed/closingif(readyState!=WebSocket::CONNECTING){returnNS_OK;}// Attempt to kill "ghost" websocket: but usually too early for check to failnsresultrv=mWebSocket->CheckInnerWindowCorrectness();if(NS_FAILED(rv)){CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY);returnrv;}if(!mRequestedProtocolList.IsEmpty()){mChannel->GetProtocol(mWebSocket->mEstablishedProtocol);}mChannel->GetExtensions(mWebSocket->mEstablishedExtensions);UpdateURI();mWebSocket->SetReadyState(WebSocket::OPEN);mService->WebSocketOpened(mChannel->Serial(),mInnerWindowID,mWebSocket->mEffectiveURL,mWebSocket->mEstablishedProtocol,mWebSocket->mEstablishedExtensions);// Let's keep the object alive because the webSocket can be CCed in the// onopen callback.RefPtr<WebSocket>webSocket=mWebSocket;// Call 'onopen'rv=webSocket->CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("open"));if(NS_FAILED(rv)){NS_WARNING("Failed to dispatch the open event");}webSocket->UpdateMustKeepAlive();returnNS_OK;}NS_IMETHODIMPWebSocketImpl::OnStop(nsISupports*aContext,nsresultaStatusCode){AssertIsOnTargetThread();if(mDisconnectingOrDisconnected){returnNS_OK;}// We can be CONNECTING here if connection failed.// We can be OPEN if we have encountered a fatal protocol error// We can be CLOSING if close() was called and/or server initiated close.MOZ_ASSERT(mWebSocket->ReadyState()!=WebSocket::CLOSED,"Shouldn't already be CLOSED when OnStop called");returnScheduleConnectionCloseEvents(aContext,aStatusCode);}nsresultWebSocketImpl::ScheduleConnectionCloseEvents(nsISupports*aContext,nsresultaStatusCode){AssertIsOnTargetThread();// no-op if some other code has already initiated close eventif(!mOnCloseScheduled){mCloseEventWasClean=NS_SUCCEEDED(aStatusCode);if(aStatusCode==NS_BASE_STREAM_CLOSED){// don't generate an error event just because of an unclean closeaStatusCode=NS_OK;}if(NS_FAILED(aStatusCode)){ConsoleError();mFailed=true;}mOnCloseScheduled=true;NS_DispatchToCurrentThread(newCallDispatchConnectionCloseEvents(this));}returnNS_OK;}NS_IMETHODIMPWebSocketImpl::OnAcknowledge(nsISupports*aContext,uint32_taSize){AssertIsOnTargetThread();if(mDisconnectingOrDisconnected){returnNS_OK;}if(aSize>mWebSocket->mOutgoingBufferedAmount){returnNS_ERROR_UNEXPECTED;}mWebSocket->mOutgoingBufferedAmount-=aSize;returnNS_OK;}NS_IMETHODIMPWebSocketImpl::OnServerClose(nsISupports*aContext,uint16_taCode,constnsACString&aReason){AssertIsOnTargetThread();if(mDisconnectingOrDisconnected){returnNS_OK;}int16_treadyState=mWebSocket->ReadyState();MOZ_ASSERT(readyState!=WebSocket::CONNECTING,"Received server close before connected?");MOZ_ASSERT(readyState!=WebSocket::CLOSED,"Received server close after already closed!");// store code/string for onclose DOM eventmCloseEventCode=aCode;CopyUTF8toUTF16(aReason,mCloseEventReason);if(readyState==WebSocket::OPEN){// Server initiating close.// RFC 6455, 5.5.1: "When sending a Close frame in response, the endpoint// typically echos the status code it received".// But never send certain codes, per section 7.4.1if(aCode==1005||aCode==1006||aCode==1015){CloseConnection(0,EmptyCString());}else{CloseConnection(aCode,aReason);}}else{// We initiated close, and server has replied: OnStop does rest of the work.MOZ_ASSERT(readyState==WebSocket::CLOSING,"unknown state");}returnNS_OK;}//-----------------------------------------------------------------------------// WebSocketImpl::nsIInterfaceRequestor//-----------------------------------------------------------------------------NS_IMETHODIMPWebSocketImpl::GetInterface(constnsIID&aIID,void**aResult){AssertIsOnMainThread();if(!mWebSocket||mWebSocket->ReadyState()==WebSocket::CLOSED){returnNS_ERROR_FAILURE;}if(aIID.Equals(NS_GET_IID(nsIAuthPrompt))||aIID.Equals(NS_GET_IID(nsIAuthPrompt2))){nsresultrv;nsIScriptContext*sc=mWebSocket->GetContextForEventHandlers(&rv);nsCOMPtr<nsIDocument>doc=nsContentUtils::GetDocumentFromScriptContext(sc);if(!doc){returnNS_ERROR_NOT_AVAILABLE;}nsCOMPtr<nsIPromptFactory>wwatch=do_GetService(NS_WINDOWWATCHER_CONTRACTID,&rv);NS_ENSURE_SUCCESS(rv,rv);nsCOMPtr<nsPIDOMWindowOuter>outerWindow=doc->GetWindow();returnwwatch->GetPrompt(outerWindow,aIID,aResult);}returnQueryInterface(aIID,aResult);}////////////////////////////////////////////////////////////////////////////////// WebSocket////////////////////////////////////////////////////////////////////////////////WebSocket::WebSocket(nsPIDOMWindowInner*aOwnerWindow):DOMEventTargetHelper(aOwnerWindow),mIsMainThread(true),mKeepingAlive(false),mCheckMustKeepAlive(true),mOutgoingBufferedAmount(0),mBinaryType(dom::BinaryType::Blob),mMutex("WebSocket::mMutex"),mReadyState(CONNECTING){mImpl=newWebSocketImpl(this);mIsMainThread=mImpl->mIsMainThread;}WebSocket::~WebSocket(){}JSObject*WebSocket::WrapObject(JSContext*cx,JS::Handle<JSObject*>aGivenProto){returnWebSocketBinding::Wrap(cx,this,aGivenProto);}//---------------------------------------------------------------------------// WebIDL//---------------------------------------------------------------------------// Constructor:already_AddRefed<WebSocket>WebSocket::Constructor(constGlobalObject&aGlobal,constnsAString&aUrl,ErrorResult&aRv){Sequence<nsString>protocols;returnWebSocket::Constructor(aGlobal,aUrl,protocols,aRv);}already_AddRefed<WebSocket>WebSocket::Constructor(constGlobalObject&aGlobal,constnsAString&aUrl,constnsAString&aProtocol,ErrorResult&aRv){Sequence<nsString>protocols;if(!protocols.AppendElement(aProtocol,fallible)){aRv.Throw(NS_ERROR_OUT_OF_MEMORY);returnnullptr;}returnWebSocket::Constructor(aGlobal,aUrl,protocols,aRv);}namespace{// This class is used to clear any exception.classMOZ_STACK_CLASSClearException{public:explicitClearException(JSContext*aCx):mCx(aCx){}~ClearException(){JS_ClearPendingException(mCx);}private:JSContext*mCx;};classWebSocketMainThreadRunnable:publicWorkerMainThreadRunnable{public:WebSocketMainThreadRunnable(WorkerPrivate*aWorkerPrivate):WorkerMainThreadRunnable(aWorkerPrivate){MOZ_ASSERT(aWorkerPrivate);aWorkerPrivate->AssertIsOnWorkerThread();}boolMainThreadRun()override{AssertIsOnMainThread();// Walk up to our containing pageWorkerPrivate*wp=mWorkerPrivate;while(wp->GetParent()){wp=wp->GetParent();}nsPIDOMWindowInner*window=wp->GetWindow();if(window){returnInitWithWindow(window);}returnInitWindowless(wp);}protected:virtualboolInitWithWindow(nsPIDOMWindowInner*aWindow)=0;virtualboolInitWindowless(WorkerPrivate*aTopLevelWorkerPrivate)=0;};classInitRunnablefinal:publicWebSocketMainThreadRunnable{public:InitRunnable(WebSocketImpl*aImpl,constnsAString&aURL,nsTArray<nsString>&aProtocolArray,constnsACString&aScriptFile,uint32_taScriptLine,uint32_taScriptColumn,ErrorResult&aRv,bool*aConnectionFailed):WebSocketMainThreadRunnable(aImpl->mWorkerPrivate),mImpl(aImpl),mURL(aURL),mProtocolArray(aProtocolArray),mScriptFile(aScriptFile),mScriptLine(aScriptLine),mScriptColumn(aScriptColumn),mRv(aRv),mConnectionFailed(aConnectionFailed){MOZ_ASSERT(mWorkerPrivate);mWorkerPrivate->AssertIsOnWorkerThread();}protected:virtualboolInitWithWindow(nsPIDOMWindowInner*aWindow)override{AutoJSAPIjsapi;if(NS_WARN_IF(!jsapi.Init(aWindow))){mRv.Throw(NS_ERROR_FAILURE);returntrue;}ClearExceptionce(jsapi.cx());nsIDocument*doc=aWindow->GetExtantDoc();if(!doc){mRv.Throw(NS_ERROR_FAILURE);returntrue;}nsCOMPtr<nsIPrincipal>principal=doc->NodePrincipal();if(!principal){mRv.Throw(NS_ERROR_FAILURE);returntrue;}mImpl->Init(jsapi.cx(),principal,mURL,mProtocolArray,mScriptFile,mScriptLine,mScriptColumn,mRv,mConnectionFailed);returntrue;}virtualboolInitWindowless(WorkerPrivate*aTopLevelWorkerPrivate)override{MOZ_ASSERT(NS_IsMainThread());MOZ_ASSERT(aTopLevelWorkerPrivate&&!aTopLevelWorkerPrivate->GetWindow());mImpl->Init(nullptr,aTopLevelWorkerPrivate->GetPrincipal(),mURL,mProtocolArray,mScriptFile,mScriptLine,mScriptColumn,mRv,mConnectionFailed);returntrue;}// Raw pointer. This worker runs synchronously.WebSocketImpl*mImpl;constnsAString&mURL;nsTArray<nsString>&mProtocolArray;nsCStringmScriptFile;uint32_tmScriptLine;uint32_tmScriptColumn;ErrorResult&mRv;bool*mConnectionFailed;};classAsyncOpenRunnablefinal:publicWebSocketMainThreadRunnable{public:AsyncOpenRunnable(WebSocketImpl*aImpl,ErrorResult&aRv):WebSocketMainThreadRunnable(aImpl->mWorkerPrivate),mImpl(aImpl),mRv(aRv){MOZ_ASSERT(mWorkerPrivate);mWorkerPrivate->AssertIsOnWorkerThread();}protected:virtualboolInitWithWindow(nsPIDOMWindowInner*aWindow)override{AssertIsOnMainThread();MOZ_ASSERT(aWindow);nsIDocument*doc=aWindow->GetExtantDoc();if(!doc){mRv.Throw(NS_ERROR_FAILURE);returntrue;}nsCOMPtr<nsIPrincipal>principal=doc->NodePrincipal();if(!principal){mRv.Throw(NS_ERROR_FAILURE);returntrue;}uint64_twindowID=0;nsCOMPtr<nsPIDOMWindowOuter>topWindow=aWindow->GetScriptableTop();nsCOMPtr<nsPIDOMWindowInner>topInner;if(topWindow){topInner=topWindow->GetCurrentInnerWindow();}if(topInner){windowID=topInner->WindowID();}mImpl->AsyncOpen(principal,windowID,mRv);returntrue;}virtualboolInitWindowless(WorkerPrivate*aTopLevelWorkerPrivate)override{MOZ_ASSERT(NS_IsMainThread());MOZ_ASSERT(aTopLevelWorkerPrivate&&!aTopLevelWorkerPrivate->GetWindow());mImpl->AsyncOpen(aTopLevelWorkerPrivate->GetPrincipal(),0,mRv);returntrue;}private:// Raw pointer. This worker runs synchronously.WebSocketImpl*mImpl;ErrorResult&mRv;};}// namespacealready_AddRefed<WebSocket>WebSocket::Constructor(constGlobalObject&aGlobal,constnsAString&aUrl,constSequence<nsString>&aProtocols,ErrorResult&aRv){nsCOMPtr<nsIPrincipal>principal;nsCOMPtr<nsPIDOMWindowInner>ownerWindow;if(NS_IsMainThread()){nsCOMPtr<nsIScriptObjectPrincipal>scriptPrincipal=do_QueryInterface(aGlobal.GetAsSupports());if(!scriptPrincipal){aRv.Throw(NS_ERROR_FAILURE);returnnullptr;}principal=scriptPrincipal->GetPrincipal();if(!principal){aRv.Throw(NS_ERROR_FAILURE);returnnullptr;}nsCOMPtr<nsIScriptGlobalObject>sgo=do_QueryInterface(aGlobal.GetAsSupports());if(!sgo){aRv.Throw(NS_ERROR_FAILURE);returnnullptr;}ownerWindow=do_QueryInterface(aGlobal.GetAsSupports());if(!ownerWindow){aRv.Throw(NS_ERROR_FAILURE);returnnullptr;}}MOZ_ASSERT_IF(ownerWindow,ownerWindow->IsInnerWindow());nsTArray<nsString>protocolArray;for(uint32_tindex=0,len=aProtocols.Length();index<len;++index){constnsString&protocolElement=aProtocols[index];if(protocolElement.IsEmpty()){aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);returnnullptr;}if(protocolArray.Contains(protocolElement)){aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);returnnullptr;}if(protocolElement.FindChar(',')!=-1)/* interferes w/list */{aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);returnnullptr;}protocolArray.AppendElement(protocolElement);}RefPtr<WebSocket>webSocket=newWebSocket(ownerWindow);RefPtr<WebSocketImpl>kungfuDeathGrip=webSocket->mImpl;boolconnectionFailed=true;if(NS_IsMainThread()){webSocket->mImpl->Init(aGlobal.Context(),principal,aUrl,protocolArray,EmptyCString(),0,0,aRv,&connectionFailed);}else{// In workers we have to keep the worker alive using a feature in order to// dispatch messages correctly.if(!webSocket->mImpl->RegisterFeature()){aRv.Throw(NS_ERROR_FAILURE);returnnullptr;}unsignedlineno,column;JS::AutoFilenamefile;if(!JS::DescribeScriptedCaller(aGlobal.Context(),&file,&lineno,&column)){NS_WARNING("Failed to get line number and filename in workers.");}RefPtr<InitRunnable>runnable=newInitRunnable(webSocket->mImpl,aUrl,protocolArray,nsAutoCString(file.get()),lineno,column,aRv,&connectionFailed);runnable->Dispatch(aRv);}if(NS_WARN_IF(aRv.Failed())){returnnullptr;}// It can be that we have been already disconnected because the WebSocket is// gone away while we where initializing the webSocket.if(!webSocket->mImpl){aRv.Throw(NS_ERROR_FAILURE);returnnullptr;}// We don't return an error if the connection just failed. Instead we dispatch// an event.if(connectionFailed){webSocket->mImpl->FailConnection(nsIWebSocketChannel::CLOSE_ABNORMAL);}// If we don't have a channel, the connection is failed and onerror() will be// called asynchrounsly.if(!webSocket->mImpl->mChannel){returnwebSocket.forget();}classMOZ_STACK_CLASSClearWebSocket{public:explicitClearWebSocket(WebSocketImpl*aWebSocketImpl):mWebSocketImpl(aWebSocketImpl),mDone(false){}voidDone(){mDone=true;}~ClearWebSocket(){if(!mDone){mWebSocketImpl->mChannel=nullptr;mWebSocketImpl->FailConnection(nsIWebSocketChannel::CLOSE_ABNORMAL);}}WebSocketImpl*mWebSocketImpl;boolmDone;};ClearWebSocketcws(webSocket->mImpl);// This operation must be done on the correct thread. The rest must run on the// main-thread.aRv=webSocket->mImpl->mChannel->SetNotificationCallbacks(webSocket->mImpl);if(NS_WARN_IF(aRv.Failed())){returnnullptr;}if(NS_IsMainThread()){MOZ_ASSERT(principal);nsPIDOMWindowOuter*outerWindow=ownerWindow->GetOuterWindow();uint64_twindowID=0;nsCOMPtr<nsPIDOMWindowOuter>topWindow=outerWindow->GetScriptableTop();nsCOMPtr<nsPIDOMWindowInner>topInner;if(topWindow){topInner=topWindow->GetCurrentInnerWindow();}if(topInner){windowID=topInner->WindowID();}webSocket->mImpl->AsyncOpen(principal,windowID,aRv);}else{RefPtr<AsyncOpenRunnable>runnable=newAsyncOpenRunnable(webSocket->mImpl,aRv);runnable->Dispatch(aRv);}if(NS_WARN_IF(aRv.Failed())){returnnullptr;}// It can be that we have been already disconnected because the WebSocket is// gone away while we where initializing the webSocket.if(!webSocket->mImpl){aRv.Throw(NS_ERROR_FAILURE);returnnullptr;}// Let's inform devtools about this new active WebSocket.webSocket->mImpl->mService->WebSocketCreated(webSocket->mImpl->mChannel->Serial(),webSocket->mImpl->mInnerWindowID,webSocket->mURI,webSocket->mImpl->mRequestedProtocolList);cws.Done();returnwebSocket.forget();}NS_IMPL_CYCLE_COLLECTION_CLASS(WebSocket)NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(WebSocket)boolisBlack=tmp->IsBlack();if(isBlack||tmp->mKeepingAlive){if(tmp->mListenerManager){tmp->mListenerManager->MarkForCC();}if(!isBlack&&tmp->PreservingWrapper()){// This marks the wrapper black.tmp->GetWrapper();}returntrue;}NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_ENDNS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(WebSocket)returntmp->IsBlack();NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_ENDNS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(WebSocket)returntmp->IsBlack();NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_ENDNS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WebSocket,DOMEventTargetHelper)NS_IMPL_CYCLE_COLLECTION_TRACE_ENDNS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WebSocket,DOMEventTargetHelper)if(tmp->mImpl){NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mImpl->mChannel)}NS_IMPL_CYCLE_COLLECTION_TRAVERSE_ENDNS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WebSocket,DOMEventTargetHelper)if(tmp->mImpl){NS_IMPL_CYCLE_COLLECTION_UNLINK(mImpl->mChannel)tmp->mImpl->Disconnect();MOZ_ASSERT(!tmp->mImpl);}NS_IMPL_CYCLE_COLLECTION_UNLINK_ENDNS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WebSocket)NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)NS_IMPL_ADDREF_INHERITED(WebSocket,DOMEventTargetHelper)NS_IMPL_RELEASE_INHERITED(WebSocket,DOMEventTargetHelper)voidWebSocket::DisconnectFromOwner(){AssertIsOnMainThread();DOMEventTargetHelper::DisconnectFromOwner();if(mImpl){mImpl->CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY);}DontKeepAliveAnyMore();}//-----------------------------------------------------------------------------// WebSocketImpl:: initialization//-----------------------------------------------------------------------------voidWebSocketImpl::Init(JSContext*aCx,nsIPrincipal*aPrincipal,constnsAString&aURL,nsTArray<nsString>&aProtocolArray,constnsACString&aScriptFile,uint32_taScriptLine,uint32_taScriptColumn,ErrorResult&aRv,bool*aConnectionFailed){AssertIsOnMainThread();MOZ_ASSERT(aPrincipal);mService=WebSocketEventService::GetOrCreate();// We need to keep the implementation alive in case the init disconnects it// because of some error.RefPtr<WebSocketImpl>kungfuDeathGrip=this;// Attempt to kill "ghost" websocket: but usually too early for check to failaRv=mWebSocket->CheckInnerWindowCorrectness();if(NS_WARN_IF(aRv.Failed())){return;}// Shut down websocket if window is frozen or destroyed (only needed for// "ghost" websockets--see bug 696085)if(!mWorkerPrivate){nsCOMPtr<nsIObserverService>os=mozilla::services::GetObserverService();if(NS_WARN_IF(!os)){aRv.Throw(NS_ERROR_FAILURE);return;}aRv=os->AddObserver(this,DOM_WINDOW_DESTROYED_TOPIC,true);if(NS_WARN_IF(aRv.Failed())){return;}aRv=os->AddObserver(this,DOM_WINDOW_FROZEN_TOPIC,true);if(NS_WARN_IF(aRv.Failed())){return;}}if(mWorkerPrivate){mScriptFile=aScriptFile;mScriptLine=aScriptLine;mScriptColumn=aScriptColumn;}else{MOZ_ASSERT(aCx);unsignedlineno,column;JS::AutoFilenamefile;if(JS::DescribeScriptedCaller(aCx,&file,&lineno,&column)){mScriptFile=file.get();mScriptLine=lineno;mScriptColumn=column;}}// If we don't have aCx, we are window-less, so we don't have a// inner-windowID. This can happen in sharedWorkers and ServiceWorkers or in// DedicateWorkers created by JSM.if(aCx){mInnerWindowID=nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(aCx);}// parses the urlaRv=ParseURL(PromiseFlatString(aURL));if(NS_WARN_IF(aRv.Failed())){return;}nsIScriptContext*sc=nullptr;{nsresultrv;sc=mWebSocket->GetContextForEventHandlers(&rv);if(NS_WARN_IF(NS_FAILED(rv))){aRv.Throw(rv);return;}}nsCOMPtr<nsIURI>uri;{nsresultrv=NS_NewURI(getter_AddRefs(uri),mURI);// We crash here because we are sure that mURI is a valid URI, so either we// are OOM'ing or something else bad is happening.if(NS_WARN_IF(NS_FAILED(rv))){MOZ_CRASH();}}// Check content policy.int16_tshouldLoad=nsIContentPolicy::ACCEPT;nsCOMPtr<nsIDocument>originDoc=nsContentUtils::GetDocumentFromScriptContext(sc);mOriginDocument=do_GetWeakReference(originDoc);aRv=NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_WEBSOCKET,uri,aPrincipal,originDoc,EmptyCString(),nullptr,&shouldLoad,nsContentUtils::GetContentPolicy(),nsContentUtils::GetSecurityManager());if(NS_WARN_IF(aRv.Failed())){return;}if(NS_CP_REJECTED(shouldLoad)){// Disallowed by content policy.aRv.Throw(NS_ERROR_CONTENT_BLOCKED);return;}// Potentially the page uses the CSP directive 'upgrade-insecure-requests'.// In such a case we have to upgrade ws: to wss: and also update mSecure// to reflect that upgrade. Please note that we can not upgrade from ws:// to wss: before performing content policy checks because CSP needs to// send reports in case the scheme is about to be upgraded.if(!mSecure&&originDoc&&originDoc->GetUpgradeInsecureRequests(false)){// let's use the old specification before the upgrade for loggingNS_ConvertUTF8toUTF16reportSpec(mURI);// upgrade the request from ws:// to wss:// and mark as securemURI.ReplaceSubstring("ws://","wss://");if(NS_WARN_IF(mURI.Find("wss://")!=0)){return;}mSecure=true;constchar16_t*params[]={reportSpec.get(),MOZ_UTF16("wss")};CSP_LogLocalizedStr(MOZ_UTF16("upgradeInsecureRequest"),params,ArrayLength(params),EmptyString(),// aSourceFileEmptyString(),// aScriptSample0,// aLineNumber0,// aColumnNumbernsIScriptError::warningFlag,"CSP",mInnerWindowID);}// Don't allow https:// to open ws://if(!mSecure&&!Preferences::GetBool("network.websocket.allowInsecureFromHTTPS",false)){// Confirmed we are opening plain ws:// and want to prevent this from a// secure context (e.g. https).nsCOMPtr<nsIPrincipal>principal;nsCOMPtr<nsIURI>originURI;if(mWorkerPrivate){// For workers, retrieve the URI from the WorkerPrivateprincipal=mWorkerPrivate->GetPrincipal();}else{// Check the principal's uri to determine if we were loaded from https.nsCOMPtr<nsIGlobalObject>globalObject(GetEntryGlobal());if(globalObject){principal=globalObject->PrincipalOrNull();}nsCOMPtr<nsPIDOMWindowInner>innerWindow;while(true){boolisNullPrincipal=true;if(principal){nsresultrv=principal->GetIsNullPrincipal(&isNullPrincipal);if(NS_WARN_IF(NS_FAILED(rv))){aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);return;}}if(!isNullPrincipal){break;}if(!innerWindow){innerWindow=do_QueryInterface(globalObject);if(NS_WARN_IF(!innerWindow)){aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);return;}}nsCOMPtr<nsPIDOMWindowOuter>parentWindow=innerWindow->GetScriptableParent();if(NS_WARN_IF(!parentWindow)){aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);return;}nsCOMPtr<nsPIDOMWindowInner>currentInnerWindow=parentWindow->GetCurrentInnerWindow();if(NS_WARN_IF(!currentInnerWindow)){aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);return;}// We are at the top. Let's see if we have an opener window.if(innerWindow==currentInnerWindow){ErrorResulterror;parentWindow=nsGlobalWindow::Cast(innerWindow)->GetOpenerWindow(error);if(NS_WARN_IF(error.Failed())){error.SuppressException();aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);return;}if(!parentWindow){break;}currentInnerWindow=parentWindow->GetCurrentInnerWindow();if(NS_WARN_IF(!currentInnerWindow)){aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);return;}MOZ_ASSERT(currentInnerWindow!=innerWindow);}innerWindow=currentInnerWindow;nsCOMPtr<nsIDocument>document=innerWindow->GetExtantDoc();if(NS_WARN_IF(!document)){aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);return;}principal=document->NodePrincipal();}}if(principal){principal->GetURI(getter_AddRefs(originURI));}if(originURI){booloriginIsHttps=false;aRv=originURI->SchemeIs("https",&originIsHttps);if(NS_WARN_IF(aRv.Failed())){return;}if(originIsHttps){aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);return;}}}// Assign the sub protocol list and scan it for illegal valuesfor(uint32_tindex=0;index<aProtocolArray.Length();++index){for(uint32_ti=0;i<aProtocolArray[index].Length();++i){if(aProtocolArray[index][i]<static_cast<char16_t>(0x0021)||aProtocolArray[index][i]>static_cast<char16_t>(0x007E)){aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);return;}}if(!mRequestedProtocolList.IsEmpty()){mRequestedProtocolList.AppendLiteral(", ");}AppendUTF16toUTF8(aProtocolArray[index],mRequestedProtocolList);}// the constructor should throw a SYNTAX_ERROR only if it fails to parse the// url parameter, so don't throw if InitializeConnection fails, and call// onerror/onclose asynchronouslyif(NS_FAILED(InitializeConnection(aPrincipal))){*aConnectionFailed=true;}else{*aConnectionFailed=false;}}voidWebSocketImpl::AsyncOpen(nsIPrincipal*aPrincipal,uint64_taInnerWindowID,ErrorResult&aRv){MOZ_ASSERT(NS_IsMainThread(),"Not running on main thread");nsCStringasciiOrigin;aRv=nsContentUtils::GetASCIIOrigin(aPrincipal,asciiOrigin);if(NS_WARN_IF(aRv.Failed())){return;}ToLowerCase(asciiOrigin);nsCOMPtr<nsIURI>uri;aRv=NS_NewURI(getter_AddRefs(uri),mURI);MOZ_ASSERT(!aRv.Failed());aRv=mChannel->AsyncOpen(uri,asciiOrigin,aInnerWindowID,this,nullptr);if(NS_WARN_IF(aRv.Failed())){return;}mInnerWindowID=aInnerWindowID;}//-----------------------------------------------------------------------------// WebSocketImpl methods://-----------------------------------------------------------------------------classnsAutoCloseWSfinal{public:explicitnsAutoCloseWS(WebSocketImpl*aWebSocketImpl):mWebSocketImpl(aWebSocketImpl){}~nsAutoCloseWS(){if(!mWebSocketImpl->mChannel){mWebSocketImpl->CloseConnection(nsIWebSocketChannel::CLOSE_INTERNAL_ERROR);}}private:RefPtr<WebSocketImpl>mWebSocketImpl;};nsresultWebSocketImpl::InitializeConnection(nsIPrincipal*aPrincipal){AssertIsOnMainThread();MOZ_ASSERT(!mChannel,"mChannel should be null");nsCOMPtr<nsIWebSocketChannel>wsChannel;nsAutoCloseWSautoClose(this);nsresultrv;if(mSecure){wsChannel=do_CreateInstance("@mozilla.org/network/protocol;1?name=wss",&rv);}else{wsChannel=do_CreateInstance("@mozilla.org/network/protocol;1?name=ws",&rv);}NS_ENSURE_SUCCESS(rv,rv);// add ourselves to the document's load group and// provide the http stack the loadgroup info toonsCOMPtr<nsILoadGroup>loadGroup;rv=GetLoadGroup(getter_AddRefs(loadGroup));if(loadGroup){rv=wsChannel->SetLoadGroup(loadGroup);NS_ENSURE_SUCCESS(rv,rv);rv=loadGroup->AddRequest(this,nullptr);NS_ENSURE_SUCCESS(rv,rv);mWeakLoadGroup=do_GetWeakReference(loadGroup);}// manually adding loadinfo to the channel since it// was not set during channel creation.nsCOMPtr<nsIDocument>doc=do_QueryReferent(mOriginDocument);// mOriginDocument has to be release on main-thread because WeakReferences// are not thread-safe.mOriginDocument=nullptr;wsChannel->InitLoadInfo(doc?doc->AsDOMNode():nullptr,doc?doc->NodePrincipal():aPrincipal,aPrincipal,nsILoadInfo::SEC_NORMAL,nsIContentPolicy::TYPE_WEBSOCKET);if(!mRequestedProtocolList.IsEmpty()){rv=wsChannel->SetProtocol(mRequestedProtocolList);NS_ENSURE_SUCCESS(rv,rv);}nsCOMPtr<nsIThreadRetargetableRequest>rr=do_QueryInterface(wsChannel);NS_ENSURE_TRUE(rr,NS_ERROR_FAILURE);rv=rr->RetargetDeliveryTo(this);NS_ENSURE_SUCCESS(rv,rv);mChannel=wsChannel;returnNS_OK;}voidWebSocketImpl::DispatchConnectionCloseEvents(){AssertIsOnTargetThread();if(mDisconnectingOrDisconnected){return;}mWebSocket->SetReadyState(WebSocket::CLOSED);// Let's keep the object alive because the webSocket can be CCed in the// onerror or in the onclose callback.RefPtr<WebSocket>webSocket=mWebSocket;// Call 'onerror' if neededif(mFailed){nsresultrv=webSocket->CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error"));if(NS_FAILED(rv)){NS_WARNING("Failed to dispatch the error event");}}nsresultrv=webSocket->CreateAndDispatchCloseEvent(mCloseEventWasClean,mCloseEventCode,mCloseEventReason);if(NS_FAILED(rv)){NS_WARNING("Failed to dispatch the close event");}webSocket->UpdateMustKeepAlive();Disconnect();}nsresultWebSocket::CreateAndDispatchSimpleEvent(constnsAString&aName){MOZ_ASSERT(mImpl);AssertIsOnTargetThread();nsresultrv=CheckInnerWindowCorrectness();if(NS_FAILED(rv)){returnNS_OK;}RefPtr<Event>event=NS_NewDOMEvent(this,nullptr,nullptr);// it doesn't bubble, and it isn't cancelableevent->InitEvent(aName,false,false);event->SetTrusted(true);returnDispatchDOMEvent(nullptr,event,nullptr,nullptr);}nsresultWebSocket::CreateAndDispatchMessageEvent(constnsACString&aData,boolaIsBinary){MOZ_ASSERT(mImpl);AssertIsOnTargetThread();AutoJSAPIjsapi;if(NS_IsMainThread()){if(NS_WARN_IF(!jsapi.Init(GetOwner()))){returnNS_ERROR_FAILURE;}}else{MOZ_ASSERT(!mIsMainThread);MOZ_ASSERT(mImpl->mWorkerPrivate);if(NS_WARN_IF(!jsapi.Init(mImpl->mWorkerPrivate->GlobalScope()))){returnNS_ERROR_FAILURE;}}jsapi.TakeOwnershipOfErrorReporting();JSContext*cx=jsapi.cx();nsresultrv=CheckInnerWindowCorrectness();if(NS_FAILED(rv)){returnNS_OK;}uint16_tmessageType=nsIWebSocketEventListener::TYPE_STRING;// Create appropriate JS object for messageJS::Rooted<JS::Value>jsData(cx);if(aIsBinary){if(mBinaryType==dom::BinaryType::Blob){messageType=nsIWebSocketEventListener::TYPE_BLOB;nsresultrv=nsContentUtils::CreateBlobBuffer(cx,GetOwner(),aData,&jsData);NS_ENSURE_SUCCESS(rv,rv);}elseif(mBinaryType==dom::BinaryType::Arraybuffer){messageType=nsIWebSocketEventListener::TYPE_ARRAYBUFFER;JS::Rooted<JSObject*>arrayBuf(cx);nsresultrv=nsContentUtils::CreateArrayBuffer(cx,aData,arrayBuf.address());NS_ENSURE_SUCCESS(rv,rv);jsData.setObject(*arrayBuf);}else{NS_RUNTIMEABORT("Unknown binary type!");returnNS_ERROR_UNEXPECTED;}}else{// JS stringNS_ConvertUTF8toUTF16utf16Data(aData);JSString*jsString;jsString=JS_NewUCStringCopyN(cx,utf16Data.get(),utf16Data.Length());NS_ENSURE_TRUE(jsString,NS_ERROR_FAILURE);jsData.setString(jsString);}mImpl->mService->WebSocketMessageAvailable(mImpl->mChannel->Serial(),mImpl->mInnerWindowID,aData,messageType);// create an event that uses the MessageEvent interface,// which does not bubble, is not cancelable, and has no default actionRefPtr<MessageEvent>event=NS_NewDOMMessageEvent(this,nullptr,nullptr);event->InitMessageEvent(nullptr,NS_LITERAL_STRING("message"),false,false,jsData,mImpl->mUTF16Origin,EmptyString(),nullptr,nullptr);event->SetTrusted(true);returnDispatchDOMEvent(nullptr,static_cast<Event*>(event),nullptr,nullptr);}nsresultWebSocket::CreateAndDispatchCloseEvent(boolaWasClean,uint16_taCode,constnsAString&aReason){MOZ_ASSERT(mImpl);AssertIsOnTargetThread();if(mImpl->mChannel){mImpl->mService->WebSocketClosed(mImpl->mChannel->Serial(),mImpl->mInnerWindowID,aWasClean,aCode,aReason);}nsresultrv=CheckInnerWindowCorrectness();if(NS_FAILED(rv)){returnNS_OK;}CloseEventInitinit;init.mBubbles=false;init.mCancelable=false;init.mWasClean=aWasClean;init.mCode=aCode;init.mReason=aReason;RefPtr<CloseEvent>event=CloseEvent::Constructor(this,NS_LITERAL_STRING("close"),init);event->SetTrusted(true);returnDispatchDOMEvent(nullptr,event,nullptr,nullptr);}nsresultWebSocketImpl::ParseURL(constnsAString&aURL){AssertIsOnMainThread();NS_ENSURE_TRUE(!aURL.IsEmpty(),NS_ERROR_DOM_SYNTAX_ERR);nsCOMPtr<nsIURI>uri;nsresultrv=NS_NewURI(getter_AddRefs(uri),aURL);NS_ENSURE_SUCCESS(rv,NS_ERROR_DOM_SYNTAX_ERR);nsCOMPtr<nsIURL>parsedURL=do_QueryInterface(uri,&rv);NS_ENSURE_SUCCESS(rv,NS_ERROR_DOM_SYNTAX_ERR);boolhasRef;rv=parsedURL->GetHasRef(&hasRef);NS_ENSURE_TRUE(NS_SUCCEEDED(rv)&&!hasRef,NS_ERROR_DOM_SYNTAX_ERR);nsAutoCStringscheme;rv=parsedURL->GetScheme(scheme);NS_ENSURE_TRUE(NS_SUCCEEDED(rv)&&!scheme.IsEmpty(),NS_ERROR_DOM_SYNTAX_ERR);nsAutoCStringhost;rv=parsedURL->GetAsciiHost(host);NS_ENSURE_TRUE(NS_SUCCEEDED(rv)&&!host.IsEmpty(),NS_ERROR_DOM_SYNTAX_ERR);int32_tport;rv=parsedURL->GetPort(&port);NS_ENSURE_SUCCESS(rv,NS_ERROR_DOM_SYNTAX_ERR);rv=NS_CheckPortSafety(port,scheme.get());NS_ENSURE_SUCCESS(rv,NS_ERROR_DOM_SECURITY_ERR);nsAutoCStringfilePath;rv=parsedURL->GetFilePath(filePath);if(filePath.IsEmpty()){filePath.Assign('/');}NS_ENSURE_SUCCESS(rv,NS_ERROR_DOM_SYNTAX_ERR);nsAutoCStringquery;rv=parsedURL->GetQuery(query);NS_ENSURE_SUCCESS(rv,NS_ERROR_DOM_SYNTAX_ERR);if(scheme.LowerCaseEqualsLiteral("ws")){mSecure=false;mPort=(port==-1)?DEFAULT_WS_SCHEME_PORT:port;}elseif(scheme.LowerCaseEqualsLiteral("wss")){mSecure=true;mPort=(port==-1)?DEFAULT_WSS_SCHEME_PORT:port;}else{returnNS_ERROR_DOM_SYNTAX_ERR;}rv=nsContentUtils::GetUTFOrigin(parsedURL,mUTF16Origin);NS_ENSURE_SUCCESS(rv,NS_ERROR_DOM_SYNTAX_ERR);mAsciiHost=host;ToLowerCase(mAsciiHost);mResource=filePath;if(!query.IsEmpty()){mResource.Append('?');mResource.Append(query);}uint32_tlength=mResource.Length();uint32_ti;for(i=0;i<length;++i){if(mResource[i]<static_cast<char16_t>(0x0021)||mResource[i]>static_cast<char16_t>(0x007E)){returnNS_ERROR_DOM_SYNTAX_ERR;}}rv=parsedURL->GetSpec(mURI);MOZ_ASSERT(NS_SUCCEEDED(rv));CopyUTF8toUTF16(mURI,mWebSocket->mURI);returnNS_OK;}//-----------------------------------------------------------------------------// Methods that keep alive the WebSocket object when:// 1. the object has registered event listeners that can be triggered// ("strong event listeners");// 2. there are outgoing not sent messages.//-----------------------------------------------------------------------------voidWebSocket::UpdateMustKeepAlive(){// Here we could not have mImpl.MOZ_ASSERT(NS_IsMainThread()==mIsMainThread);if(!mCheckMustKeepAlive||!mImpl){return;}boolshouldKeepAlive=false;uint16_treadyState=ReadyState();if(mListenerManager){switch(readyState){caseCONNECTING:{if(mListenerManager->HasListenersFor(nsGkAtoms::onopen)||mListenerManager->HasListenersFor(nsGkAtoms::onmessage)||mListenerManager->HasListenersFor(nsGkAtoms::onerror)||mListenerManager->HasListenersFor(nsGkAtoms::onclose)){shouldKeepAlive=true;}}break;caseOPEN:caseCLOSING:{if(mListenerManager->HasListenersFor(nsGkAtoms::onmessage)||mListenerManager->HasListenersFor(nsGkAtoms::onerror)||mListenerManager->HasListenersFor(nsGkAtoms::onclose)||mOutgoingBufferedAmount!=0){shouldKeepAlive=true;}}break;caseCLOSED:{shouldKeepAlive=false;}}}if(mKeepingAlive&&!shouldKeepAlive){mKeepingAlive=false;mImpl->ReleaseObject();}elseif(!mKeepingAlive&&shouldKeepAlive){mKeepingAlive=true;mImpl->AddRefObject();}}voidWebSocket::DontKeepAliveAnyMore(){// Here we could not have mImpl.MOZ_ASSERT(NS_IsMainThread()==mIsMainThread);if(mKeepingAlive){MOZ_ASSERT(mImpl);mKeepingAlive=false;mImpl->ReleaseObject();}mCheckMustKeepAlive=false;}namespace{classWebSocketWorkerFeaturefinal:publicWorkerFeature{public:explicitWebSocketWorkerFeature(WebSocketImpl*aWebSocketImpl):mWebSocketImpl(aWebSocketImpl){}boolNotify(JSContext*aCx,StatusaStatus)override{MOZ_ASSERT(aStatus>workers::Running);if(aStatus>=Canceling){{MutexAutoLocklock(mWebSocketImpl->mMutex);mWebSocketImpl->mWorkerShuttingDown=true;}mWebSocketImpl->CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY,EmptyCString());}returntrue;}private:WebSocketImpl*mWebSocketImpl;};}// namespacevoidWebSocketImpl::AddRefObject(){AssertIsOnTargetThread();AddRef();}voidWebSocketImpl::ReleaseObject(){AssertIsOnTargetThread();Release();}boolWebSocketImpl::RegisterFeature(){mWorkerPrivate->AssertIsOnWorkerThread();MOZ_ASSERT(!mWorkerFeature);mWorkerFeature=newWebSocketWorkerFeature(this);if(!mWorkerPrivate->AddFeature(mWorkerFeature)){NS_WARNING("Failed to register a feature.");mWorkerFeature=nullptr;returnfalse;}#ifdef DEBUGSetHasFeatureRegistered(true);#endifreturntrue;}voidWebSocketImpl::UnregisterFeature(){MOZ_ASSERT(mDisconnectingOrDisconnected);MOZ_ASSERT(mWorkerPrivate);mWorkerPrivate->AssertIsOnWorkerThread();MOZ_ASSERT(mWorkerFeature);mWorkerPrivate->RemoveFeature(mWorkerFeature);mWorkerFeature=nullptr;mWorkerPrivate=nullptr;#ifdef DEBUGSetHasFeatureRegistered(false);#endif}nsresultWebSocketImpl::UpdateURI(){AssertIsOnTargetThread();// Check for RedirectionsRefPtr<BaseWebSocketChannel>channel;channel=static_cast<BaseWebSocketChannel*>(mChannel.get());MOZ_ASSERT(channel);channel->GetEffectiveURL(mWebSocket->mEffectiveURL);mSecure=channel->IsEncrypted();returnNS_OK;}voidWebSocket::EventListenerAdded(nsIAtom*aType){AssertIsOnMainThread();UpdateMustKeepAlive();}voidWebSocket::EventListenerRemoved(nsIAtom*aType){AssertIsOnMainThread();UpdateMustKeepAlive();}//-----------------------------------------------------------------------------// WebSocket - methods//-----------------------------------------------------------------------------// webIDL: readonly attribute unsigned short readyState;uint16_tWebSocket::ReadyState(){MutexAutoLocklock(mMutex);returnmReadyState;}voidWebSocket::SetReadyState(uint16_taReadyState){MutexAutoLocklock(mMutex);mReadyState=aReadyState;}// webIDL: readonly attribute unsigned long bufferedAmount;uint32_tWebSocket::BufferedAmount()const{AssertIsOnTargetThread();returnmOutgoingBufferedAmount;}// webIDL: attribute BinaryType binaryType;dom::BinaryTypeWebSocket::BinaryType()const{AssertIsOnTargetThread();returnmBinaryType;}// webIDL: attribute BinaryType binaryType;voidWebSocket::SetBinaryType(dom::BinaryTypeaData){AssertIsOnTargetThread();mBinaryType=aData;}// webIDL: readonly attribute DOMString urlvoidWebSocket::GetUrl(nsAString&aURL){AssertIsOnTargetThread();if(mEffectiveURL.IsEmpty()){aURL=mURI;}else{aURL=mEffectiveURL;}}// webIDL: readonly attribute DOMString extensions;voidWebSocket::GetExtensions(nsAString&aExtensions){AssertIsOnTargetThread();CopyUTF8toUTF16(mEstablishedExtensions,aExtensions);}// webIDL: readonly attribute DOMString protocol;voidWebSocket::GetProtocol(nsAString&aProtocol){AssertIsOnTargetThread();CopyUTF8toUTF16(mEstablishedProtocol,aProtocol);}// webIDL: void send(DOMString data);voidWebSocket::Send(constnsAString&aData,ErrorResult&aRv){AssertIsOnTargetThread();NS_ConvertUTF16toUTF8msgString(aData);Send(nullptr,msgString,msgString.Length(),false,aRv);}voidWebSocket::Send(Blob&aData,ErrorResult&aRv){AssertIsOnTargetThread();nsCOMPtr<nsIInputStream>msgStream;aData.GetInternalStream(getter_AddRefs(msgStream),aRv);if(NS_WARN_IF(aRv.Failed())){return;}uint64_tmsgLength=aData.GetSize(aRv);if(NS_WARN_IF(aRv.Failed())){return;}if(msgLength>UINT32_MAX){aRv.Throw(NS_ERROR_FILE_TOO_BIG);return;}Send(msgStream,EmptyCString(),msgLength,true,aRv);}voidWebSocket::Send(constArrayBuffer&aData,ErrorResult&aRv){AssertIsOnTargetThread();aData.ComputeLengthAndData();static_assert(sizeof(*aData.Data())==1,"byte-sized data required");uint32_tlen=aData.Length();char*data=reinterpret_cast<char*>(aData.Data());nsDependentCSubstringmsgString(data,len);Send(nullptr,msgString,len,true,aRv);}voidWebSocket::Send(constArrayBufferView&aData,ErrorResult&aRv){AssertIsOnTargetThread();aData.ComputeLengthAndData();static_assert(sizeof(*aData.Data())==1,"byte-sized data required");uint32_tlen=aData.Length();char*data=reinterpret_cast<char*>(aData.Data());nsDependentCSubstringmsgString(data,len);Send(nullptr,msgString,len,true,aRv);}voidWebSocket::Send(nsIInputStream*aMsgStream,constnsACString&aMsgString,uint32_taMsgLength,boolaIsBinary,ErrorResult&aRv){AssertIsOnTargetThread();int64_treadyState=ReadyState();if(readyState==CONNECTING){aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);return;}// Always increment outgoing buffer len, even if closedmOutgoingBufferedAmount+=aMsgLength;if(readyState==CLOSING||readyState==CLOSED){return;}// We must have mImpl when connected.MOZ_ASSERT(mImpl);MOZ_ASSERT(readyState==OPEN,"Unknown state in WebSocket::Send");nsresultrv;if(aMsgStream){rv=mImpl->mChannel->SendBinaryStream(aMsgStream,aMsgLength);}else{if(aIsBinary){rv=mImpl->mChannel->SendBinaryMsg(aMsgString);}else{rv=mImpl->mChannel->SendMsg(aMsgString);}}if(NS_FAILED(rv)){aRv.Throw(rv);return;}UpdateMustKeepAlive();}// webIDL: void close(optional unsigned short code, optional DOMString reason):voidWebSocket::Close(constOptional<uint16_t>&aCode,constOptional<nsAString>&aReason,ErrorResult&aRv){AssertIsOnTargetThread();// the reason code is optional, but if provided it must be in a specific rangeuint16_tcloseCode=0;if(aCode.WasPassed()){if(aCode.Value()!=1000&&(aCode.Value()<3000||aCode.Value()>4999)){aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);return;}closeCode=aCode.Value();}nsCStringcloseReason;if(aReason.WasPassed()){CopyUTF16toUTF8(aReason.Value(),closeReason);// The API requires the UTF-8 string to be 123 or less bytesif(closeReason.Length()>123){aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);return;}}int64_treadyState=ReadyState();if(readyState==CLOSING||readyState==CLOSED){return;}// If the webSocket is not closed we MUST have a mImpl.MOZ_ASSERT(mImpl);if(readyState==CONNECTING){mImpl->FailConnection(closeCode,closeReason);return;}MOZ_ASSERT(readyState==OPEN);mImpl->CloseConnection(closeCode,closeReason);}//-----------------------------------------------------------------------------// WebSocketImpl::nsIObserver//-----------------------------------------------------------------------------NS_IMETHODIMPWebSocketImpl::Observe(nsISupports*aSubject,constchar*aTopic,constchar16_t*aData){AssertIsOnMainThread();int64_treadyState=mWebSocket->ReadyState();if((readyState==WebSocket::CLOSING)||(readyState==WebSocket::CLOSED)){returnNS_OK;}nsCOMPtr<nsPIDOMWindowInner>window=do_QueryInterface(aSubject);if(!mWebSocket->GetOwner()||window!=mWebSocket->GetOwner()){returnNS_OK;}if((strcmp(aTopic,DOM_WINDOW_FROZEN_TOPIC)==0)||(strcmp(aTopic,DOM_WINDOW_DESTROYED_TOPIC)==0)){CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY);}returnNS_OK;}//-----------------------------------------------------------------------------// WebSocketImpl::nsIRequest//-----------------------------------------------------------------------------NS_IMETHODIMPWebSocketImpl::GetName(nsACString&aName){AssertIsOnMainThread();CopyUTF16toUTF8(mWebSocket->mURI,aName);returnNS_OK;}NS_IMETHODIMPWebSocketImpl::IsPending(bool*aValue){AssertIsOnTargetThread();int64_treadyState=mWebSocket->ReadyState();*aValue=(readyState!=WebSocket::CLOSED);returnNS_OK;}NS_IMETHODIMPWebSocketImpl::GetStatus(nsresult*aStatus){AssertIsOnTargetThread();*aStatus=NS_OK;returnNS_OK;}namespace{classCancelRunnablefinal:publicWorkerRunnable{public:CancelRunnable(WorkerPrivate*aWorkerPrivate,WebSocketImpl*aImpl):WorkerRunnable(aWorkerPrivate,WorkerThreadUnchangedBusyCount),mImpl(aImpl){}boolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override{aWorkerPrivate->AssertIsOnWorkerThread();aWorkerPrivate->ModifyBusyCountFromWorker(true);return!NS_FAILED(mImpl->CancelInternal());}voidPostRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate,boolaRunResult)override{aWorkerPrivate->ModifyBusyCountFromWorker(false);}boolPreDispatch(WorkerPrivate*aWorkerPrivate)override{// We don't call WorkerRunnable::PreDispatch because it would assert the// wrong thing about which thread we're on.AssertIsOnMainThread();returntrue;}voidPostDispatch(WorkerPrivate*aWorkerPrivate,boolaDispatchResult)override{// We don't call WorkerRunnable::PostDispatch because it would assert the// wrong thing about which thread we're on.AssertIsOnMainThread();}private:RefPtr<WebSocketImpl>mImpl;};}// namespace// Window closed, stop/reload button pressed, user navigated away from page, etc.NS_IMETHODIMPWebSocketImpl::Cancel(nsresultaStatus){AssertIsOnMainThread();if(!mIsMainThread){MOZ_ASSERT(mWorkerPrivate);RefPtr<CancelRunnable>runnable=newCancelRunnable(mWorkerPrivate,this);if(!runnable->Dispatch()){returnNS_ERROR_FAILURE;}returnNS_OK;}returnCancelInternal();}nsresultWebSocketImpl::CancelInternal(){AssertIsOnTargetThread();// If CancelInternal is called by a runnable, we may already be disconnected// by the time it runs.if(mDisconnectingOrDisconnected){returnNS_OK;}int64_treadyState=mWebSocket->ReadyState();if(readyState==WebSocket::CLOSING||readyState==WebSocket::CLOSED){returnNS_OK;}ConsoleError();returnCloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY);}NS_IMETHODIMPWebSocketImpl::Suspend(){AssertIsOnMainThread();returnNS_ERROR_NOT_IMPLEMENTED;}NS_IMETHODIMPWebSocketImpl::Resume(){AssertIsOnMainThread();returnNS_ERROR_NOT_IMPLEMENTED;}NS_IMETHODIMPWebSocketImpl::GetLoadGroup(nsILoadGroup**aLoadGroup){AssertIsOnMainThread();*aLoadGroup=nullptr;if(mIsMainThread){nsresultrv;nsIScriptContext*sc=mWebSocket->GetContextForEventHandlers(&rv);nsCOMPtr<nsIDocument>doc=nsContentUtils::GetDocumentFromScriptContext(sc);if(doc){*aLoadGroup=doc->GetDocumentLoadGroup().take();}returnNS_OK;}MOZ_ASSERT(mWorkerPrivate);// Walk up to our containing pageWorkerPrivate*wp=mWorkerPrivate;while(wp->GetParent()){wp=wp->GetParent();}nsPIDOMWindowInner*window=wp->GetWindow();if(!window){returnNS_OK;}nsIDocument*doc=window->GetExtantDoc();if(doc){*aLoadGroup=doc->GetDocumentLoadGroup().take();}returnNS_OK;}NS_IMETHODIMPWebSocketImpl::SetLoadGroup(nsILoadGroup*aLoadGroup){AssertIsOnMainThread();returnNS_ERROR_UNEXPECTED;}NS_IMETHODIMPWebSocketImpl::GetLoadFlags(nsLoadFlags*aLoadFlags){AssertIsOnMainThread();*aLoadFlags=nsIRequest::LOAD_BACKGROUND;returnNS_OK;}NS_IMETHODIMPWebSocketImpl::SetLoadFlags(nsLoadFlagsaLoadFlags){AssertIsOnMainThread();// we won't change the load flags at all.returnNS_OK;}namespace{classWorkerRunnableDispatcherfinal:publicWorkerRunnable{RefPtr<WebSocketImpl>mWebSocketImpl;public:WorkerRunnableDispatcher(WebSocketImpl*aImpl,WorkerPrivate*aWorkerPrivate,already_AddRefed<nsIRunnable>&&aEvent):WorkerRunnable(aWorkerPrivate,WorkerThreadUnchangedBusyCount),mWebSocketImpl(aImpl),mEvent(aEvent){}boolWorkerRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate)override{aWorkerPrivate->AssertIsOnWorkerThread();aWorkerPrivate->ModifyBusyCountFromWorker(true);// No messages when disconnected.if(mWebSocketImpl->mDisconnectingOrDisconnected){NS_WARNING("Dispatching a WebSocket event after the disconnection!");returntrue;}return!NS_FAILED(mEvent->Run());}voidPostRun(JSContext*aCx,WorkerPrivate*aWorkerPrivate,boolaRunResult)override{aWorkerPrivate->ModifyBusyCountFromWorker(false);}boolPreDispatch(WorkerPrivate*aWorkerPrivate)override{// We don't call WorkerRunnable::PreDispatch because it would assert the// wrong thing about which thread we're on. We're on whichever thread the// channel implementation is running on (probably the main thread or socket// transport thread).returntrue;}voidPostDispatch(WorkerPrivate*aWorkerPrivate,boolaDispatchResult)override{// We don't call WorkerRunnable::PreDispatch because it would assert the// wrong thing about which thread we're on. We're on whichever thread the// channel implementation is running on (probably the main thread or socket// transport thread).}private:nsCOMPtr<nsIRunnable>mEvent;};}// namespaceNS_IMETHODIMPWebSocketImpl::DispatchFromScript(nsIRunnable*aEvent,uint32_taFlags){nsCOMPtr<nsIRunnable>event(aEvent);returnDispatch(event.forget(),aFlags);}NS_IMETHODIMPWebSocketImpl::Dispatch(already_AddRefed<nsIRunnable>&&aEvent,uint32_taFlags){nsCOMPtr<nsIRunnable>event_ref(aEvent);// If the target is the main-thread we can just dispatch the runnable.if(mIsMainThread){returnNS_DispatchToMainThread(event_ref.forget());}MutexAutoLocklock(mMutex);if(mWorkerShuttingDown){returnNS_OK;}MOZ_ASSERT(mWorkerPrivate);#ifdef DEBUGMOZ_ASSERT(HasFeatureRegistered());#endif// If the target is a worker, we have to use a custom WorkerRunnableDispatcher// runnable.RefPtr<WorkerRunnableDispatcher>event=newWorkerRunnableDispatcher(this,mWorkerPrivate,event_ref.forget());if(!event->Dispatch()){returnNS_ERROR_FAILURE;}returnNS_OK;}NS_IMETHODIMPWebSocketImpl::IsOnCurrentThread(bool*aResult){*aResult=IsTargetThread();returnNS_OK;}boolWebSocketImpl::IsTargetThread()const{returnNS_IsMainThread()==mIsMainThread;}voidWebSocket::AssertIsOnTargetThread()const{MOZ_ASSERT(NS_IsMainThread()==mIsMainThread);}}// namespace dom}// namespace mozilla