content/base/src/WebSocket.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

     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/. */
     7 #include "WebSocket.h"
     8 #include "mozilla/dom/WebSocketBinding.h"
    10 #include "jsapi.h"
    11 #include "jsfriendapi.h"
    12 #include "js/OldDebugAPI.h"
    13 #include "mozilla/DOMEventTargetHelper.h"
    14 #include "nsIScriptGlobalObject.h"
    15 #include "nsIDOMWindow.h"
    16 #include "nsIDocument.h"
    17 #include "nsXPCOM.h"
    18 #include "nsIXPConnect.h"
    19 #include "nsContentUtils.h"
    20 #include "nsCxPusher.h"
    21 #include "nsError.h"
    22 #include "nsIScriptObjectPrincipal.h"
    23 #include "nsIURL.h"
    24 #include "nsIUnicodeEncoder.h"
    25 #include "nsThreadUtils.h"
    26 #include "nsIDOMMessageEvent.h"
    27 #include "nsIPromptFactory.h"
    28 #include "nsIWindowWatcher.h"
    29 #include "nsIPrompt.h"
    30 #include "nsIStringBundle.h"
    31 #include "nsIConsoleService.h"
    32 #include "nsIDOMCloseEvent.h"
    33 #include "nsICryptoHash.h"
    34 #include "nsJSUtils.h"
    35 #include "nsIScriptError.h"
    36 #include "nsNetUtil.h"
    37 #include "nsILoadGroup.h"
    38 #include "mozilla/Preferences.h"
    39 #include "xpcpublic.h"
    40 #include "nsContentPolicyUtils.h"
    41 #include "nsDOMFile.h"
    42 #include "nsWrapperCacheInlines.h"
    43 #include "nsIObserverService.h"
    44 #include "nsIWebSocketChannel.h"
    45 #include "GeneratedEvents.h"
    47 namespace mozilla {
    48 namespace dom {
    50 #define UTF_8_REPLACEMENT_CHAR    static_cast<char16_t>(0xFFFD)
    52 class CallDispatchConnectionCloseEvents: public nsRunnable
    53 {
    54 public:
    55 CallDispatchConnectionCloseEvents(WebSocket* aWebSocket)
    56   : mWebSocket(aWebSocket)
    57   {}
    59   NS_IMETHOD Run()
    60   {
    61     mWebSocket->DispatchConnectionCloseEvents();
    62     return NS_OK;
    63   }
    65 private:
    66   nsRefPtr<WebSocket> mWebSocket;
    67 };
    69 //-----------------------------------------------------------------------------
    70 // WebSocket
    71 //-----------------------------------------------------------------------------
    73 nsresult
    74 WebSocket::PrintErrorOnConsole(const char *aBundleURI,
    75                                const char16_t *aError,
    76                                const char16_t **aFormatStrings,
    77                                uint32_t aFormatStringsLen)
    78 {
    79   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
    81   nsresult rv;
    82   nsCOMPtr<nsIStringBundleService> bundleService =
    83     do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
    84   NS_ENSURE_SUCCESS(rv, rv);
    86   nsCOMPtr<nsIStringBundle> strBundle;
    87   rv = bundleService->CreateBundle(aBundleURI, getter_AddRefs(strBundle));
    88   NS_ENSURE_SUCCESS(rv, rv);
    90   nsCOMPtr<nsIConsoleService> console(
    91     do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv));
    92   NS_ENSURE_SUCCESS(rv, rv);
    94   nsCOMPtr<nsIScriptError> errorObject(
    95     do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv));
    96   NS_ENSURE_SUCCESS(rv, rv);
    98   // Localize the error message
    99   nsXPIDLString message;
   100   if (aFormatStrings) {
   101     rv = strBundle->FormatStringFromName(aError, aFormatStrings,
   102                                          aFormatStringsLen,
   103                                          getter_Copies(message));
   104   } else {
   105     rv = strBundle->GetStringFromName(aError, getter_Copies(message));
   106   }
   107   NS_ENSURE_SUCCESS(rv, rv);
   109   rv = errorObject->InitWithWindowID(message,
   110                                      NS_ConvertUTF8toUTF16(mScriptFile),
   111                                      EmptyString(), mScriptLine, 0,
   112                                      nsIScriptError::errorFlag, "Web Socket",
   113                                      mInnerWindowID);
   114   NS_ENSURE_SUCCESS(rv, rv);
   116   // print the error message directly to the JS console
   117   rv = console->LogMessage(errorObject);
   118   NS_ENSURE_SUCCESS(rv, rv);
   120   return NS_OK;
   121 }
   123 nsresult
   124 WebSocket::CloseConnection(uint16_t aReasonCode,
   125                              const nsACString& aReasonString)
   126 {
   127   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   128   if (mReadyState == WebSocket::CLOSING ||
   129       mReadyState == WebSocket::CLOSED) {
   130     return NS_OK;
   131   }
   133   // The common case...
   134   if (mChannel) {
   135     mReadyState = WebSocket::CLOSING;
   136     return mChannel->Close(aReasonCode, aReasonString);
   137   }
   139   // No channel, but not disconnected: canceled or failed early
   140   //
   141   MOZ_ASSERT(mReadyState == WebSocket::CONNECTING,
   142              "Should only get here for early websocket cancel/error");
   144   // Server won't be sending us a close code, so use what's passed in here.
   145   mCloseEventCode = aReasonCode;
   146   CopyUTF8toUTF16(aReasonString, mCloseEventReason);
   148   mReadyState = WebSocket::CLOSING;
   150   // Can be called from Cancel() or Init() codepaths, so need to dispatch
   151   // onerror/onclose asynchronously
   152   ScheduleConnectionCloseEvents(
   153                     nullptr,
   154                     (aReasonCode == nsIWebSocketChannel::CLOSE_NORMAL ||
   155                      aReasonCode == nsIWebSocketChannel::CLOSE_GOING_AWAY) ?
   156                      NS_OK : NS_ERROR_FAILURE,
   157                     false);
   159   return NS_OK;
   160 }
   162 nsresult
   163 WebSocket::ConsoleError()
   164 {
   165   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   167   nsAutoCString targetSpec;
   168   nsresult rv = mURI->GetSpec(targetSpec);
   169   if (NS_FAILED(rv)) {
   170     NS_WARNING("Failed to get targetSpec");
   171   } else {
   172     NS_ConvertUTF8toUTF16 specUTF16(targetSpec);
   173     const char16_t* formatStrings[] = { specUTF16.get() };
   175     if (mReadyState < WebSocket::OPEN) {
   176       PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
   177                           MOZ_UTF16("connectionFailure"),
   178                           formatStrings, ArrayLength(formatStrings));
   179     } else {
   180       PrintErrorOnConsole("chrome://global/locale/appstrings.properties",
   181                           MOZ_UTF16("netInterrupt"),
   182                           formatStrings, ArrayLength(formatStrings));
   183     }
   184   }
   185   /// todo some specific errors - like for message too large
   186   return rv;
   187 }
   190 void
   191 WebSocket::FailConnection(uint16_t aReasonCode,
   192                           const nsACString& aReasonString)
   193 {
   194   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   196   ConsoleError();
   197   mFailed = true;
   198   CloseConnection(aReasonCode, aReasonString);
   199 }
   201 nsresult
   202 WebSocket::Disconnect()
   203 {
   204   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   206   if (mDisconnected)
   207     return NS_OK;
   209   nsCOMPtr<nsILoadGroup> loadGroup;
   210   GetLoadGroup(getter_AddRefs(loadGroup));
   211   if (loadGroup)
   212     loadGroup->RemoveRequest(this, nullptr, NS_OK);
   214   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   215   if (os) {
   216     os->RemoveObserver(this, DOM_WINDOW_DESTROYED_TOPIC);
   217     os->RemoveObserver(this, DOM_WINDOW_FROZEN_TOPIC);
   218   }
   220   // DontKeepAliveAnyMore() can release the object. So hold a reference to this
   221   // until the end of the method.
   222   nsRefPtr<WebSocket> kungfuDeathGrip = this;
   224   DontKeepAliveAnyMore();
   225   mChannel = nullptr;
   226   mDisconnected = true;
   228   return NS_OK;
   229 }
   231 //-----------------------------------------------------------------------------
   232 // WebSocket::nsIWebSocketListener methods:
   233 //-----------------------------------------------------------------------------
   235 nsresult
   236 WebSocket::DoOnMessageAvailable(const nsACString& aMsg, bool isBinary)
   237 {
   238   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   240   if (mReadyState == WebSocket::CLOSED) {
   241     NS_ERROR("Received message after CLOSED");
   242     return NS_ERROR_UNEXPECTED;
   243   }
   245   if (mReadyState == WebSocket::OPEN) {
   246     // Dispatch New Message
   247     nsresult rv = CreateAndDispatchMessageEvent(aMsg, isBinary);
   248     if (NS_FAILED(rv)) {
   249       NS_WARNING("Failed to dispatch the message event");
   250     }
   251   } else {
   252     // CLOSING should be the only other state where it's possible to get msgs
   253     // from channel: Spec says to drop them.
   254     MOZ_ASSERT(mReadyState == WebSocket::CLOSING,
   255                "Received message while CONNECTING or CLOSED");
   256   }
   258   return NS_OK;
   259 }
   261 NS_IMETHODIMP
   262 WebSocket::OnMessageAvailable(nsISupports* aContext, const nsACString& aMsg)
   263 {
   264   return DoOnMessageAvailable(aMsg, false);
   265 }
   267 NS_IMETHODIMP
   268 WebSocket::OnBinaryMessageAvailable(nsISupports* aContext,
   269                                     const nsACString& aMsg)
   270 {
   271   return DoOnMessageAvailable(aMsg, true);
   272 }
   274 NS_IMETHODIMP
   275 WebSocket::OnStart(nsISupports* aContext)
   276 {
   277   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   279   // This is the only function that sets OPEN, and should be called only once
   280   MOZ_ASSERT(mReadyState != WebSocket::OPEN,
   281              "readyState already OPEN! OnStart called twice?");
   283   // Nothing to do if we've already closed/closing
   284   if (mReadyState != WebSocket::CONNECTING) {
   285     return NS_OK;
   286   }
   288   // Attempt to kill "ghost" websocket: but usually too early for check to fail
   289   nsresult rv = CheckInnerWindowCorrectness();
   290   if (NS_FAILED(rv)) {
   291     CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY);
   292     return rv;
   293   }
   295   if (!mRequestedProtocolList.IsEmpty()) {
   296     mChannel->GetProtocol(mEstablishedProtocol);
   297   }
   299   mChannel->GetExtensions(mEstablishedExtensions);
   300   UpdateURI();
   302   mReadyState = WebSocket::OPEN;
   304   // Call 'onopen'
   305   rv = CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("open"));
   306   if (NS_FAILED(rv)) {
   307     NS_WARNING("Failed to dispatch the open event");
   308   }
   310   UpdateMustKeepAlive();
   312   return NS_OK;
   313 }
   315 NS_IMETHODIMP
   316 WebSocket::OnStop(nsISupports* aContext, nsresult aStatusCode)
   317 {
   318   // We can be CONNECTING here if connection failed.
   319   // We can be OPEN if we have encountered a fatal protocol error
   320   // We can be CLOSING if close() was called and/or server initiated close.
   321   MOZ_ASSERT(mReadyState != WebSocket::CLOSED,
   322              "Shouldn't already be CLOSED when OnStop called");
   324   // called by network stack, not JS, so can dispatch JS events synchronously
   325   return ScheduleConnectionCloseEvents(aContext, aStatusCode, true);
   326 }
   328 nsresult
   329 WebSocket::ScheduleConnectionCloseEvents(nsISupports* aContext,
   330                                          nsresult aStatusCode,
   331                                          bool sync)
   332 {
   333   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   335   // no-op if some other code has already initiated close event
   336   if (!mOnCloseScheduled) {
   337     mCloseEventWasClean = NS_SUCCEEDED(aStatusCode);
   339     if (aStatusCode == NS_BASE_STREAM_CLOSED) {
   340       // don't generate an error event just because of an unclean close
   341       aStatusCode = NS_OK;
   342     }
   344     if (NS_FAILED(aStatusCode)) {
   345       ConsoleError();
   346       mFailed = true;
   347     }
   349     mOnCloseScheduled = true;
   351     if (sync) {
   352       DispatchConnectionCloseEvents();
   353     } else {
   354       NS_DispatchToMainThread(new CallDispatchConnectionCloseEvents(this),
   355                               NS_DISPATCH_NORMAL);
   356     }
   357   }
   359   return NS_OK;
   360 }
   362 NS_IMETHODIMP
   363 WebSocket::OnAcknowledge(nsISupports *aContext, uint32_t aSize)
   364 {
   365   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   367   if (aSize > mOutgoingBufferedAmount)
   368     return NS_ERROR_UNEXPECTED;
   370   mOutgoingBufferedAmount -= aSize;
   371   return NS_OK;
   372 }
   374 NS_IMETHODIMP
   375 WebSocket::OnServerClose(nsISupports *aContext, uint16_t aCode,
   376                            const nsACString &aReason)
   377 {
   378   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   380   MOZ_ASSERT(mReadyState != WebSocket::CONNECTING,
   381              "Received server close before connected?");
   382   MOZ_ASSERT(mReadyState != WebSocket::CLOSED,
   383              "Received server close after already closed!");
   385   // store code/string for onclose DOM event
   386   mCloseEventCode = aCode;
   387   CopyUTF8toUTF16(aReason, mCloseEventReason);
   389   if (mReadyState == WebSocket::OPEN) {
   390     // Server initiating close.
   391     // RFC 6455, 5.5.1: "When sending a Close frame in response, the endpoint
   392     // typically echos the status code it received".
   393     // But never send certain codes, per section 7.4.1
   394     if (aCode == 1005 || aCode == 1006 || aCode == 1015) {
   395       CloseConnection(0, EmptyCString());
   396     } else {
   397       CloseConnection(aCode, aReason);
   398     }
   399   } else {
   400     // We initiated close, and server has replied: OnStop does rest of the work.
   401     MOZ_ASSERT(mReadyState == WebSocket::CLOSING, "unknown state");
   402   }
   404   return NS_OK;
   405 }
   407 //-----------------------------------------------------------------------------
   408 // WebSocket::nsIInterfaceRequestor
   409 //-----------------------------------------------------------------------------
   411 NS_IMETHODIMP
   412 WebSocket::GetInterface(const nsIID& aIID, void** aResult)
   413 {
   414   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   416   if (mReadyState == WebSocket::CLOSED)
   417     return NS_ERROR_FAILURE;
   419   if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
   420       aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
   421     nsresult rv;
   422     nsIScriptContext* sc = GetContextForEventHandlers(&rv);
   423     nsCOMPtr<nsIDocument> doc =
   424       nsContentUtils::GetDocumentFromScriptContext(sc);
   425     if (!doc)
   426       return NS_ERROR_NOT_AVAILABLE;
   428     nsCOMPtr<nsIPromptFactory> wwatch =
   429       do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
   430     NS_ENSURE_SUCCESS(rv, rv);
   432     nsCOMPtr<nsPIDOMWindow> outerWindow = doc->GetWindow();
   433     return wwatch->GetPrompt(outerWindow, aIID, aResult);
   434   }
   436   return QueryInterface(aIID, aResult);
   437 }
   439 ////////////////////////////////////////////////////////////////////////////////
   440 // WebSocket
   441 ////////////////////////////////////////////////////////////////////////////////
   443 WebSocket::WebSocket(nsPIDOMWindow* aOwnerWindow)
   444 : DOMEventTargetHelper(aOwnerWindow),
   445   mKeepingAlive(false),
   446   mCheckMustKeepAlive(true),
   447   mOnCloseScheduled(false),
   448   mFailed(false),
   449   mDisconnected(false),
   450   mCloseEventWasClean(false),
   451   mCloseEventCode(nsIWebSocketChannel::CLOSE_ABNORMAL),
   452   mReadyState(WebSocket::CONNECTING),
   453   mOutgoingBufferedAmount(0),
   454   mBinaryType(dom::BinaryType::Blob),
   455   mScriptLine(0),
   456   mInnerWindowID(0)
   457 {
   458   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   459   MOZ_ASSERT(aOwnerWindow);
   460   MOZ_ASSERT(aOwnerWindow->IsInnerWindow());
   461 }
   463 WebSocket::~WebSocket()
   464 {
   465   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   467   // If we threw during Init we never called disconnect
   468   if (!mDisconnected) {
   469     Disconnect();
   470   }
   471 }
   473 JSObject*
   474 WebSocket::WrapObject(JSContext* cx)
   475 {
   476   return WebSocketBinding::Wrap(cx, this);
   477 }
   479 //---------------------------------------------------------------------------
   480 // WebIDL
   481 //---------------------------------------------------------------------------
   483 // Constructor:
   484 already_AddRefed<WebSocket>
   485 WebSocket::Constructor(const GlobalObject& aGlobal,
   486                        const nsAString& aUrl,
   487                        ErrorResult& aRv)
   488 {
   489   Sequence<nsString> protocols;
   490   return WebSocket::Constructor(aGlobal, aUrl, protocols, aRv);
   491 }
   493 already_AddRefed<WebSocket>
   494 WebSocket::Constructor(const GlobalObject& aGlobal,
   495                        const nsAString& aUrl,
   496                        const nsAString& aProtocol,
   497                        ErrorResult& aRv)
   498 {
   499   Sequence<nsString> protocols;
   500   protocols.AppendElement(aProtocol);
   501   return WebSocket::Constructor(aGlobal, aUrl, protocols, aRv);
   502 }
   504 already_AddRefed<WebSocket>
   505 WebSocket::Constructor(const GlobalObject& aGlobal,
   506                        const nsAString& aUrl,
   507                        const Sequence<nsString>& aProtocols,
   508                        ErrorResult& aRv)
   509 {
   510   if (!PrefEnabled()) {
   511     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
   512     return nullptr;
   513   }
   515   nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal =
   516     do_QueryInterface(aGlobal.GetAsSupports());
   517   if (!scriptPrincipal) {
   518     aRv.Throw(NS_ERROR_FAILURE);
   519     return nullptr;
   520   }
   522   nsCOMPtr<nsIPrincipal> principal = scriptPrincipal->GetPrincipal();
   523   if (!principal) {
   524     aRv.Throw(NS_ERROR_FAILURE);
   525     return nullptr;
   526   }
   528   nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aGlobal.GetAsSupports());
   529   if (!sgo) {
   530     aRv.Throw(NS_ERROR_FAILURE);
   531     return nullptr;
   532   }
   534   nsCOMPtr<nsPIDOMWindow> ownerWindow = do_QueryInterface(aGlobal.GetAsSupports());
   535   if (!ownerWindow) {
   536     aRv.Throw(NS_ERROR_FAILURE);
   537     return nullptr;
   538   }
   540   nsTArray<nsString> protocolArray;
   542   for (uint32_t index = 0, len = aProtocols.Length(); index < len; ++index) {
   544     const nsString& protocolElement = aProtocols[index];
   546     if (protocolElement.IsEmpty()) {
   547       aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
   548       return nullptr;
   549     }
   550     if (protocolArray.Contains(protocolElement)) {
   551       aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
   552       return nullptr;
   553     }
   554     if (protocolElement.FindChar(',') != -1)  /* interferes w/list */ {
   555       aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
   556       return nullptr;
   557     }
   559     protocolArray.AppendElement(protocolElement);
   560   }
   562   nsRefPtr<WebSocket> webSocket = new WebSocket(ownerWindow);
   563   nsresult rv = webSocket->Init(aGlobal.GetContext(), principal,
   564                                 aUrl, protocolArray);
   565   if (NS_FAILED(rv)) {
   566     aRv.Throw(rv);
   567     return nullptr;
   568   }
   570   return webSocket.forget();
   571 }
   573 NS_IMPL_CYCLE_COLLECTION_CLASS(WebSocket)
   575 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(WebSocket)
   576   bool isBlack = tmp->IsBlack();
   577   if (isBlack|| tmp->mKeepingAlive) {
   578     if (tmp->mListenerManager) {
   579       tmp->mListenerManager->MarkForCC();
   580     }
   581     if (!isBlack && tmp->PreservingWrapper()) {
   582       // This marks the wrapper black.
   583       tmp->GetWrapper();
   584     }
   585     return true;
   586   }
   587 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
   589 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(WebSocket)
   590   return tmp->IsBlack();
   591 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
   593 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(WebSocket)
   594   return tmp->IsBlack();
   595 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
   597 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WebSocket,
   598                                                DOMEventTargetHelper)
   599 NS_IMPL_CYCLE_COLLECTION_TRACE_END
   601 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WebSocket,
   602                                                   DOMEventTargetHelper)
   603   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal)
   604   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mURI)
   605   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel)
   606 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   608 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WebSocket,
   609                                                 DOMEventTargetHelper)
   610   tmp->Disconnect();
   611   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrincipal)
   612   NS_IMPL_CYCLE_COLLECTION_UNLINK(mURI)
   613   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannel)
   614 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   616 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(WebSocket)
   617   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   618   NS_INTERFACE_MAP_ENTRY(nsIWebSocketListener)
   619   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   620   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   621   NS_INTERFACE_MAP_ENTRY(nsIRequest)
   622 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
   624 NS_IMPL_ADDREF_INHERITED(WebSocket, DOMEventTargetHelper)
   625 NS_IMPL_RELEASE_INHERITED(WebSocket, DOMEventTargetHelper)
   627 void
   628 WebSocket::DisconnectFromOwner()
   629 {
   630   DOMEventTargetHelper::DisconnectFromOwner();
   631   CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY);
   632   DontKeepAliveAnyMore();
   633 }
   635 //-----------------------------------------------------------------------------
   636 // WebSocket:: initialization
   637 //-----------------------------------------------------------------------------
   639 nsresult
   640 WebSocket::Init(JSContext* aCx,
   641                 nsIPrincipal* aPrincipal,
   642                 const nsAString& aURL,
   643                 nsTArray<nsString>& aProtocolArray)
   644 {
   645   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   646   MOZ_ASSERT(aPrincipal);
   648   if (!PrefEnabled()) {
   649     return NS_ERROR_DOM_SECURITY_ERR;
   650   }
   652   mPrincipal = aPrincipal;
   654   // Attempt to kill "ghost" websocket: but usually too early for check to fail
   655   nsresult rv = CheckInnerWindowCorrectness();
   656   NS_ENSURE_SUCCESS(rv, rv);
   658   // Shut down websocket if window is frozen or destroyed (only needed for
   659   // "ghost" websockets--see bug 696085)
   660   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   661   NS_ENSURE_STATE(os);
   662   rv = os->AddObserver(this, DOM_WINDOW_DESTROYED_TOPIC, true);
   663   NS_ENSURE_SUCCESS(rv, rv);
   664   rv = os->AddObserver(this, DOM_WINDOW_FROZEN_TOPIC, true);
   665   NS_ENSURE_SUCCESS(rv, rv);
   667   unsigned lineno;
   668   JS::AutoFilename file;
   669   if (JS::DescribeScriptedCaller(aCx, &file, &lineno)) {
   670     mScriptFile = file.get();
   671     mScriptLine = lineno;
   672   }
   674   // Get WindowID
   675   mInnerWindowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(aCx);
   677   // parses the url
   678   rv = ParseURL(PromiseFlatString(aURL));
   679   NS_ENSURE_SUCCESS(rv, rv);
   681   nsIScriptContext* sc = GetContextForEventHandlers(&rv);
   682   NS_ENSURE_SUCCESS(rv, rv);
   684   nsCOMPtr<nsIDocument> originDoc = nsContentUtils::GetDocumentFromScriptContext(sc);
   686   // Don't allow https:// to open ws://
   687   if (!mSecure &&
   688       !Preferences::GetBool("network.websocket.allowInsecureFromHTTPS",
   689                             false)) {
   690     // Confirmed we are opening plain ws:// and want to prevent this from a
   691     // secure context (e.g. https). Check the security context of the document
   692     // associated with this script, which is the same as associated with mOwner.
   693     if (originDoc && originDoc->GetSecurityInfo()) {
   694       return NS_ERROR_DOM_SECURITY_ERR;
   695     }
   696   }
   698   // Assign the sub protocol list and scan it for illegal values
   699   for (uint32_t index = 0; index < aProtocolArray.Length(); ++index) {
   700     for (uint32_t i = 0; i < aProtocolArray[index].Length(); ++i) {
   701       if (aProtocolArray[index][i] < static_cast<char16_t>(0x0021) ||
   702           aProtocolArray[index][i] > static_cast<char16_t>(0x007E))
   703         return NS_ERROR_DOM_SYNTAX_ERR;
   704     }
   706     if (!mRequestedProtocolList.IsEmpty()) {
   707       mRequestedProtocolList.Append(NS_LITERAL_CSTRING(", "));
   708     }
   710     AppendUTF16toUTF8(aProtocolArray[index], mRequestedProtocolList);
   711   }
   713   // Check content policy.
   714   int16_t shouldLoad = nsIContentPolicy::ACCEPT;
   715   rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_WEBSOCKET,
   716                                  mURI,
   717                                  mPrincipal,
   718                                  originDoc,
   719                                  EmptyCString(),
   720                                  nullptr,
   721                                  &shouldLoad,
   722                                  nsContentUtils::GetContentPolicy(),
   723                                  nsContentUtils::GetSecurityManager());
   724   NS_ENSURE_SUCCESS(rv, rv);
   725   if (NS_CP_REJECTED(shouldLoad)) {
   726     // Disallowed by content policy.
   727     return NS_ERROR_CONTENT_BLOCKED;
   728   }
   730   // the constructor should throw a SYNTAX_ERROR only if it fails to parse the
   731   // url parameter, so don't throw if EstablishConnection fails, and call
   732   // onerror/onclose asynchronously
   733   if (NS_FAILED(EstablishConnection())) {
   734     FailConnection(nsIWebSocketChannel::CLOSE_ABNORMAL);
   735   }
   737   return NS_OK;
   738 }
   740 //-----------------------------------------------------------------------------
   741 // WebSocket methods:
   742 //-----------------------------------------------------------------------------
   744 class nsAutoCloseWS
   745 {
   746 public:
   747   nsAutoCloseWS(WebSocket* aWebSocket)
   748     : mWebSocket(aWebSocket)
   749   {}
   751   ~nsAutoCloseWS()
   752   {
   753     if (!mWebSocket->mChannel) {
   754       mWebSocket->CloseConnection(nsIWebSocketChannel::CLOSE_INTERNAL_ERROR);
   755     }
   756   }
   757 private:
   758   nsRefPtr<WebSocket> mWebSocket;
   759 };
   761 nsresult
   762 WebSocket::EstablishConnection()
   763 {
   764   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   765   NS_ABORT_IF_FALSE(!mChannel, "mChannel should be null");
   767   nsCOMPtr<nsIWebSocketChannel> wsChannel;
   768   nsAutoCloseWS autoClose(this);
   769   nsresult rv;
   771   if (mSecure) {
   772     wsChannel =
   773       do_CreateInstance("@mozilla.org/network/protocol;1?name=wss", &rv);
   774   } else {
   775     wsChannel =
   776       do_CreateInstance("@mozilla.org/network/protocol;1?name=ws", &rv);
   777   }
   778   NS_ENSURE_SUCCESS(rv, rv);
   780   rv = wsChannel->SetNotificationCallbacks(this);
   781   NS_ENSURE_SUCCESS(rv, rv);
   783   // add ourselves to the document's load group and
   784   // provide the http stack the loadgroup info too
   785   nsCOMPtr<nsILoadGroup> loadGroup;
   786   rv = GetLoadGroup(getter_AddRefs(loadGroup));
   787   if (loadGroup) {
   788     rv = wsChannel->SetLoadGroup(loadGroup);
   789     NS_ENSURE_SUCCESS(rv, rv);
   790     rv = loadGroup->AddRequest(this, nullptr);
   791     NS_ENSURE_SUCCESS(rv, rv);
   792   }
   794   if (!mRequestedProtocolList.IsEmpty()) {
   795     rv = wsChannel->SetProtocol(mRequestedProtocolList);
   796     NS_ENSURE_SUCCESS(rv, rv);
   797   }
   799   nsCString asciiOrigin;
   800   rv = nsContentUtils::GetASCIIOrigin(mPrincipal, asciiOrigin);
   801   NS_ENSURE_SUCCESS(rv, rv);
   803   ToLowerCase(asciiOrigin);
   805   rv = wsChannel->AsyncOpen(mURI, asciiOrigin, this, nullptr);
   806   NS_ENSURE_SUCCESS(rv, rv);
   808   mChannel = wsChannel;
   810   return NS_OK;
   811 }
   813 void
   814 WebSocket::DispatchConnectionCloseEvents()
   815 {
   816   mReadyState = WebSocket::CLOSED;
   818   // Call 'onerror' if needed
   819   if (mFailed) {
   820     nsresult rv = CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error"));
   821     if (NS_FAILED(rv)) {
   822       NS_WARNING("Failed to dispatch the error event");
   823     }
   824   }
   826   nsresult rv = CreateAndDispatchCloseEvent(mCloseEventWasClean, mCloseEventCode,
   827                                             mCloseEventReason);
   828   if (NS_FAILED(rv)) {
   829     NS_WARNING("Failed to dispatch the close event");
   830   }
   832   UpdateMustKeepAlive();
   833   Disconnect();
   834 }
   836 nsresult
   837 WebSocket::CreateAndDispatchSimpleEvent(const nsString& aName)
   838 {
   839   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   841   nsresult rv = CheckInnerWindowCorrectness();
   842   if (NS_FAILED(rv)) {
   843     return NS_OK;
   844   }
   846   nsCOMPtr<nsIDOMEvent> event;
   847   rv = NS_NewDOMEvent(getter_AddRefs(event), this, nullptr, nullptr);
   848   NS_ENSURE_SUCCESS(rv, rv);
   850   // it doesn't bubble, and it isn't cancelable
   851   rv = event->InitEvent(aName, false, false);
   852   NS_ENSURE_SUCCESS(rv, rv);
   854   event->SetTrusted(true);
   856   return DispatchDOMEvent(nullptr, event, nullptr, nullptr);
   857 }
   859 nsresult
   860 WebSocket::CreateAndDispatchMessageEvent(const nsACString& aData,
   861                                          bool isBinary)
   862 {
   863   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   865   nsresult rv = CheckInnerWindowCorrectness();
   866   if (NS_FAILED(rv))
   867     return NS_OK;
   869   // Get the JSContext
   870   nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(GetOwner());
   871   NS_ENSURE_TRUE(sgo, NS_ERROR_FAILURE);
   873   nsIScriptContext* scriptContext = sgo->GetContext();
   874   NS_ENSURE_TRUE(scriptContext, NS_ERROR_FAILURE);
   876   AutoPushJSContext cx(scriptContext->GetNativeContext());
   877   NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
   879   // Create appropriate JS object for message
   880   JS::Rooted<JS::Value> jsData(cx);
   881   if (isBinary) {
   882     if (mBinaryType == dom::BinaryType::Blob) {
   883       rv = nsContentUtils::CreateBlobBuffer(cx, aData, &jsData);
   884       NS_ENSURE_SUCCESS(rv, rv);
   885     } else if (mBinaryType == dom::BinaryType::Arraybuffer) {
   886       JS::Rooted<JSObject*> arrayBuf(cx);
   887       rv = nsContentUtils::CreateArrayBuffer(cx, aData, arrayBuf.address());
   888       NS_ENSURE_SUCCESS(rv, rv);
   889       jsData = OBJECT_TO_JSVAL(arrayBuf);
   890     } else {
   891       NS_RUNTIMEABORT("Unknown binary type!");
   892       return NS_ERROR_UNEXPECTED;
   893     }
   894   } else {
   895     // JS string
   896     NS_ConvertUTF8toUTF16 utf16Data(aData);
   897     JSString* jsString;
   898     jsString = JS_NewUCStringCopyN(cx, utf16Data.get(), utf16Data.Length());
   899     NS_ENSURE_TRUE(jsString, NS_ERROR_FAILURE);
   901     jsData = STRING_TO_JSVAL(jsString);
   902   }
   904   // create an event that uses the MessageEvent interface,
   905   // which does not bubble, is not cancelable, and has no default action
   907   nsCOMPtr<nsIDOMEvent> event;
   908   rv = NS_NewDOMMessageEvent(getter_AddRefs(event), this, nullptr, nullptr);
   909   NS_ENSURE_SUCCESS(rv, rv);
   911   nsCOMPtr<nsIDOMMessageEvent> messageEvent = do_QueryInterface(event);
   912   rv = messageEvent->InitMessageEvent(NS_LITERAL_STRING("message"),
   913                                       false, false,
   914                                       jsData,
   915                                       mUTF16Origin,
   916                                       EmptyString(), nullptr);
   917   NS_ENSURE_SUCCESS(rv, rv);
   919   event->SetTrusted(true);
   921   return DispatchDOMEvent(nullptr, event, nullptr, nullptr);
   922 }
   924 nsresult
   925 WebSocket::CreateAndDispatchCloseEvent(bool aWasClean,
   926                                        uint16_t aCode,
   927                                        const nsString &aReason)
   928 {
   929   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
   931   nsresult rv = CheckInnerWindowCorrectness();
   932   if (NS_FAILED(rv)) {
   933     return NS_OK;
   934   }
   936   // create an event that uses the CloseEvent interface,
   937   // which does not bubble, is not cancelable, and has no default action
   939   nsCOMPtr<nsIDOMEvent> event;
   940   rv = NS_NewDOMCloseEvent(getter_AddRefs(event), this, nullptr, nullptr);
   941   NS_ENSURE_SUCCESS(rv, rv);
   943   nsCOMPtr<nsIDOMCloseEvent> closeEvent = do_QueryInterface(event);
   944   rv = closeEvent->InitCloseEvent(NS_LITERAL_STRING("close"),
   945                                   false, false,
   946                                   aWasClean, aCode, aReason);
   947   NS_ENSURE_SUCCESS(rv, rv);
   949   event->SetTrusted(true);
   951   return DispatchDOMEvent(nullptr, event, nullptr, nullptr);
   952 }
   954 bool
   955 WebSocket::PrefEnabled(JSContext* aCx, JSObject* aGlobal)
   956 {
   957   return Preferences::GetBool("network.websocket.enabled", true);
   958 }
   960 nsresult
   961 WebSocket::ParseURL(const nsString& aURL)
   962 {
   963   NS_ENSURE_TRUE(!aURL.IsEmpty(), NS_ERROR_DOM_SYNTAX_ERR);
   965   nsCOMPtr<nsIURI> uri;
   966   nsresult rv = NS_NewURI(getter_AddRefs(uri), aURL);
   967   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
   969   nsCOMPtr<nsIURL> parsedURL = do_QueryInterface(uri, &rv);
   970   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
   972   nsAutoCString fragment;
   973   rv = parsedURL->GetRef(fragment);
   974   NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && fragment.IsEmpty(),
   975                  NS_ERROR_DOM_SYNTAX_ERR);
   977   nsAutoCString scheme;
   978   rv = parsedURL->GetScheme(scheme);
   979   NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && !scheme.IsEmpty(),
   980                  NS_ERROR_DOM_SYNTAX_ERR);
   982   nsAutoCString host;
   983   rv = parsedURL->GetAsciiHost(host);
   984   NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && !host.IsEmpty(), NS_ERROR_DOM_SYNTAX_ERR);
   986   int32_t port;
   987   rv = parsedURL->GetPort(&port);
   988   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
   990   rv = NS_CheckPortSafety(port, scheme.get());
   991   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
   993   nsAutoCString filePath;
   994   rv = parsedURL->GetFilePath(filePath);
   995   if (filePath.IsEmpty()) {
   996     filePath.AssignLiteral("/");
   997   }
   998   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
  1000   nsAutoCString query;
  1001   rv = parsedURL->GetQuery(query);
  1002   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
  1004   if (scheme.LowerCaseEqualsLiteral("ws")) {
  1005      mSecure = false;
  1006      mPort = (port == -1) ? DEFAULT_WS_SCHEME_PORT : port;
  1007   } else if (scheme.LowerCaseEqualsLiteral("wss")) {
  1008     mSecure = true;
  1009     mPort = (port == -1) ? DEFAULT_WSS_SCHEME_PORT : port;
  1010   } else {
  1011     return NS_ERROR_DOM_SYNTAX_ERR;
  1014   rv = nsContentUtils::GetUTFOrigin(parsedURL, mUTF16Origin);
  1015   NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_SYNTAX_ERR);
  1017   mAsciiHost = host;
  1018   ToLowerCase(mAsciiHost);
  1020   mResource = filePath;
  1021   if (!query.IsEmpty()) {
  1022     mResource.AppendLiteral("?");
  1023     mResource.Append(query);
  1025   uint32_t length = mResource.Length();
  1026   uint32_t i;
  1027   for (i = 0; i < length; ++i) {
  1028     if (mResource[i] < static_cast<char16_t>(0x0021) ||
  1029         mResource[i] > static_cast<char16_t>(0x007E)) {
  1030       return NS_ERROR_DOM_SYNTAX_ERR;
  1034   mOriginalURL = aURL;
  1035   mURI = parsedURL;
  1036   return NS_OK;
  1039 //-----------------------------------------------------------------------------
  1040 // Methods that keep alive the WebSocket object when:
  1041 //   1. the object has registered event listeners that can be triggered
  1042 //      ("strong event listeners");
  1043 //   2. there are outgoing not sent messages.
  1044 //-----------------------------------------------------------------------------
  1046 void
  1047 WebSocket::UpdateMustKeepAlive()
  1049   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
  1050   if (!mCheckMustKeepAlive) {
  1051     return;
  1054   bool shouldKeepAlive = false;
  1056   if (mListenerManager) {
  1057     switch (mReadyState)
  1059       case WebSocket::CONNECTING:
  1061         if (mListenerManager->HasListenersFor(nsGkAtoms::onopen) ||
  1062             mListenerManager->HasListenersFor(nsGkAtoms::onmessage) ||
  1063             mListenerManager->HasListenersFor(nsGkAtoms::onerror) ||
  1064             mListenerManager->HasListenersFor(nsGkAtoms::onclose)) {
  1065           shouldKeepAlive = true;
  1068       break;
  1070       case WebSocket::OPEN:
  1071       case WebSocket::CLOSING:
  1073         if (mListenerManager->HasListenersFor(nsGkAtoms::onmessage) ||
  1074             mListenerManager->HasListenersFor(nsGkAtoms::onerror) ||
  1075             mListenerManager->HasListenersFor(nsGkAtoms::onclose) ||
  1076             mOutgoingBufferedAmount != 0) {
  1077           shouldKeepAlive = true;
  1080       break;
  1082       case WebSocket::CLOSED:
  1084         shouldKeepAlive = false;
  1089   if (mKeepingAlive && !shouldKeepAlive) {
  1090     mKeepingAlive = false;
  1091     static_cast<EventTarget*>(this)->Release();
  1092   } else if (!mKeepingAlive && shouldKeepAlive) {
  1093     mKeepingAlive = true;
  1094     static_cast<EventTarget*>(this)->AddRef();
  1098 void
  1099 WebSocket::DontKeepAliveAnyMore()
  1101   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
  1102   if (mKeepingAlive) {
  1103     mKeepingAlive = false;
  1104     static_cast<EventTarget*>(this)->Release();
  1106   mCheckMustKeepAlive = false;
  1109 nsresult
  1110 WebSocket::UpdateURI()
  1112   // Check for Redirections
  1113   nsCOMPtr<nsIURI> uri;
  1114   nsresult rv = mChannel->GetURI(getter_AddRefs(uri));
  1115   NS_ENSURE_SUCCESS(rv, rv);
  1117   nsAutoCString spec;
  1118   rv = uri->GetSpec(spec);
  1119   NS_ENSURE_SUCCESS(rv, rv);
  1120   CopyUTF8toUTF16(spec, mEffectiveURL);
  1122   bool isWSS = false;
  1123   rv = uri->SchemeIs("wss", &isWSS);
  1124   NS_ENSURE_SUCCESS(rv, rv);
  1125   mSecure = isWSS ? true : false;
  1127   return NS_OK;
  1130 void
  1131 WebSocket::EventListenerAdded(nsIAtom* aType)
  1133   UpdateMustKeepAlive();
  1136 void
  1137 WebSocket::EventListenerRemoved(nsIAtom* aType)
  1139   UpdateMustKeepAlive();
  1142 //-----------------------------------------------------------------------------
  1143 // WebSocket - methods
  1144 //-----------------------------------------------------------------------------
  1146 // webIDL: readonly attribute DOMString url
  1147 void
  1148 WebSocket::GetUrl(nsAString& aURL)
  1150   if (mEffectiveURL.IsEmpty()) {
  1151     aURL = mOriginalURL;
  1152   } else {
  1153     aURL = mEffectiveURL;
  1157 // webIDL: readonly attribute DOMString extensions;
  1158 void
  1159 WebSocket::GetExtensions(nsAString& aExtensions)
  1161   CopyUTF8toUTF16(mEstablishedExtensions, aExtensions);
  1164 // webIDL: readonly attribute DOMString protocol;
  1165 void
  1166 WebSocket::GetProtocol(nsAString& aProtocol)
  1168   CopyUTF8toUTF16(mEstablishedProtocol, aProtocol);
  1171 // webIDL: void send(DOMString data);
  1172 void
  1173 WebSocket::Send(const nsAString& aData,
  1174                 ErrorResult& aRv)
  1176   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
  1178   NS_ConvertUTF16toUTF8 msgString(aData);
  1179   Send(nullptr, msgString, msgString.Length(), false, aRv);
  1182 void
  1183 WebSocket::Send(nsIDOMBlob* aData,
  1184                 ErrorResult& aRv)
  1186   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
  1188   nsCOMPtr<nsIInputStream> msgStream;
  1189   nsresult rv = aData->GetInternalStream(getter_AddRefs(msgStream));
  1190   if (NS_FAILED(rv)) {
  1191     aRv.Throw(rv);
  1192     return;
  1195   uint64_t msgLength;
  1196   rv = aData->GetSize(&msgLength);
  1197   if (NS_FAILED(rv)) {
  1198     aRv.Throw(rv);
  1199     return;
  1202   if (msgLength > UINT32_MAX) {
  1203     aRv.Throw(NS_ERROR_FILE_TOO_BIG);
  1204     return;
  1207   Send(msgStream, EmptyCString(), msgLength, true, aRv);
  1210 void
  1211 WebSocket::Send(const ArrayBuffer& aData,
  1212                 ErrorResult& aRv)
  1214   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
  1216   aData.ComputeLengthAndData();
  1218   static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
  1220   uint32_t len = aData.Length();
  1221   char* data = reinterpret_cast<char*>(aData.Data());
  1223   nsDependentCSubstring msgString(data, len);
  1224   Send(nullptr, msgString, len, true, aRv);
  1227 void
  1228 WebSocket::Send(const ArrayBufferView& aData,
  1229                 ErrorResult& aRv)
  1231   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
  1233   aData.ComputeLengthAndData();
  1235   static_assert(sizeof(*aData.Data()) == 1, "byte-sized data required");
  1237   uint32_t len = aData.Length();
  1238   char* data = reinterpret_cast<char*>(aData.Data());
  1240   nsDependentCSubstring msgString(data, len);
  1241   Send(nullptr, msgString, len, true, aRv);
  1244 void
  1245 WebSocket::Send(nsIInputStream* aMsgStream,
  1246                 const nsACString& aMsgString,
  1247                 uint32_t aMsgLength,
  1248                 bool aIsBinary,
  1249                 ErrorResult& aRv)
  1251   if (mReadyState == WebSocket::CONNECTING) {
  1252     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
  1253     return;
  1256   // Always increment outgoing buffer len, even if closed
  1257   mOutgoingBufferedAmount += aMsgLength;
  1259   if (mReadyState == WebSocket::CLOSING ||
  1260       mReadyState == WebSocket::CLOSED) {
  1261     return;
  1264   MOZ_ASSERT(mReadyState == WebSocket::OPEN,
  1265              "Unknown state in WebSocket::Send");
  1267   nsresult rv;
  1268   if (aMsgStream) {
  1269     rv = mChannel->SendBinaryStream(aMsgStream, aMsgLength);
  1270   } else {
  1271     if (aIsBinary) {
  1272       rv = mChannel->SendBinaryMsg(aMsgString);
  1273     } else {
  1274       rv = mChannel->SendMsg(aMsgString);
  1278   if (NS_FAILED(rv)) {
  1279     aRv.Throw(rv);
  1280     return;
  1283   UpdateMustKeepAlive();
  1286 // webIDL: void close(optional unsigned short code, optional DOMString reason):
  1287 void
  1288 WebSocket::Close(const Optional<uint16_t>& aCode,
  1289                  const Optional<nsAString>& aReason,
  1290                  ErrorResult& aRv)
  1292   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
  1294   // the reason code is optional, but if provided it must be in a specific range
  1295   uint16_t closeCode = 0;
  1296   if (aCode.WasPassed()) {
  1297     if (aCode.Value() != 1000 && (aCode.Value() < 3000 || aCode.Value() > 4999)) {
  1298       aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
  1299       return;
  1301     closeCode = aCode.Value();
  1304   nsCString closeReason;
  1305   if (aReason.WasPassed()) {
  1306     CopyUTF16toUTF8(aReason.Value(), closeReason);
  1308     // The API requires the UTF-8 string to be 123 or less bytes
  1309     if (closeReason.Length() > 123) {
  1310       aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
  1311       return;
  1315   if (mReadyState == WebSocket::CLOSING ||
  1316       mReadyState == WebSocket::CLOSED) {
  1317     return;
  1320   if (mReadyState == WebSocket::CONNECTING) {
  1321     FailConnection(closeCode, closeReason);
  1322     return;
  1325   MOZ_ASSERT(mReadyState == WebSocket::OPEN);
  1326   CloseConnection(closeCode, closeReason);
  1329 //-----------------------------------------------------------------------------
  1330 // WebSocket::nsIObserver
  1331 //-----------------------------------------------------------------------------
  1333 NS_IMETHODIMP
  1334 WebSocket::Observe(nsISupports* aSubject,
  1335                    const char* aTopic,
  1336                    const char16_t* aData)
  1338   if ((mReadyState == WebSocket::CLOSING) ||
  1339       (mReadyState == WebSocket::CLOSED)) {
  1340     return NS_OK;
  1343   nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aSubject);
  1344   if (!GetOwner() || window != GetOwner()) {
  1345     return NS_OK;
  1348   if ((strcmp(aTopic, DOM_WINDOW_FROZEN_TOPIC) == 0) ||
  1349       (strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC) == 0))
  1351     CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY);
  1354   return NS_OK;
  1357 //-----------------------------------------------------------------------------
  1358 // WebSocket::nsIRequest
  1359 //-----------------------------------------------------------------------------
  1361 NS_IMETHODIMP
  1362 WebSocket::GetName(nsACString& aName)
  1364   CopyUTF16toUTF8(mOriginalURL, aName);
  1365   return NS_OK;
  1368 NS_IMETHODIMP
  1369 WebSocket::IsPending(bool* aValue)
  1371   *aValue = (mReadyState != WebSocket::CLOSED);
  1372   return NS_OK;
  1375 NS_IMETHODIMP
  1376 WebSocket::GetStatus(nsresult* aStatus)
  1378   *aStatus = NS_OK;
  1379   return NS_OK;
  1382 // Window closed, stop/reload button pressed, user navigated away from page, etc.
  1383 NS_IMETHODIMP
  1384 WebSocket::Cancel(nsresult aStatus)
  1386   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread");
  1388   if (mReadyState == CLOSING || mReadyState == CLOSED) {
  1389     return NS_OK;
  1392   ConsoleError();
  1394   return CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY);
  1397 NS_IMETHODIMP
  1398 WebSocket::Suspend()
  1400   return NS_ERROR_NOT_IMPLEMENTED;
  1403 NS_IMETHODIMP
  1404 WebSocket::Resume()
  1406   return NS_ERROR_NOT_IMPLEMENTED;
  1409 NS_IMETHODIMP
  1410 WebSocket::GetLoadGroup(nsILoadGroup** aLoadGroup)
  1412   *aLoadGroup = nullptr;
  1414   nsresult rv;
  1415   nsIScriptContext* sc = GetContextForEventHandlers(&rv);
  1416   nsCOMPtr<nsIDocument> doc =
  1417     nsContentUtils::GetDocumentFromScriptContext(sc);
  1419   if (doc) {
  1420     *aLoadGroup = doc->GetDocumentLoadGroup().take();
  1423   return NS_OK;
  1426 NS_IMETHODIMP
  1427 WebSocket::SetLoadGroup(nsILoadGroup* aLoadGroup)
  1429   return NS_ERROR_UNEXPECTED;
  1432 NS_IMETHODIMP
  1433 WebSocket::GetLoadFlags(nsLoadFlags* aLoadFlags)
  1435   *aLoadFlags = nsIRequest::LOAD_BACKGROUND;
  1436   return NS_OK;
  1439 NS_IMETHODIMP
  1440 WebSocket::SetLoadFlags(nsLoadFlags aLoadFlags)
  1442   // we won't change the load flags at all.
  1443   return NS_OK;
  1446 } // dom namespace
  1447 } // mozilla namespace

mercurial